# Different Shelf > Different Shelf follows apparently sensible ideas through software, player experience and organisations until their real consequences appear. Public Ghost content for AI and LLM tooling. This file includes a bounded export of public pages first, then recent public posts. Append `.md` to any post or page URL to get the content in Markdown (for example, `/example-post.md`). ## Pages ### Ideas are tidier than their consequences URL: https://www.differentshelf.com/about/ Last updated: 2026-08-22T23:14:20.000Z Most bad systems began as reasonable decisions. A metric makes one problem legible and quietly changes what people optimise. A standard removes needless variation until it erases a difference that mattered. The platform that promised reuse acquires a queue. The management model that looked tidy on a slide becomes somebody's working week. Different Shelf examines the distance between the approved explanation and the lived effect. I write about software, player experience, organisations, AI, tools and the occasional cultural object that makes the argument easier to see. The subject changes. The question is stubborn: what happens when people have to live with this? ## Human judgement still matters. Evidence matters because it can correct the story we prefer. It can show where time went or what the system did under load. It cannot decide whether the result was worth the cost. Technology can extend judgement and a model can discipline it, but responsibility remains human. Different Shelf is not an argument against measurement, consistency or machines. It looks for the part their account leaves out, then follows that omission until its consequence becomes visible. ## Close to the material That perspective comes from working close to both code and consequence. I have spent more than two decades building software and leading product engineering in start-ups and global consumer businesses. The work has ranged from the behaviour of an interface to the architecture and organisations behind products used at scale. I also make pen-plotter art. Code may describe a perfect line; paper, ink, friction and a badly behaved pen offer their own review. Production software is less polite, but the lesson is similar. The model is only the beginning. ## What belongs here This is neither a technical news site nor a leadership diary. An essay may begin with frontend architecture, a management span, a cassette mechanism or a sign worth reading. It belongs here when a familiar explanation hides a consequence worth seeing. Each piece should leave enough evidence for disagreement. The conclusion is mine; the route to it should remain visible. Browse [all essays](https://www.differentshelf.com/essays/), see [what the shelf contains](https://www.differentshelf.com/#evidence), or [write to me](mailto:jupiter@jayna.family). ## Posts ### I Only Wanted the Tests to Run Faster URL: https://www.differentshelf.com/i-only-wanted-the-tests-to-run-faster/ Last updated: 2026-09-15T22:15:27.000Z *I went looking for the cause of a slow test suite and ended up with a fix merged into* [*V8*](https://v8.dev/?ref=differentshelf.com)*, the JavaScript engine behind Chrome and* [*Node.js*](https://nodejs.org/en/about?ref=differentshelf.com)*. From my Node bug report to both projects merging changes took about two and a half days. I am still rather excited about that.* For the wider argument, start with [The Coordination Tax in Your Software Estate](https://www.differentshelf.com/the-coordination-tax-in-your-software-estate/). This experience left me delighted by open-source collaboration and wondering why companies copy the separation that independent projects need, even when they own both sides. [InnerSource](https://innersourcecommons.org/learn/learning-path/introduction/01/?ref=differentshelf.com) brings open-source ways of working inside a company. The code stays private, but colleagues can read it and propose changes across team boundaries. The team responsible still sets the direction, reviews contributions and decides what to merge. My experience crossed three independently maintained projects. For anyone trying to make InnerSource work between teams in one company, there is plenty to celebrate here, and something to learn about which boundaries we actually need. ## The problem kept leading somewhere else Our tests ran under [Vitest](https://vitest.dev/?ref=differentshelf.com), a tool for running automated tests. Vitest, in turn, ran inside [Node.js](https://nodejs.org/en/about?ref=differentshelf.com). Node uses [V8](https://v8.dev/?ref=differentshelf.com) to execute JavaScript. A slowdown in the tests could come from any of those layers. I started with the one we used directly. Vitest already had a known problem with [mocks](https://vitest.dev/guide/mocking.html?ref=differentshelf.com), the functions that stand in for real ones during a test. Andrew Brennan had [reported it in January](https://github.com/vitest-dev/vitest/issues/9492?ref=differentshelf.com): as a test run continued, mocks accumulated, and clearing their state took longer. Scott Cooper [changed Vitest to stop repeatedly scanning mocks that had already been cleared](https://github.com/vitest-dev/vitest/pull/11092?ref=differentshelf.com). His change merged on 2 September. That improvement was in Vitest 5\. We were on Vitest 4, so we needed to upgrade or bring the patch back to our version. But there was another question. Our tests had become slower after a Node upgrade. Vitest's repeated cleanup explained the amount of work; it did not explain why the same work now took longer. I used AI to help investigate that question, and throughout the patch work that followed. I was not a specialist in the engine underneath Node. I still had to choose what to investigate, test the explanations and decide what the evidence justified. I remained responsible for what I submitted. To separate Vitest's behaviour from Node's, I took the relevant mock code and removed pieces while checking that the slowdown remained. Eventually, a [small JavaScript program with no dependencies](https://github.com/nodejs/node/issues/66011?ref=differentshelf.com) reproduced it. The program did not need Vitest at all. Changing the Node version was enough to make it fast or slow. That gave me a much smaller experiment. I compared Node releases and found the jump between 26.3.1 and 26.4.0\. Nightly builds narrowed it to a single day's changes. Building Node before and after the suspect change confirmed which change exposed the slowdown. Then I measured where the extra time went. It was inside V8, the engine executing the program, when it wrote values into objects. V8 keeps internal descriptions of object structure so it can reuse work. In the slow case, objects that had shared those descriptions were getting separate ones. I traced that loss of reuse to [two kinds of object competing for the same entry in a small V8 cache](https://github.com/vitest-dev/vitest/issues/9492?ref=differentshelf.com#issuecomment-5651735852). Each kept displacing the other. Both Node builds contained the same V8 version, but the Node change had altered the engine's initial state so that this particular pair collided. The upgrade had exposed an engine weakness. Now the two parts made sense together. Vitest was doing unnecessary cleanup, and V8 was making that cleanup more expensive. Reducing the work in Vitest offered a way around the slowdown. Repairing the cache in V8 could also help other code that encountered the same behaviour. I proposed changing how V8 chose the cache entry and added a regression test. On 13 September, I submitted the V8 patch and documented the reproduction and diagnosis in [Node issue #66011](https://github.com/nodejs/node/issues/66011?ref=differentshelf.com). Maintainers could check the reasoning without our application or test suite. The submission had to earn their time. A plausible explanation and generated code would not have been enough. The reproduction, comparisons between builds and regression test gave them something concrete to assess. During review, Leszek Swirski [suggested using V8's existing hash-combining function](https://chromium-review.googlesource.com/c/v8/v8/+/8397811/comment/b081e55a%5Fe77cb53f/?ref=differentshelf.com). I revised the patch and tested it more widely. I shared the unfavourable results too, so the reviewers could judge the trade-off. Leszek and Jakob Kummerow approved the change, the landing checks ran, and [the fix and regression test merged](https://chromium-review.googlesource.com/c/v8/v8/+/8397811?ref=differentshelf.com). The affected loop in the local reproduction went from [roughly 50 to 6 nanoseconds per item](https://chromium-review.googlesource.com/c/v8/v8/+/8397811/comment/315c7015%5F1551c99e/?ref=differentshelf.com), about eightfold for that operation. The benefit to an application depends on how much time it spends doing that work. I had followed a problem into an engine I had previously only used. Its maintainers helped improve the repair and kept the decision over what entered their project. I could contribute without joining their team or taking over their responsibilities. ## A small repair in a widely shared engine Part of the excitement is where the repair can travel. [V8 runs JavaScript in Chrome and Node.js](https://v8.dev/?ref=differentshelf.com). It powers [Cloudflare Workers](https://developers.cloudflare.com/workers/reference/how-workers-works/?ref=differentshelf.com), and [Electron bundles it](https://www.electronjs.org/docs/latest/why-electron?ref=differentshelf.com) into desktop software, including [Visual Studio Code and Slack](https://www.electronjs.org/apps/?ref=differentshelf.com). The same engine sits underneath remarkably different parts of an ordinary working day. Those products will not all encounter this particular slowdown. But as they adopt versions containing the fix, affected code can benefit without its authors repeating our investigation. Something that began with our tests has a route into software far beyond our application. I find that wonderful. ## Four patches across three layers Back in our own test suite, I had also [ported the Vitest improvement to Vitest 4 and tested it](https://github.com/vitest-dev/vitest/issues/9492?ref=differentshelf.com#issuecomment-5651735852) that Sunday, 13 September. A colleague then upgraded us to [Vitest 5](https://github.com/vitest-dev/vitest/releases/tag/v5.0.0?ref=differentshelf.com), so we no longer needed that patch. The version boundary had already created work, even though an upgrade eventually removed our need to carry it. While the V8 work proceeded, Matteo Collina [made a separate change in Node](https://github.com/nodejs/node/pull/66015?ref=differentshelf.com). It avoided the immediate trigger by changing when some internal symbols were created. My V8 patch addressed the cache hash directly. Someone [asked whether the Node change merely moved the problem](https://github.com/nodejs/node/pull/66015?ref=differentshelf.com#issuecomment-5654363434), leaving it liable to return after a later change. Matteo [explained why he was proceeding](https://github.com/nodejs/node/pull/66015?ref=differentshelf.com#issuecomment-5655548133): > “However we can't assume when the V8 fix is going to land, and this seems better than reverting the ffi change.” That is a sound decision for an independent project. Node is responsible for its users and cannot set V8's priorities or release schedule. It needed a remedy it could control while the engine change was still being reviewed. Look across the whole chain and there are four patches across three layers: Scott's Vitest improvement, my Vitest 4 backport, Matteo's Node mitigation and my V8 repair. Vitest reduced the work. Node avoided the trigger. V8 repaired the cache behaviour underneath. The engine defect belonged in V8, but projects above it had good reasons to help their own users sooner. The Vitest improvement has value of its own; these were four pieces of implementation work rather than four copies of one fix. Each offered a route to relief through a different version or project. The collaboration worked, and the separate release paths still required extra effort. ![One slow test suite led to four patches across three layers: Scott Cooper’s Vitest 5 improvement, my tested Vitest 4 backport, Matteo Collina’s Node mitigation and my V8 cache repair. The figure shows the questions, evidence and expert review behind the contribution. Dated records distinguish the backport from merged changes; Node and V8 merged within about 59 hours of the Node report. A separate, illustrative comparison shows how a company with shared source and integration can retain review and ownership while avoiding intermediate internal package upgrades.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/09/vitest-node-v8-contribution-timeline.png) Four pieces of implementation work, with different roles. Merge dates come from the public record; the backport date comes from my recollection and same-day report. The company comparison illustrates a choice about shared source and integration. Sources: [Vitest improvement](https://github.com/vitest-dev/vitest/pull/11092?ref=differentshelf.com), [my backport](https://github.com/vitest-dev/vitest/issues/9492?ref=differentshelf.com#issuecomment-5651735852), [Node report](https://github.com/nodejs/node/issues/66011?ref=differentshelf.com), [Node mitigation](https://github.com/nodejs/node/pull/66015?ref=differentshelf.com) and [V8 review and repair](https://chromium-review.googlesource.com/c/v8/v8/+/8397811?ref=differentshelf.com). ## This is the coordination tax That extra effort is the coordination tax: getting a shared change through separate versions, release schedules and adoption decisions. The merged V8 repair still has to pass through those steps. Node can acquire the change through a V8 upgrade or a [cherry-pick](https://git-scm.com/docs/git-cherry-pick?ref=differentshelf.com) into an existing release line. A cherry-pick need not wait for a new V8 release, but it still needs review and testing. Node must then ship the resulting runtime and its users must adopt it. [Node documents those routes](https://github.com/nodejs/node/blob/main/doc/contributing/maintaining/maintaining-V8.md?ref=differentshelf.com). Vitest needs no further release for existing tests to benefit from a fixed Node runtime. I have [asked whether a Node 26 cherry-pick would be useful](https://github.com/nodejs/node/issues/66011?ref=differentshelf.com#issuecomment-5686138544); there is no commitment yet. Matteo's mitigation gives Node its own route to relief in the meantime. This is entirely legitimate in open source. Independent projects serve different consumers, answer to different maintainers and cannot assume everyone will move together. Their freedom to evolve separately is part of what makes the ecosystem work. ## One company can make the next step easier But why recreate that separation between teams when we own the code on both sides? Team ownership does not require a separate package release, version negotiation and upgrade project for every shared repair. For our own interdependent code, that is work we choose to impose on ourselves. InnerSource gives us the contribution path I experienced here: bring evidence and a proposed improvement, then work with the owners to make it fit. The contributor owes the owning team a considered, tested proposal and the willingness to revise it. Owners bring the wider context and judgement over whether it belongs. That takes review capacity, but it does not require treating teams as independent suppliers. Consider the internal equivalent: a shared component, a layer that consumes it and an application that exposes the fault. In a [monorepo](https://research.google/pubs/why-google-stores-billions-of-lines-of-code-in-a-single-repository/?ref=differentshelf.com) where those consumers build against the same shared source, a contributor can fix the component and test the affected application against that revision. The relevant owners can review the work together. When the shared fix merges, the next consumer build can contain it. There is no need to publish an intermediate internal package and ask every consuming team to schedule its own upgrade. That can also remove the reason for a temporary implementation downstream while another team prepares its release. Open contribution gives a repair a way in; shared source can give its consumers a shorter way to benefit. A monorepo has to be operated that way. Putting separately pinned copies and independent integration queues into one directory preserves much of the delay. Builds and release controls remain, and changes across services may need staged deployment. External runtimes still need to be adopted too: moving our application code into a monorepo would not make V8 part of our own release authority. We should keep an internal release boundary because the software needs it, not simply because another team owns it. Companies can embrace the openness I found in this experience, then use the advantage independent projects do not have: we can change our own shared code and its consumers together. We do not have to organise ourselves as strangers exchanging releases. I only wanted the tests to run faster. I came away with a small contribution to V8, a great deal of gratitude to its maintainers and less patience for companies that make this harder than it needs to be. ### The Coordination Tax in Your Software Estate URL: https://www.differentshelf.com/the-coordination-tax-in-your-software-estate/ Last updated: 2026-08-26T13:11:02.000Z *A shared change once took a quarter to reach every product in a large software estate because each team had to adopt and release it separately. Reuse was meant to lower the company's marginal cost of change, yet rebuilding was often faster and cheaper for each product team.* *The technology payroll is visible. The coordination tax hides in backlogs, package upgrades, release calendars and the people employed to remember how everything fits together. Faster code generation leaves that queue intact.* In plain English: think of a repository as a controlled records room for software. Polyrepo spreads the software behind your products across separate rooms; a shared change must enter each affected room. Monorepo keeps all that software in the same room, still divided into distinct products and components, so related changes can happen together. Ownership and release can remain separate. For a large company building proprietary software, polyrepo is often the wrong default. Polyrepo makes intuitive sense in open source, where repositories often represent distinct projects with their own direction, governance and release cadence. Separate repositories preserve their autonomy. Inside one company, related proprietary systems serve the same commercial outcomes. Open source made independent software easy to combine; a company building proprietary software needs to make interdependent software easy to change. [Ronald Coase explained why companies exist: internal coordination can cost less than market exchange](https://www.nobelprize.org/prizes/economic-sciences/1991/coase/lecture/?ref=differentshelf.com). Polyrepo recreates market-style transactions inside the company whenever one change crosses repository boundaries. Engineers must find the right owners, negotiate a route through several backlogs and repeat integration and validation for every consumer. A monorepo can make related software part of the company's cheaper internal coordination system. At scale, repository boundaries often become organisational boundaries, acquiring separate owners, backlogs and delivery plans. The repository model then reinforces the communication structure Conway's Law predicts. Source topology does not by itself change the organisation, architecture or delivery model. Where inherited structures create inertia, however, a monorepo is often the precursor that makes wider change practicable. It changes the default: related source becomes discoverable and changeable together, common standards can be applied once and cross-product work can begin without first negotiating through several delivery systems. Ownership, architecture and release authority can remain distributed. Shared source gives later improvements a productive path through the company. A boundary earns its keep when the independence it buys costs less than the negotiation it introduces. ## Where the opportunity sits In one large company's proprietary software estate, work touched around 3,000 separate repositories during 90 days. The same company has already demonstrated the alternative: one shared repository serving multiple products handled about 1,400 completed changes in its busiest recent month, more than seven times its 2023 peak as products were consolidated. The fragmented estate shows the opportunity; the shared repository shows that change is feasible at enterprise volume. I do not think the wider estate is unusual; its shape will be familiar to large companies built over time through acquisition, product expansion and successive generations of technology. ![An observed operating change in one large proprietary software estate. Before consolidation, more than 15 product repositories required separate adoption and one shared change could take up to a quarter to reach every product. After consolidation, one shared codebase supported at least weekly delivery across every product for 18 months and handled about 1,400 completed source changes in its busiest month. Consolidation supported a wider change to ownership, tooling and delivery.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/monorepo-observed-operating-change-ds-2026-6.png) Consolidation creates four forms of business return: **Operating leverage.** Common ownership records, dependencies, build configuration and controls can be maintained once and applied across products. **Time to value.** Related work can cross products without entering a new discovery, negotiation, version and adoption queue each time. **Risk and control.** Common controls and evidence reduce the reconstruction required during audits and incidents. **Strategic capacity.** Familiar commands, conventions and dependencies allow engineers to follow demand across product lines. The opportunity is distributed across platform budgets, backlogs, releases and incidents. Pricing it connects local engineering improvements to company-level customer and financial outcomes. Repository count defines the surface on which coordination costs compound. As an illustrative annual model, allow an average 30 minutes of repository-specific ownership, dependency, build, policy and coordination work each week for 48 weeks of the year, leaving four weeks outside the model. Across 3,000 repositories, that equals 72,000 hours: roughly 40 engineer-years or £2.5 million at an illustrative blended loaded cost of £60,000. The investment case must replace that assumption with measured work: sample representative changes, record how many repositories and teams they touch, and separate active effort from waiting time. Then establish the addressable share and the migration and running costs. State separately whether the return appears as cash, delivery capacity or lower risk. ![A financial bridge begins with around 3,000 separate repositories carrying active work. An illustrative annual model allows 30 minutes of weekly coordination across each active repository for 48 working weeks, equivalent to 72,000 hours, about 40 engineer-years or £2.5 million of capacity exposure at a blended loaded cost of £60,000. The business case replaces the assumption with measured work, establishes the addressable share, subtracts migration and operating cost, then states whether the return lands in cash, delivery capacity or lower risk.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/repository-tax-sensitivity-ds-2026-14.png) A CFO would not accept thousands of unpriced recurring contracts because each was cheap to create. Why does engineering accept the equivalent? ## Make reuse the shortest path Reusable capability works only when adoption is easier than rebuilding. When every consumer must schedule, integrate, validate and release a package, duplication becomes locally rational and collectively expensive. Pay once for the capability and make the shared route the fastest route. A shared codebase restores the economics intended by reuse: build once, change once and make the improvement available across products. Technical separation remains available inside a shared repository; it does not require a separate delivery boundary. ## Give AI a clear path to value AI makes implementation cheaper. To turn that into company productivity, the route from idea to production must become cheaper too. When the constraint sits after coding, faster implementation delivers more work to the queue without reducing customer lead time. [DORA's 2025 research](https://dora.dev/research/2025/dora-report/?ref=differentshelf.com) describes AI as an amplifier of the company adopting it. The dividend appears only when the cost and elapsed time of the whole change fall. A monorepo gives engineers and AI the related source and dependency context needed to carry a wider change, while accountable owners retain review and production authority. [Google describes large-scale migrations](https://research.google/blog/accelerating-code-migrations-with-ai/?ref=differentshelf.com) in which related changes are generated and validated together, then routed to the owners of affected code for review. ## Make controls reusable A mandatory control should become part of delivery infrastructure, allowing the company to implement it once and apply it consistently. Across thousands of separate repositories, the same decision instead becomes an adoption campaign with repeated migrations, exceptions and follow-up. Shared source and common tooling can integrate the mechanism once, update it centrally and evidence it consistently. That leverage requires investment: a credible monorepo needs company standards for building, testing and changing software, backed by clear ownership, fine-grained access, scalable build and test infrastructure, and developer tooling. The platform cost is explicit and central; the alternative distributes reconciliation across the estate. ## Invest in fewer, stronger boundaries A monorepo can remove a source-coordination boundary while products remain separately owned, controlled and deployed. Team structures, release cadences and life cycles are not automatic reasons for separate source. Keep a repository boundary when the independence it protects is worth its recurring cost. Untouched repositories can be among the riskiest in the estate. Their chance of receiving security updates is limited, while build paths and ownership decay. Inactive software still in use needs an explicit owner and maintenance path. Software no longer in use should be archived or deleted. Repository boundaries are cheap to create and expensive to remove. They survive product convergence and reorganisations because no future initiative owns their removal. Finance and technology should be able to answer three questions: 1. In a representative customer change, how much time is active work and how much is waiting at repository or team boundaries? 2. What annual cost and delay would consolidation remove, what would it cost, and would the benefit appear as cash, delivery capacity or lower risk? 3. Which boundaries buy necessary independence, and which merely encode historical organisational structure? Companies exist because coordination within them can cost less than exchange between them. When shared work must negotiate its way across a repository boundary, the company gives back part of that advantage. Retain only the boundaries whose independence earns the recurring cost. ### Faux InnerSource URL: https://www.differentshelf.com/faux-innersource/ Last updated: 2026-08-25T13:15:00.000Z *An internally open repository can still preserve every old boundary.* *InnerSource often begins when a Host Team cannot deliver a feature in time and a Guest Team can. That is a legitimate starting point. Faux InnerSource mistakes that beginning for the operating model. It borrows hands while keeping context, decisions and practical stewardship closed. Every contribution starts again from zero.* The useful distinction is not between an organisation with controls and one without them. Host Teams remain accountable. Product responsibility exists on both sides. For a product change, the Guest and Host product counterparts need a handshake on the intended outcome, priority, existing capability and destination direction. Architecture, security and release decisions may need specialist judgement. Regulated software does not become less regulated because the contributor sits elsewhere. The test is what the contribution leaves behind. If a Guest Team supplies implementation and the project returns unchanged to the same small group of decision-makers, the organisation has borrowed capacity. If the contribution process leaves the project easier to discover, build, test, understand and deliver, with knowledge the next Contributor can reuse and wider stewardship where it helps, the organisation has built capability. Done well, InnerSource lets comparable future contributions reuse more context and evidence. Faux InnerSource preserves the bottleneck and gives it a contribution process. ## What faux InnerSource looks like Faux InnerSource turns a legitimate starting condition into a permanent boundary. Host incapacity becomes the only permitted entry condition. The Guest Team supplies labour without making future contribution easier. Here is the recognisable pattern. Contribution is permitted only after the Host Team cannot deliver. A small correction and a cross-service change pass through much the same front door, documents, meetings and approvals. The Guest Team owns design, implementation, testing, deployment and post-release support; the Host Team retains the material decisions. Consequential work may be ruled out before contribution can begin. The next Guest Team starts the same passage with the same people and reconstructed context. That is an exception process for borrowing implementation capacity, not an engineering system learning to collaborate. The problem is not Host Team accountability. Trust, evidence and improvements to the project cannot change the route. Discovery begins with permission rather than a draft change. Governance capacity is rationed independently of demand and consequence. Review returns to the same scarce people. The project remains no easier for the next capable engineer to change. None of those controls proves the model is false on its own. A high-risk change may need every one of them. The tell is that the process cannot become lighter through evidence, familiarity or improvement to the project. It can accept code from outside the Host Team, but it does not develop an internally open engineering system. The bottleneck moves from the Host Team's backlog to its design decisions, review attention and release authority. Those decisions may be necessary, but their capacity has not grown with the supply of proposed work. More contributors do not create more decision capacity. Expensive passages change behaviour. Contributors may batch more into each submission because each review cycle is costly. Maintainers then receive larger, less familiar changes, which take longer to understand. Other teams may avoid the shared component or reproduce capability inside boundaries they control. These are possible effects, not proof of bad intent. The answer is a proportionate route that consumes less scarce context. ## InnerSource has a standard vocabulary [InnerSource Commons](https://innersourcecommons.org/learn/learning-path/introduction/03/?ref=differentshelf.com) describes a Host Team, which owns the project, and a Guest Team, whose Contributor proposes a change through a pull request, called a merge request in GitLab. Product has a role on both sides. The Guest counterpart brings the priority, intended outcome and success measure. The Host counterpart brings knowledge of existing capability and destination direction. For new features and material capability changes, they agree a contribution path before substantial work begins. Routine fixes and local refactors need no Product handshake. Trusted Committers guide contributors, review work and protect the health of the project. The corresponding principles are [Openness, Transparency, Prioritized Mentorship and Voluntary Code Contribution](https://innersourcecommons.org/learn/learning-path/introduction/05/?ref=differentshelf.com). None gives a Contributor the right to merge. None removes the Host Team's enduring responsibility. Once a team chooses to host an InnerSource project, direction and contribution rules should be visible and mentorship treated as project work. The Host Team may decline a contribution before work begins. Once it agrees a contribution path, it owes timely review against its published standards. Agreement creates a credible route to a decision, never a guarantee of merge. The contribution is voluntary. A declared InnerSource project must still be ready to receive it. InnerSource also supports different [governance levels](https://patterns.innersourcecommons.org/p/governance-levels?ref=differentshelf.com). A Host Team may welcome Guest Contributions while retaining control and strategy. Other projects may grant shared write access or develop shared ownership. These are legitimate choices, not a ladder every Contributor must climb. Retained authority alone is not evidence of faux InnerSource. ## The next contribution tests the system An effective Host Team becomes good at being understood. Its direction, ownership and contribution standards are visible. A capable engineer can build it, run meaningful tests and trace a changed line to a running system. Interfaces make sense beyond their authors. Deployment and observability evidence do not depend on private knowledge. These are not courtesies for Guest Teams. They are properties of healthy software. If another engineer cannot build the project, the build has exposed a constraint. If only an incumbent can interpret the tests, the tests have exposed a constraint. If a meeting is required to discover the owner, ownership has exposed a constraint. InnerSource makes these conditions visible because an unfamiliar but capable engineer attempts a real change. That obligation to mentor creates useful pressure. Maintainers must explain decisions well enough that a comparable future contribution can reuse the context. Repeated questions should improve documentation, automation or interfaces. Good review transfers context instead of merely checking compliance. The Guest Team changes too. Its engineers work within another team's constraints, learn the operational consequences of their decisions and contribute evidence that remains after they leave. They become participants in a shared engineering system, not customers passing requirements across a boundary. ## Controls are not the dividing line Design constraints, scheduled review, controlled release and temporary post-release accountability can all belong to legitimate InnerSource. A Guest contribution should follow the Host Team's normal path, standards and quality bar as closely as practical. Additional control should follow the change, not the Contributor's place in the organisation. A shared library may need little beyond owner review and automated compatibility evidence. A service that handles identity, money or regulated behaviour may require security review, segregation of duties and a controlled production decision. A change that crosses repositories or team boundaries may need more integration evidence than a contained one. Those boundaries can increase coordination, atomicity, operational and support risk. They should inform the assessment. They should not determine the control path by themselves. The Guest Team may need to validate the outcome and correct defects attributable to its contribution until the agreed outcome and stability evidence are met. The Host Team remains the service owner throughout. The better question is whether the control responds to the consequence of the change and the evidence available. A returning Contributor with demonstrated project knowledge, reliable automated tests and a reversible change should not consume the same explanatory effort as a first contribution to an opaque, high-risk service. [DORA's guidance on change approval](https://dora.dev/capabilities/streamlining-change-approval/?ref=differentshelf.com) favours peer review and automation for individual changes, with additional scrutiny for high-risk work. It also recommends examining the change process end to end and identifying bottlenecks. The aim is not the fewest controls. It is the shortest trustworthy path from proposal to evidence. ## The merge request can hold the discovery Approval processes often assume a clean sequence: define the requirement, approve the design, then permit development. Real engineering is less obedient. An implementation reveals an awkward dependency. A test exposes an assumption hidden in an interface. A small experiment shows that a proposed abstraction is unnecessary. Coding is one of the ways engineers discover the shape of the problem. A draft merge request lets design, experiment and review develop beside the implementation. It creates no entitlement to merge. The Host Team can reject the approach, ask for a different boundary or decline the contribution. The Contributor accepts that exploratory code may never merge. Acceptance governs shared software and its production path, not whether an engineer may learn enough to make a good proposal. A routine contribution does not become safer because its reasoning is copied into a second system. The merge request can hold the problem, the proposed decision, the implementation, discussion, accountable approval, automated evidence and links to deployment and observation. In GitLab, a draft merge request makes the provisional state explicit. The evidence stays attached to the revision it describes. A separate RFC or architecture decision record is warranted when the decision must outlive this implementation, establishes a shared dependency, spans several repositories, commits material cost or changes a lasting security, privacy or operational obligation. It should be linked from the change, not reproduced for another audience. Evidence should accumulate around the change, not be recopied between systems. The first Guest Team may need a meeting and a separate explanation because the project does not expose its decisions well. The next should find more of that context beside the code. ## Stewardship can expand A Trusted Committer is not required. In an exception-only model, every contribution returns to the same scarce approvers. Familiarity does not expand the organisation's ability to decide. In a capability-building model, demonstrated stewardship can lead to the [Trusted Committer](https://patterns.innersourcecommons.org/p/trusted-committer?ref=differentshelf.com) role. Scope remains explicit. The Host Team retains accountability. Authority grows because a Contributor has shown judgement, project knowledge and care for its operation. Not every Contributor will want that role, and some Host Teams should retain all write authority. Repeated contribution should still create reusable knowledge when authority does not move. ## Measure whether the system learns InnerSource should be judged across contributions, not by the publication of a policy or the opening of a repository. Measure time to the first accepted change and the share spent waiting. Track review batch size, change-failure rate and the proportion of Contributors who return. Compare work of similar consequence and complexity. Separate the advantage of a returning Contributor's memory from the onboarding cost for a new capable Contributor. Count improvement as system learning only when it can be traced to retained documentation, automation, evidence or distributed stewardship. Record how many projects can be built and tested from their published instructions. Where the governance model permits it, track how many Contributors earn scoped stewardship. Reserved review capacity and additional gates may be sensible scaffolding while a Host Team improves brittle builds, implicit contracts or operational evidence. Scaffolding should have an expected effect. If comparable work keeps consuming the same reconstructed context, the operating model is not yet building capability. In [The Code Belongs to Everyone](https://www.differentshelf.com/the-code-belongs-to-everyone/), I argued that the ability to propose a change should be the invitation. An open door is necessary. The Host Team becoming easier to contribute to is the result. InnerSource may begin because one team needs another team's capacity. Its lasting value is the engineering organisation they leave behind. ### Nx Should Make Code Graphs Cacheable URL: https://www.differentshelf.com/nx-should-make-code-graphs-cacheable/ Last updated: 2026-08-24T16:18:17.000Z *Part Two of two. Nx already knows which files changed, which projects own them and how to share cached outputs. Its opportunity is to make finer indexes revision-aware, reusable and queryable.* --- [Nx](https://nx.dev/?ref=differentshelf.com) is an open-source monorepo platform and build system. It maps projects and their dependencies, uses that graph to decide which tasks must run, and can share cached task outputs through Nx Cloud. Our monorepo contains 1,091 Nx projects and 9,946 captured dependencies. From a distance, the graph is impressive. It shows Angular and TypeScript, .NET, workspace tooling and the first coarse outlines of native code. The test is whether finer resolution cuts agent discovery time, token use and usage-based AI spend without lowering the change success rate. Then I try to zoom in. Nx can tell me which project depends on another project and which source file created the edge. It cannot normally tell me which method calls another method, which implementation satisfies an interface or which route reaches a particular service. The image has structure, but it runs out of resolution just when an unfamiliar codebase becomes interesting. Nx's v21 documentation called its workspace model a [knowledge graph](https://21.nx.dev/docs/getting-started/intro?ref=differentshelf.com). The current model remains a Project Graph of projects and dependencies. Either description fits, but only from a great height. The surprise is not that a build graph stops at projects. It is that Nx already owns valuable infrastructure around the next level down. The hypothesis worth testing is not simply whether Nx can build another local index. It is whether code analysis is expensive and repetitive enough for Nx Cloud to reuse indexes across developers, agents and continuous integration when declared inputs match. The value depends on indexing cost, branch overlap and cache-hit rate. Nx already has much of the machinery needed to run that experiment. This follows [A Monorepo Is a Knowledge Graph](https://www.differentshelf.com/a-monorepo-is-a-knowledge-graph/). Part One argues that repositories and build systems should own source and build facts; this part asks whether Nx should help map the code within each project. ## The Graph Stops at Projects The public [Project Graph](https://nx.dev/docs/reference/devkit/ProjectGraph?ref=differentshelf.com) contains projects, external packages and dependencies. Nx can record the [source file that created a static dependency](https://nx.dev/docs/reference/devkit/StaticDependency?ref=differentshelf.com). It combines that model with targets to decide what runs, in what order and which cached outputs it can restore. That is operational knowledge. A missing or incorrect edge can miss tests or permit stale cache reuse; an unnecessary edge usually creates extra work. Nx has good reason to keep the model quick and conservative. It stops at project knowledge. A project node may conceal thousands of functions, classes, routes and message handlers. An edge proves that two projects are related, but not the call path that matters to the engineer standing in one of them. ![The operational Nx Project Graph selects a project through a captured dependency. An advisory Code Graph then resolves illustrative file, class, route, method and callee relationships inside that scope.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/nx-graph-two-resolutions-ds-2026.png) The gap appears even where source is already co-located. Our native wrapper projects contain Swift and Kotlin, yet the Project Graph exposes no internal relationships. What is missing is resolution, not source. That gap is not an argument for abandoning the Project Graph. It is an argument for placing a Code Graph beside it. ## Nx Already Owns Part of the Plumbing A useful Code Graph must answer three questions before it answers anything about code: which source state does this fact describe, which extraction rules produced it, and when should it be recomputed? Nx already knows when declared inputs change. Its daemon [keeps the Project Graph warm](https://21.nx.dev/docs/reference/devkit/createProjectGraphAsync?ref=differentshelf.com). Plugins map files to projects, and the change calculation identifies projects a change can influence. Its task system hashes declared source, configuration, dependency and runtime inputs. Nx Cloud can [restore outputs for a matching computation hash](https://21.nx.dev/docs/concepts/how-caching-works?ref=differentshelf.com). Nx can detect matching inputs. The query layer must still prove which checkout matches the cached index. This machinery detects changed inputs and distributes matching outputs. A computation hash identifies declared computation inputs, not the unique checkout that requested them. The cache key must include every source, configuration and extractor input the analysis can read; the index should record those inputs and the extractor version as provenance. At query time, the service should record the current commit and dirty-working-tree state, verify that the checkout produces the same computation hash, and attach that checkout identity to the answer. An index cached by computation hash can save the organisation from repeated analysis when those declared inputs match. The cache supplies computation identity; the query layer supplies revision provenance. This preserves reuse without mislabelling one commit as another. For change-impact questions, the Project Graph can seed the search with affected projects. It should not necessarily bound the search because symbol paths may cross dependants or relationships the build graph does not capture. Polygraph could eventually federate such indexes across repositories; its current public tools federate repository context, sessions, pull requests and continuous integration. ## KiroGraph Shows the Missing Resolution As of 17 August 2026, [KiroGraph](https://github.com/davide-desio-eleva/kirograph?ref=differentshelf.com) demonstrates one practical shape for the lower layer. Its documentation describes Tree-sitter extraction across more than 30 languages, with functions, classes, calls, routes and type hierarchies stored in local SQLite. Optional modules add architecture metrics, documentation retrieval, embeddings and persistent observations. This is useful code knowledge, but KiroGraph maintains another index beside the build. Its [index and sync lifecycle](https://github.com/davide-desio-eleva/kirograph/blob/main/docs/guide/how-it-works.md?ref=differentshelf.com) must determine freshness independently because it does not own the project graph, affected calculation or build cache. KiroGraph is a useful feasibility example, not proof of broad demand or production-scale accuracy. It supplies the resolution Nx lacks while exposing work that an independent index must repeat: mapping files to projects, tracking source state and keeping results current. The combination could be stronger than either tool alone. Nx could seed the search with affected projects. The Code Graph could trace relevant symbols. Compilers and tests would challenge the inferred path. A plausible call edge can guide exploration without silently deciding that a test need not run. ## The Public Roadmap Travels Sideways Nx's [2026 roadmap](https://nx.dev/blog/nx-2026-roadmap?ref=differentshelf.com), reviewed on 17 August 2026, uses the phrase full codebase intelligence. Its concrete graph investments broaden or accelerate the Project Graph: synthetic monorepos, additional languages and loading parts of very large graphs only when needed. [Polygraph's public tools](https://docs.trypolygraph.com/?ref=differentshelf.com) coordinate repositories, agents, pull requests and continuous integration; they do not document symbol or call-graph queries. In the public roadmap and open-source surface I reviewed, the clearest movement towards symbols was an external proposal for [tree-shaking-based dependency narrowing](https://github.com/nrwl/nx/issues/35797?ref=differentshelf.com). It remained low priority on 17 August 2026\. The proposal uses symbol analysis to narrow Project Graph edges and improve change selection; it does not create a persistent graph that an engineer or agent can query. That search cannot establish Nx's private plans. It suggests that full codebase intelligence is currently broader product language than a commitment to a persistent symbol graph. ## Keep the Two Graphs Honest Millions of queryable symbol nodes should not be added wholesale to the Project Graph. Dynamic dispatch, reflection, generated code, dependency injection and network calls make some relationships uncertain. High-confidence semantic extraction often needs language-specific, compiler-aware analysis. Symbol identities must survive renames, and advisory nodes could slow a graph on the critical path. Those constraints do not automatically make a Code Graph an Nx product. Language servers and compilers already hold much of the semantic truth. The bounded opportunity is for Nx to own the source-state, project, invalidation and cache contract that makes code indexes incremental and composable. First-party indexes could prove that contract in selected ecosystems; external analysers could supply the rest. The Project Graph should remain operational: small, fast and conservative enough to govern builds, tests and caching. The Code Graph should be advisory: detailed, provenance-rich and allowed to say that an edge is inferred. They should connect at the project and file boundary without pretending to share the same authority. The Project Graph should decide what runs. The Code Graph should explain where to look. That separation also protects Nx from turning a build tool into a general knowledge platform. It need not ingest every deployment, incident, ownership judgement or business process. It needs to model the code closely enough that other systems do not have to begin by rediscovering the source tree. ## Add the Missing Lens Start with four commands: `nx code callers`, `nx code implementations`, `nx code path` and `nx code impact`. Offer the same contract through an API or Model Context Protocol adapter if consumers need one; the graph should not depend on either interface. Platform teams should own the indexing contract, provenance model and query service. Language-integration owners should maintain extractors. Product teams should not curate a second description of relationships the source can reveal. Nx Cloud could distribute versioned Code Graph indexes as cached task artefacts, avoiding repeated analysis across developers and agents. Polygraph could later join those indexes across repositories. For Nx, this could become a versioned indexing capability. For an engineering leader, the test is simpler: does it raise the change success rate and dependency recall, or reduce discovery time and token use, enough to repay indexing latency, language maintenance, source-code access risk and vendor dependence? Measure cache-hit rate, branch freshness, false edges and cost per successful change against the Project Graph alone before centralising the Code Graph. Nx does not need to build every parser or own every semantic fact. It already knows where each project lives and when its declared inputs change. It needs to make finer code indexes revision-aware, incremental and queryable, then prove which parts deserve to become Nx products. ### The CLI Will Outlive Your MCP Server URL: https://www.differentshelf.com/the-cli-will-outlive-your-mcp-server/ Last updated: 2026-08-24T16:18:17.000Z *MCP wraps AI-friendly conventions around software that should already have a good API or CLI. Fix the source, put security and policy where they belong, and treat the wrapper as disposable.* --- Twenty years ago, enterprise software wrapped XML in [SOAP envelopes](https://www.w3.org/TR/soap12-part0/?ref=differentshelf.com) and described services through machine-readable contracts. SOAP solved a real interoperability problem. Then [the machinery became the architecture](https://www.w3.org/TR/ws-arch/?ref=differentshelf.com). The surrounding WS-\* family accumulated standards for security, policy, discovery, addressing, transactions and reliable messaging. Each made sense alone. Together they produced an industry of envelopes, generated clients and specialists explaining why those clients had stopped speaking to one another. I feel a faint memory of SOAP whenever the response to "we need AI to use our system" is "build an MCP server". The Model Context Protocol (MCP) is lighter and uses JSON-RPC rather than XML. It gives AI hosts a common way to discover and invoke tools. That is convenient. But convenience is becoming confused with architecture, and the protocol is receiving credit for problems that already have better owners. Every frontier-model release quietly cancels part of this integration roadmap. Models are getting better at reading `--help`, inspecting schemas, writing small programs, calling APIs and filtering JSON. The half-life of a hand-written wrapper around those capabilities is shrinking. The source-first rule is simple: improve the authoritative API or command-line interface (CLI) before compensating for it. Generate what can be generated. Add an adapter only for value that remains. I came to this view through use, not protocol design. Across strategy work and substantial changes to TypeScript, .NET, iOS, Android, Kotlin and Java, frontier coding agents relied mainly on files, build tools, documentation and ordinary command lines. MCP was rarely central. Remote systems have not required custom MCP servers either. I use Cloudflare, GitLab, Jira, Confluence and Slack through their APIs. GitLab already publishes an API and [glab](https://docs.gitlab.com/cli/?ref=differentshelf.com); every capable coding agent with shell access can use them. Adding an MCP server does not create access to GitLab. It creates another way to perform the same operations. Chrome complicated the argument. Access to a real browser, rendered pages, console output, network traffic and performance traces changed what an agent could verify. Its [experimental CLI](https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/cli.md?ref=differentshelf.com) exposes those tools through ordinary commands and JSON, although it is generated from the MCP definitions and talks to a background MCP daemon. The valuable asset is the browser capability and its authoritative schema. The CLI demonstrates that MCP need not be the model-facing interface. ## The Wrapper Is Not the Capability An MCP tool has a name, description and input schema. It has the same semantic problem as any interface: somebody must explain what the operation does, what its fields mean and what happens when it fails. Wrapping a poor API and poor documentation in MCP does not repair the underlying system. It creates a second API and documentation surface. The new layer may have nicer names, but somebody must keep them aligned with the old ones. A hand-written wrapper also creates another versioned contract. Changes to the underlying API, MCP software development kit or client behaviour can require translation work that adds no product capability. What a bore. Several independently built wrappers around the same product are not evidence that the organisation needs several MCP servers. They are product feedback. If each adapter adds its own search, discovery, comparison, caching, recent-change queries or authentication workarounds, those capabilities belong upstream. The best wrapper may be a useful prototype and an excellent requirements document. Consolidating the wrappers into another wrapper merely preserves the wrong ownership. This duplication made more sense when models could not explore software reliably. In February 2026, Nx explained [why it deleted most of its MCP tools](https://nx.dev/blog/why-we-deleted-most-of-our-mcp-tools?ref=differentshelf.com). A capable agent could call `nx show project`, process the result with `jq` and retain only the facts it needed. Passing a large project graph through a dedicated tool added tokens without adding capability. Anthropic reached a related conclusion from inside the MCP ecosystem. In its illustrative [code execution with MCP](https://www.anthropic.com/engineering/code-execution-with-mcp?ref=differentshelf.com) workflow, loading definitions on demand and filtering results in code reduced tool-related context from 150,000 tokens to 2,000\. MCP remained underneath. The gain came from letting the model use ordinary software techniques rather than making every intermediate result part of the conversation. Better models can remove the need for a wrapper. They cannot remove the need for a good interface. For local software, the CLI is the durable default. It offers discovery through `--help`, composition through pipes, exit codes, operating-system permissions and decades of tooling. Add non-interactive operation, structured errors and optional JSON output, and humans, continuous integration and agents can use the same interface. For a remote service, the equivalent investment is a well-designed API with a machine-readable contract. Improve the interface every consumer can use before funding one only AI clients need. Documentation changes shape in the same way. A model can generate a tour of an API when needed. It cannot reliably infer whether `pending` means awaiting approval, awaiting settlement or merely stuck. Tool descriptions and JSON schemas are documentation in another format. Explanatory prose is becoming cheap; names, contracts, invariants and error semantics are becoming more valuable. As I argued in [The Code Is No Longer the Scarce Part](https://www.differentshelf.com/the-code-is-no-longer-the-scarce-part/), producing prose is cheap; maintaining truth is not. ## MCP Is the Wrong Layer The standard defences of MCP sound persuasive because they name real problems: credentials, restricted network access, consent, audit, state and communication with live processes. The mistake is assigning those problems to MCP. Credentials belong in identity infrastructure, workload identity or a credential broker. Authorisation belongs in the service and may be enforced through an API gateway. Network restrictions belong in egress policy, proxies and firewalls. Rate limits and request policy belong in the gateway. Audit belongs in gateway, service and host telemetry. Human consent belongs in the host or an approval workflow. Business state belongs in the authoritative service. A protocol can expose an operation. It should not own the operation's policy, identity or state. If an agent must not reach an administrative API, expose a narrow operation behind a gateway. If its process must not see a token, let a credential broker or trusted service perform the call. If an action needs approval, make the host ask. An MCP server can sit somewhere in each design, but the security comes from those controls, not from MCP. The live-process argument is no stronger. Chrome has the [Chrome DevTools Protocol](https://developer.chrome.com/docs/devtools/protocol-monitor?ref=differentshelf.com). Debuggers, databases, build systems and integrated development environments have native protocols, sockets, APIs and CLIs. MCP can wrap them. It does not create the connection or own the state. Nor does the absence of a shell establish a need for MCP. If a host can call an MCP server, it can call a constrained HTTP API. Choosing one sanctioned integration surface may improve the product experience, but it is a packaging decision. ## The LSP Defence Is Weaker Than It Looks The strongest case for MCP is the Language Server Protocol (LSP). Before [LSP](https://microsoft.github.io/language-server-protocol/?ref=differentshelf.com), support for each programming language had to be rebuilt for each editor. A shared protocol let one language server work with many tools. But LSP did more than standardise an envelope. It defined shared semantics for documents, diagnostics, symbols, completions and edits. Editors could not inspect an arbitrary language service and improvise those behaviours. Frontier agents can improvise. They can all call the same documented CLI. The shared integration already exists; adding MCP changes the route rather than the destination. ![Two routes reach the same GitLab API. The direct route uses the documented glab command line. The wrapped route adds an MCP client and MCP server without changing the authoritative capability or policy boundary.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/mcp-route-comparison-ds-2026.png) MCP standardises how a host lists and invokes GitLab-specific tools. It does not standardise what a GitLab merge request and a GitHub pull request mean. The model supplies much of that semantic adaptation by reading the interfaces. This changes the old many-to-many arithmetic. A common protocol reduces integrations only when every client would otherwise require bespoke code. If every capable agent can use the same API or CLI, the common integration was already there. MCP may still win distribution. Hosts may build marketplaces, vendors may publish adapters and customers may ask for the familiar checkbox. Standards acquire network effects. That is a commercial reason to offer MCP, not an architectural reason to put product meaning, security policy or business logic inside it. Generate the adapter from the authoritative interface and keep it disposable. ## Fix the Source Before Adding the Overlay Before funding an MCP server, ask a frontier model to use the existing interface. Give it the CLI, API documentation, examples and a safe execution environment. Watch where it fails. If the failure is ambiguous names, inconsistent errors, missing schema, unsafe retries or awkward authentication, fix the product interface. Humans and every form of automation benefit. An MCP wrapper over the same defect merely makes the defect less visible. Harvest useful semantics from the adapters, move them into the authoritative API or client, and generate whatever compatibility layer remains. This is the same source-first discipline I argued for in [A Monorepo Is a Knowledge Graph](https://www.differentshelf.com/a-monorepo-is-a-knowledge-graph/). Start with authoritative source and build relationships, then add a graph for runtime and human knowledge that does not exist there. An overlay earns its place by answering a residual question, not by concealing a foundation we declined to fix. MCP may survive as a compatibility format. SOAP survived too. Survival does not make a protocol the right centre of gravity. The CLI will outlive your MCP server. Build the durable interface first, then generate the envelope if the market still wants one. ### A Monorepo Is a Knowledge Graph URL: https://www.differentshelf.com/a-monorepo-is-a-knowledge-graph/ Last updated: 2026-08-24T16:18:16.000Z *Part One of two. Split related code across repositories and you must reconstruct its shared graph. A graph-aware monorepo can reduce the time and tokens AI spends rediscovering it.* --- A monorepo has an underappreciated quality: it remembers. Not as a grand semantic model of everything, but as an operational dependency graph whose edges the build can use. Every import, project reference and build target is raw material for a live model. A graph-aware build tool turns that material into an operational graph that can answer practical questions: what depends on this, what must run, and where can a change travel? That is the best case for a monorepo. It does not merely put code in one place. It turns knowledge about how software fits together into infrastructure used by ordinary development. AI makes the cost of missing structure measurable. An agent spends more time finding repositories and more tokens retrieving context that the build could have selected. A better graph should reduce both without lowering the change success rate. Lower token use can also reduce usage-based AI spend. Some repository boundaries are real. Restricted access, separate legal ownership, different lifecycles and software that rarely changes together can justify separate source. The expensive mistake is using a repository boundary when the only boundary is the organisation chart. Team autonomy and independent deployment do not, by themselves, require separate history. After enough repository-by-default decisions, a global question becomes strangely difficult: if this changes, what else might break? A knowledge graph is the natural response: create a machine-readable map of repositories, services, dependencies, owners and deployments. People and AI agents can then discover what exists, understand an unfamiliar system, estimate the blast radius of a change and find the right team during an incident. Build the map, but begin with the sources that already govern the work. Let the repository and build own source and build facts. Add code, deployment, runtime and human overlays only for questions they cannot answer. A reconstructed graph watches the code. An operational build graph participates in changing it. The duplication then compounds. One team builds a software catalogue. Another crawls source code for dependencies. A third generates documentation. Someone adds a graph database to reconnect the pieces. Then AI is given tools for asking the graph where the code lives. The result can be sophisticated. It is also a little like dividing a library among a thousand locked cupboards and commissioning an excellent map room next door. The map room may be useful. A monorepo gives it fewer cupboards to reconcile and a stronger map to begin with. ## The Graph Comes With the Work An app imported a library. A build required another build to finish first. A project reference had consumers. Splitting related code across repositories removed none of those relationships. What it often removed was a shared transaction in which they could be changed and tested together. A federated build can reproduce much of this by pinning repository revisions and joining their graphs. It must still build the revision-coordination machinery that one repository supplies by default, and a cross-repository change is still not one source commit. Where that machinery is absent, reconstruction begins. [Nx](https://nx.dev/?ref=differentshelf.com) is an open-source monorepo platform and build system. Its team has now commercialised that reconstruction path as [Polygraph](https://trypolygraph.com/?ref=differentshelf.com). Polygraph indexes repositories and connects them through packages and APIs, then gives agents a cross-repository graph, persistent sessions and coordinated pull requests and continuous integration. Nx calls the result a synthetic monorepo. This is useful for boundaries that must remain. It is also revealing: scattered repositories require extra machinery to recover context that one repository supplies by construction. Polygraph can coordinate several commits; it cannot turn them into one atomic source change. Nor does a monorepo supply all the semantic context and workflow that Polygraph adds. Static analysis reconstructs imports. Parsers infer endpoints and message topics. Scheduled jobs check for drift. Language models turn fragments into plausible flows. Service owners are then asked to correct the result. No individual response is ridiculous. Together they can form a second software estate whose job is to observe the first. I have already made the broader case for [why a monorepo matters](https://www.differentshelf.com/what-is-a-monorepo-and-why-does-it-matter/). Here, the narrower consequence is how much knowledge repository fragmentation forces us to reconstruct. ## Make the Graph Operational A monorepo alone is merely a large folder. The useful combination is a monorepo and a graph-aware build system, such as Nx, Bazel, Buck or Pants. Nx is a useful example. Its [Project Graph](https://nx.dev/docs/features/explore-graph?ref=differentshelf.com) represents projects and dependencies; Nx combines it with task configuration to construct the Task Graph that selects and orders work. Configured rules can enforce dependency boundaries, and the model is available to other tools. First-party integrations cover TypeScript, [.NET through MSBuild](https://nx.dev/docs/technologies/dotnet/introduction?ref=differentshelf.com) and Java through [Gradle](https://nx.dev/docs/technologies/java/gradle/introduction?ref=differentshelf.com) or the experimental [Maven integration](https://nx.dev/docs/technologies/java/maven/introduction?ref=differentshelf.com). Polyglot does not have to mean polyrepo. Reading the Nx 23 source makes the shape reassuringly plain. Plugins can [discover projects, add dependencies and enrich metadata](https://github.com/nrwl/nx/blob/23.0.0/packages/nx/src/project-graph/plugins/public-api.ts?ref=differentshelf.com); `nx graph` can export the resulting model as JSON. That makes the workspace graph available to other tools, although durable consumers still need a versioned contract rather than assume every exported field will remain stable. That machine-readable model changes the maintenance bargain. A catalogue can keep descriptors near code, but duplicated facts still require two representations. Imports, project references and build manifests change with the software; continuous integration uses them before merge. On 17 August 2026, our Nx export contained 1,091 project nodes and 9,946 captured project dependencies. It modelled the TypeScript, Angular and .NET estates in detail, but saw the native code only as two wrappers. ![An Nx export records 1,091 projects and 9,946 dependencies. TypeScript and Angular dominate the graph; the native source is present as two projects but has no captured internal native dependencies.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/monorepo-operational-graph-ds-2026.png) The empty native row identifies the next piece of platform work: let Gradle report Android and Kotlin project dependencies, and build the equivalent project adapter for Xcode. Source co-location gives both integrations one checkout by default; modelling turns it into a useful operational graph. Even a complete operational graph would not explain everything. We still keep steering and domain documentation for accessibility, design systems and server-side rendering. Machines maintain what the build can know; people explain what requires judgement. Knowledge kept beside the work can still drift. Knowledge used by the work is harder to ignore. The build graph is not infallible. Nx says its calculations are [only as accurate as their edges](https://nx.dev/docs/kb/add-language-support?ref=differentshelf.com). Reflection, runtime configuration, HTTP calls and undeclared message flows can escape it. A missing edge may under-select tests instead of failing the build. But use places the graph under pressure. A missing or incorrectly declared edge can cause a missed test or stale cache reuse; an unnecessary edge usually causes extra work. An independently reconstructed graph can be wrong while every build remains green. For facts the build already governs, that is weaker authority. Block shows both the gain and the cost. In March 2026 it described [moving roughly 450 Cash App JVM services and libraries into a monorepo](https://engineering.block.xyz/blog/from-polyrepo-fragmentation-to-monorepo-leverage?ref=differentshelf.com). A core-library change used by hundreds of services reached a green build in ten minutes. Two years earlier, the same kind of change had required days of coordination across dozens of repositories. Moving the books was not free. The work included a seven-month pilot, an 18-month migration, shared Gradle plugins, flaky-test control, merge queues and a custom IntelliJ plugin. Some outliers stayed outside. The monorepo did not delete platform work. It moved work from reconstruction into build infrastructure, where it could prevent a bad change rather than document one later. ## Let the Agent Query the Build The fragmentation tax becomes acute on a cross-repository change. An agent must discover which repositories matter, retrieve summaries, resolve contradictions and eventually inspect the source. Humans hid the same cost rather well, calling it coordination, onboarding, release management or knowing whom to ask. A monorepo does not make the whole codebase fit inside a prompt. Nor should it. It makes a maintained dependency graph available for selecting the small part that matters. Two studies show what dependency graphs add, although neither compares monorepos with polyrepos. The final July 2024 [CodePlan](https://doi.org/10.1145/3643757?ref=differentshelf.com) paper reports that its generated changes built without errors and made the expected code edits in five of seven repositories; baselines using similar context without dependency-led planning did so in none. In a January 2026 preprint, the [Repository Intelligence Graph](https://arxiv.org/abs/2601.10112?ref=differentshelf.com) authors report that deterministic build-and-test maps improved mean accuracy by 12.2 per cent and reduced completion time by 53.9 per cent across three agents and eight repositories. CodePlan tests repository editing; Repository Intelligence Graph tests structured architecture questions. The narrower claim is this: when the build graph can also localise an agent's work, other systems have less context to reconstruct. Test raw source, the operational graph, a fine-grained code graph and both graphs together. Count discovery calls, token use, elapsed time, change-selection accuracy, change success rate and indexing cost. Otherwise a code graph may receive credit for relationships the build already knew. Context engineering is valuable. Context reconstruction is the tax to measure. An agent can query the graph through `nx show project`, machine-readable command output or a thin API. Model Context Protocol may provide another adapter; the operational graph should outlive every interface, as I argue in [The CLI Will Outlive Your MCP Server](https://www.differentshelf.com/the-cli-will-outlive-your-mcp-server/). ## Know Where the Operational Graph Stops Nx maps projects and tasks, not method calls, implementations or routes. A semantic Code Graph can supply that detail, but it should remain advisory. Let the Project Graph select work; let the Code Graph trace symbols inside it; let compilers and tests challenge the inference. A monorepo lets both graphs begin from the same checkout. A shared service may cache their indexes, but every answer must identify its source state and extraction rules. A developer's current checkout must outrank a stale central snapshot. [Part Two](https://www.differentshelf.com/nx-should-make-code-graphs-cacheable/) asks whether Nx should provide the revision, project and cache contract for this missing resolution without turning its Project Graph into a general knowledge platform. The same authority test applies beyond source. Each graph should claim only what its evidence can support. ![Four layers of software evidence show different claims and boundaries: build graph, code graph, deployment and runtime, and human context. A monorepo lets the first two begin from the same source snapshot.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/software-evidence-layers-ds-2026.png) Neither static graph can tell us which version is serving traffic now. Contracts and infrastructure describe intended interfaces and topology. Deployment systems describe what was deployed; telemetry describes what was observed during a stated time window. People describe purpose, ownership and risk. A central service can still join and present those views, and it may own curated facts such as purpose and criticality. Backstage draws much the same boundary: its catalogue is meant to capture [human mental models rather than an exhaustive inventory](https://backstage.io/docs/features/software-catalog/creating-the-catalog-graph/?ref=differentshelf.com), and its FAQ warns that [cataloguing every dependency is not a good fit](https://backstage.io/docs/features/software-catalog/faq/?ref=differentshelf.com). Its job becomes smaller and more honest: connect facts owned elsewhere without copying their authority. That division leaves people to maintain the facts that require judgement. Ownership, purpose and criticality qualify. Project dependencies, toolchains and build commands usually do not; branch rules belong in source-controlled policy that continuous integration can enforce. If a machine can derive or validate a fact during normal development, asking a person to maintain a second copy is not governance. It is hope with a form attached. Even so, authority is not certainty. A parser can misunderstand the same convention perfectly every week. Static analysis can omit a runtime dependency. A grounded language model can add an unsupported claim. Calculate what can be calculated, query what can be queried, and generate prose only where explanation helps. ## Build Outwards From the Monorepo The same discipline applies to repository scope. One monorepo need not contain the whole company. Security, legal access, lifecycle and genuine product boundaries remain good reasons to separate code. At very large scale, source control, build performance and merge throughput become platform problems of their own. Uber shows why that boundary matters. Its core code lives in a few language-oriented monorepos. In an analysis of 500,000 changes to its Go monorepo, [1.4 per cent affected more than 100 services and 0.3 per cent affected more than 1,000](https://www.uber.com/us/en/blog/controlling-the-rollout-of-large-scale-monorepo-changes/?ref=differentshelf.com). Uber built cross-service rollout orchestration to contain the risk. A coherent graph can reveal a blast radius. It can also give you one. The sensible unit of consolidation is not the company. It is the boundary inside which code frequently needs to change and be validated together. A connected Java estate might form one monorepo. A product spanning Angular, .NET and shared contracts might form another. The objective is not to win a competition for the fewest repositories. That suggests a practical starting point. Take a strongly related slice of the estate and make its operational build graph the source-and-build foundation. Test it against raw source; record what it answers, misses and costs. Require every additional graph to name the question it answers, the authority it consumes and the cost of keeping it current. Then add deployment, telemetry and human context for facts that exist only there. The map room is valuable. It works best when the books that change together already share a shelf, and the shelf updates the map whenever they move. ### Sweating the Bar and Raising the Details URL: https://www.differentshelf.com/sweating-the-bar-and-raising-the-details/ Last updated: 2026-08-24T16:18:16.000Z *Andy Grove designed meetings for an older information system. We kept the meetings after replacing the system.* “We need to sweat the details on raising the bar,” somebody said in a meeting. The explanation that followed was sensible. The sentence was not. It contained two exhausted metaphors, no detail and no bar. Rearranged as “we need to sweat the bar and raise the details,” it lost surprisingly little. This is a useful test of corporate language. If its phrases can be rearranged without changing what the listener knows or does, the sentence carried mood, not information. The mood is familiar. We are serious. We have standards. Further purposeful activity will occur. What remains unclear is which standard, what must change, who will change it and how anybody will know when the bar has completed its ascent. Poor corporate English does more than offend the ear. It transfers the work of interpretation to everybody downstream. This is not another complaint that a meeting could have been an email. Much of modern management was built when information was difficult to distribute and preserve. That cost has collapsed, but the organisation built around it remains. When leaders do not write clearly, meetings and managers become the missing transport and interpretation layer. Better writing removes coordination work. Information can travel without people carrying and repeatedly explaining it. ## English as a Coordination Problem A vague managerial instruction creates a small queue downstream. Someone asks what “raising the bar” means. A meeting aligns on the ambition, a presentation socialises it and managers cascade it. A working group eventually defines the details. Weeks later, the organisation has produced abundant communication from a sentence containing no decision. Clear writing removes much of this machinery. “We need to improve quality” sounds unobjectionable because it makes no choice. “From October, releases with unresolved critical accessibility failures will not ship” is useful because people can dispute it. They can challenge the threshold, the date, the owner or the cost. The sentence exposes the decision while there is still time to change it. Precise writing need not command agreement. It must make disagreement possible before the organisation acts. Corporate euphemism avoids the discomfort. Owners become passive verbs, decisions become aspirations and problems become opportunities. Aggression performs the same trick by making tone the subject. Professional writing needs neither. Be brutal with the sentence and careful with the person. “There may be an opportunity to strengthen accountability” conceals the problem. “You have failed to take ownership” makes the person the problem. “Security owns this control; the current implementation does not meet the agreed requirement” identifies the gap and leaves motives alone. Plain English does not avoid difficult meaning. It delivers it without fog or humiliation. This is not a test of literary polish or native English. An imperfect sentence with a clear owner, action and reason is more useful than immaculate corporate jargon. That burden of interpretation helps explain why meetings became so central to management in the first place. ## The Meeting Was Also a Distribution System In [\*High Output Management\*, published in 1983](https://books.google.com/books/about/High%5Foutput%5Fmanagement.html?id=HersAAAAMAAJ&ref=differentshelf.com), Andy Grove treated meetings as part of the production system of management. Regular one-to-ones, staff meetings and operational reviews exchanged information, surfaced problems and supported teaching. Mission-oriented meetings addressed a specific problem and usually produced a decision. Grove demanded preparation, a defined purpose and only the people required. He also wrote for an organisation in which information was harder to distribute, preserve and recover. Meetings carried context as well as interpreting it. They were part of the communications infrastructure. We now have searchable internal channels, shared documents, versioned repositories, operational data and recorded decisions. One person can publish an authoritative explanation to several thousand colleagues at negligible distribution cost. Others can challenge it without sharing a calendar, and somebody joining six months later can recover the reasoning. I have argued that [managers should not be used as distribution lists](https://www.differentshelf.com/your-managers-are-not-a-distribution-list/). The same distinction applies here. Information no longer needs a room merely to travel, but some problems still need people to interact. When reasoning is published once, people can recover the context themselves. Managers relay fewer messages, specialists repeat fewer explanations and decisions need not be reconstructed from memory. This is organisational design, not etiquette. We retained the recurring meeting even after much of its information-distribution work had disappeared. ## When the Purpose Is Information, Write It Writing and meetings do different work. Use each for the part it does best. Write to distribute information. Meet when interaction can improve the understanding, relationship or outcome. I was recently invited, with more than twenty other people, to a demonstration of an internal AI tool. The invitation included no product page, documentation or link. The tool was ready to demonstrate; it was surely ready to explain. The meeting had a budget, although nobody had written it down. With more than twenty senior people invited, it had committed several thousand pounds before anybody acted on what they learned. That excludes preparation and the cost of abandoning whatever everyone had been doing beforehand. Preparing the audience was the cheapest part of the meeting, and the only part omitted. Share it beforehand. Let people understand the problem, inspect the claims and arrive with useful questions. A software vendor would consider this elementary. Writing would not replace the demonstration. It would allow the demonstration to begin at the useful part. The same preparation improves decision meetings. A disciplined paper states the problem, evidence, options, recommendation and consequences. Others can examine the reasoning before the meeting begins. Often writing reveals that the parties were answering different questions. Sometimes the decision becomes obvious and the meeting disappears with it. Meet when people need to explore uncertainty, resolve disagreement, teach, build trust, handle a sensitive subject or respond to an incident. These are interactions, not distribution tasks. ## Four Rules for Management Writing Choosing writing as the medium solves nothing if the document merely preserves the fog. Clear management writing is not merely a matter of style. Its job is to make ownership, decisions and consequences visible. Four of [Strunk’s rules](https://www.gutenberg.org/cache/epub/37134/pg37134-images.html?ref=differentshelf.com) are particularly useful: - **Use active voice.** Name the person or function responsible. - **Use definite, specific, concrete language.** Make the proposed change inspectable and disagreement possible. - **Omit needless words.** Reduce the space in which ambiguity can hide. - **Put statements in positive form.** Say what will happen. “We need to drive greater alignment around engineering excellence” has no owner, action or test. “Engineering Directors will publish the three required release checks by Friday” may be a terrible decision. At least it is a decision. These rules do not require every executive to sound like an instruction manual. Difficult subjects need qualification; unresolved problems deserve honest uncertainty. The meaning must still survive the sentence. ## The First Draft Is Now Cheap These rules require effort. That is part of their value. The author must organise the thought before asking others to spend time on it. A meeting often feels easier. Ten people can be gathered before the organiser has worked out what needs deciding. This saves the organiser time by taking it from the other nine. AI changes that bargain. It can turn notes into an argument and expose the holes. It can also inflate six vague words into two pages of confident nonsense. Fluency proves only that somebody has access to a model. I increasingly regard a leader’s repeated refusal to put consequential reasoning into durable form as organisational laziness. The work of ordering the thought passes to everybody forced to reconstruct it through meetings, questions and repetition. Use AI to get past the blank page if it helps, and mark exploratory drafts honestly. Before presenting a document as authoritative, edit without mercy: remove claims you cannot defend, words that add no meaning and conclusions that do not follow. AI has made drafting cheap. The writer still owes the reader a finished thought. ## Ask What Changes Before sending a memo, email or presentation, ask what should change after somebody reads it. Before arranging a meeting, ask what live interaction will add that writing cannot. Before requesting status, ask which decision the information will change. Before asking managers to cascade a message, ask why the owner cannot publish it directly. Grove treated management as a production process. Keep his discipline; reconsider the machinery. Publish information, expose reasoning to challenge and meet when interaction adds something writing cannot. Record any decision or durable learning where others can find it. And if the instruction is still to sweat the details on raising the bar, start by naming the details and locating the bar. ### Your Managers Are Not a Distribution List URL: https://www.differentshelf.com/your-managers-are-not-a-distribution-list/ Last updated: 2026-08-24T16:18:16.000Z *If a rule governs thousands of people, its owner should not whisper it through the organisation chart.* Consider a common failure, generalised from experience. In a large organisation, urgent work was delayed because a team waited for a control that the policy did not require. The case was resolved, the policy clarified and the lesson considered important enough to share widely. It was sent to a few dozen senior people with an instruction to cascade it through the organisation. Thousands of people needed the authoritative answer. This is how a modern company communicates when it has mistaken hierarchy for infrastructure. The policy owner tells a group of leaders, who tell their leaders, who tell their managers, who mention it during a weekly meeting to whichever employees have not taken leave, changed role or lost consciousness. By the time the message reaches the person doing the work, it has acquired local interpretation, lost inconvenient detail and become impossible to trace to its author. If it reaches them at all. ![Two communication paths. A cascade turns one decision into three successive copies; authoritative publication gives managers, engineers and new colleagues direct access to one owned decision record.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/cascade-versus-publication-ds-2026.png) A cascade does not distribute a message. It distributes responsibility for whether the message arrives. The alternative is not louder communication. The owner should publish one authoritative message where the affected audience can find it, usually without tagging anybody. Rules used repeatedly should be enforced or presented in the workflow wherever possible. Managers add local context where it helps. Publish broadly enough to establish one truth. Notify narrowly enough to protect attention. ## Why Cascades Fail The cascade made sense when communication was scarce. A chief executive could not address ten thousand people directly, so instructions travelled through the chain of command. The organisation chart was the communications network. Large companies now have distribution lists, announcement channels, searchable intranets and systems that can target the people affected by a rule. The owner can write once, publish to the right audience and answer questions at the source. Corrections remain attached to the original. New colleagues can find the decision later. Nobody has to reconstruct official policy from a screenshot of something their manager remembers being told on a call. Yet we continue to cascade through several layers. Nobody can say with confidence whether the final audience received the message, understood it or can find it later. The policy owner knows who received the first version, not who received the last or what it said when it arrived. Owning a policy means ensuring that the people governed by it can find, understand and apply it. Sending it to one's peers with an instruction to pass it on retains authority while outsourcing delivery. We would not accept a message bus in which every node rewrote the payload and delivery could not be observed. Nor would we declare an incident fixed because the first service had acknowledged it. In corporate communication, this architecture is called alignment. A [field study of enterprise social networking](https://doi.org/10.1287/isre.2014.0536?ref=differentshelf.com) suggests that visible conversations can help colleagues discover who knows what, learn from exchanges they did not join and avoid duplicating work. A public exchange leaves useful knowledge. A cascade leaves only the last retelling. ## Direct Does Not Mean Loud I have argued before that [shouting in the digital office](https://www.differentshelf.com/shouting-in-the-digital-office/) destroys attention. The answer to a bad cascade is not an indiscriminate `@channel`, or its nervous cousin `@here`. Direct communication means publishing at the source, not interrupting everybody at once. The owner should post in the channel used by the affected audience, say who is affected, explain what changed and link to the authoritative detail. People can read it when convenient unless they must act now. Use `@channel` only when every member of the channel needs timely information or action. Use `@here` when an immediate response is needed from somebody active now, most plausibly during an operational incident. Neither tag makes an ordinary announcement more important; it merely makes the interruption harder to avoid. For everything else, publish the message and leave the siren alone. People sometimes forget to post directly, do not know which channel to use or feel uneasy addressing thousands of colleagues. A large audience makes every imperfect sentence feel permanent. Give owners clear announcement channels, sensible guidance and help with consequential messages. Do not replace one nervous author with several layers of unreliable couriers. Questions whose answers would help others belong beside the original message. Managers may explain how the rule applies locally or notice that somebody has misunderstood it. That is useful leadership, not a substitute for direct publication. The owner publishes the rule. The system preserves the authoritative version. Managers explain local consequences and handle exceptions. Context enriches the source; it does not replace it. ## A Channel Must Deserve Attention Direct publication fails if the chosen channel has become a corporate attic. Once every initiative, survey, celebration and amended expense policy is declared important to everybody, nothing in the channel is important to anybody. People mute it, skim it or rely on colleagues to tell them when something real has happened. We have recreated the cascade as a coping mechanism. This is the serious counterargument to publishing broadly. A [survey of enterprise social-media users](https://doi.org/10.1371/journal.pone.0264726?ref=differentshelf.com) found that information overload was associated with strain, and that greater visibility strengthened the relationship. A [workplace study of notifications](https://www.microsoft.com/en-us/research/publication/notifications-and-awareness-a-field-study-of-alert-usage-and-preferences/?ref=differentshelf.com) found the trade-off in miniature: alerts create awareness but disrupt the task already under way. The answer is editorial discipline. Choose the smallest audience that includes everyone governed by the message. Use a channel with a clear purpose. Keep the post short, link to durable detail and state whether action is required. Do not use an engineering announcement channel for executive tourism, motivational wallpaper or news that belongs in the canteen. Publish to the smallest complete audience. Notify only the people who must act now. A useful channel earns attention by refusing most of what people would like to put in it. If leaders fill it with material that does not concern its audience, they cannot complain when the one consequential message disappears into the upholstery. ## Put the Rule Where the Work Happens There is a more important question than who should send the correction: why did thousands of people need to remember the rule during urgent work? Where a rule can be enforced safely, put it in the workflow. If an emergency change does not require an approval, the emergency process should not request one. If a repeatable security check is mandatory, the tooling should run it. Where judgement remains necessary, show the current rule, its owner and the route to an exception at the moment of need. The workflow should enforce what is mechanical and present what requires judgement. It must agree with the written policy, or the company will possess one rule in prose and another in software. An announcement corrects what people know. A well-designed workflow changes what happens next. If a rule matters when pressure is highest, enforce or show it in the workflow before asking everybody to remember it. Establish the authoritative rule, then correct the affected audience at once if people might otherwise continue doing the wrong thing. Reflect the rule at the point of work, publish the completed change and notify only those who must act now. Managers can then reinforce it where local context helps. Most organisations begin with the reinforcement and wonder why the incident repeats. ## Leaders Must Own the Words The duty to make consequential reasoning discoverable grows with seniority. A Principal Engineer's explanation may guide several teams; an executive policy may govern thousands. The wider the effect, the less defensible it is to leave the reasoning inside a private meeting and appoint the hierarchy as courier. Writing reveals whether an instruction can stand without its author in the room. A vague proposal can survive a meeting because everybody leaves with a different interpretation and calls the gathering a success. Writing makes the owner choose the words and confront the exceptions. This is one reason meetings and cascades remain popular. One can arrive at a meeting without having decided exactly what one means. A written announcement is less accommodating. It leaves evidence. Some subjects belong in private: personal circumstances, individual performance, confidential investigations and information restricted for a real reason. A rule governing how thousands of engineers should perform their work does not. ## Stop Cascading and Start Owning The cascade persists because it flatters the organisation chart. Every layer receives something to repeat, while the original owner can believe the message is moving without discovering whether it arrived. Publish the authoritative message to the people it governs. Enforce or show repeatable rules in the workflow. Use managers for context and judgement, not distribution. Own the rule, the words, the audience and the mechanism by which it becomes true. Please do not cascade this message. ### Smaller Delivery Units, Wider Management Spans URL: https://www.differentshelf.com/smaller-delivery-units-wider-management-spans/ Last updated: 2026-08-24T16:18:16.000Z *A short manifesto for making the engineering organisation follow the work.* This manifesto brings together three arguments developed in longer essays: AI is changing [what engineers need from managers](https://www.differentshelf.com/your-manager-has-six-reports-because-management-stopped-thinking-in-1933/), [the product development team need not remain the smallest unit of delivery](https://www.differentshelf.com/the-product-development-team-is-no-longer-the-smallest-unit-of-delivery/), and [engineering judgement matters more as implementation becomes abundant](https://www.differentshelf.com/the-code-is-no-longer-the-scarce-part/). The essays contain the evidence and qualifications. This is the operating model they imply. The smallest safe delivery unit and the right management span answer different questions about different work. AI helps engineers cross technical boundaries and deliver end-to-end changes with fewer compulsory handoffs. Our position is that a bounded engineering outcome should be carried by the smallest unit able to deliver it safely, sometimes one engineer, while responsibility for operating and changing the result remains collective. Management spans can widen only where this delivery model and its supporting systems remove routine coordination from the manager. AI does not manufacture engineering judgement. It expands the implementation surface over which existing judgement can act. Scope must still follow demonstrated judgement, risk and available support. Routing unfamiliar work through managers, permanent teams or assigned specialists can create avoidable delay and teach engineers to wait for help they do not need. The objective is safe, direct action with support when needed. ## 1\. AI extends reach, not judgement AI can produce substantial implementations and plausible design options. The engineer must decide what belongs in the system, identify unsafe assumptions, test the design against evidence and answer for what reaches production. Recognise senior engineers for the architecture and consequential decisions that keep systems coherent, not for code volume or line management responsibility. Technical judgement should carry status, pay and authority without requiring reports. The engineer carrying the outcome decides within agreed boundaries; Principal Engineers and specialists challenge consequential choices rather than approve every step. Scope should follow demonstrated judgement. A less experienced engineer may carry a small, reversible change; a senior engineer may carry work across several systems with greater architectural or operational consequence. Either may be a delivery unit of one. The breadth, consequence and support differ; the standard of accountability does not. ## 2\. Delivery units can become smaller The stable Product, Design and Engineering community has often been both the boundary of ownership and the default unit of delivery. The boundaries need not remain identical. The product bet remains shared. Product leads prioritisation and evidence of customer value; Engineering leads delivery and operability. Design and other disciplines contribute where the outcome requires them. The idea may originate anywhere, and participation should follow uncertainty and consequence, not a fixed staffing pattern. One engineer or a temporary pairing may carry a bounded engineering outcome from agreed intent to production. The community retains operational responsibility and long-term stewardship. An outcome is complete only when it operates safely and another qualified engineer can understand, change and support it. This applies chiefly to product development. Platforms, site reliability and systems requiring continuous ownership still need stable stewardship teams, even where individuals carry bounded changes. ## 3\. Management spans can widen Delivery-unit size and management span answer different questions. A manager can support more engineers when ordinary delivery no longer depends on them allocating work, resolving routine dependencies or supplying all technical guidance. Ask what must come from the line manager. Performance, reward, promotion, fairness, development, conflict and private support require clear employment accountability. Principal Engineers and code-owning communities can provide technical direction, Product can provide product direction, engineers can coordinate delivery, and peers can mentor and give feedback. These contributions inform people decisions without creating shadow managers. The manager protects organisational health and handles exceptions rather than routing routine work. Managers remain technically active, but delivery must not depend on their individual output. Spans widen only after routine coordination and normal delivery commitments leave the role. Adding reports to an unchanged job is overload. A span can widen only after routine coordination leaves the manager's role. One manager for every six engineers is not a rule to replace with another fixed ratio. The right span follows the work that remains uniquely managerial and the attention people need. It can widen only while engineers act within agreed boundaries without routine managerial permission, managers notice who needs attention and private access remains easy. When those conditions fail, remove work from the manager or narrow the span. ## What the model requires ### Remove routine dependencies - **Work in public unless there is a reason it cannot.** Put intended outcomes, work in progress, questions, decisions and reasoning in shared internal channels and repositories, not private messages or one-to-one meetings. This invites contribution while change is cheap and makes knowledge reusable. Keep personal, sensitive and protected matters private. - **Make routine delivery self-service.** Platforms, documentation, fast tests, observability and deployment paths should let engineers investigate, change, release and diagnose systems without seeking permission or favours. Fast feedback lets engineers test small, reversible decisions instead of escalating them in advance. - **Make inner sourcing the default.** Let engineers contribute across codebase boundaries through documented standards, clear shared ownership and agreed service levels for review and, where the standards are met, acceptance. Ownership should follow demonstrated knowledge, and no repository should depend on one person. Code owners protect coherence and operability; sufficient review capacity must prevent ownership becoming a new queue. ### Preserve coherence and control - **Coordinate across outcomes without recreating the squad.** Product leadership limits simultaneous bets and preserves a coherent customer experience. Technical leaders and code-owning communities expose adjacent changes and protect consistent behaviour across systems. When outcomes collide, the people involved coordinate directly; possible future interaction does not justify a permanent delivery unit. - **Automate routine controls and concentrate human judgement.** Tooling should enforce syntax, style, conventions and repeatable security checks on every merge request. Code review remains mandatory, but human reviewers should focus on correctness, architecture, behaviour, operability, customer impact and risks that tools cannot settle. A Principal Engineer should challenge cross-cutting architectural choices; consequential or hard-to-reverse risks require independent specialist review. Engineers remain accountable for AI-assisted work. - **Keep shared expertise accessible, not centralised.** Principal Engineers, Security and other specialists should publish reusable reasoning and encode repeatable decisions into platforms and standards. They intervene when their judgement is needed, not as universal gates or hidden queues. ### Sustain capability and management support - **Develop capability deliberately.** Pairing, sponsorship, progressively larger outcomes and expert support must build judgement and remain fairly available. A unit of one must not mean learning alone. - **Use attention according to need.** Coaching and feedback continue through the work, with reliable private access to the manager. Formal career and performance conversations remain at least quarterly, staggered through the year rather than compressed into organisation-wide windows. They do not replace continuing contact. ## The organisational consequence The organisation chart should follow the work. Use the smallest unit that can safely carry a bounded engineering outcome, given the judgement demonstrated, the risk involved and the support available, while leaving responsibility for operating and changing the result with the product community. Where that delivery model, together with strong platforms, visible work, automated controls and accessible expertise, removes routine coordination from the manager, management spans can widen. Wider spans may mean fewer dedicated line-management roles. That is a consequence, not the objective. This changes the number and nature of those roles and frees capacity for engineering and other valuable work. The objective is autonomy proportionate to demonstrated judgement and risk, with support available when needed, not its withdrawal. This is not a new universal structure or another sacred ratio. Begin with the work. Reduce the handoffs it no longer needs, preserve the support and challenge it still does, and let the organisation chart record the result. The topology becomes fluid at delivery and stable at accountability. ### The Code Is No Longer the Scarce Part URL: https://www.differentshelf.com/the-code-is-no-longer-the-scarce-part/ Last updated: 2026-08-24T16:18:16.000Z *AI can generate an implementation and may even propose the strategy. The better its answers become, the more we need engineers who can recognise what belongs in the system, detect what is wrong and remain accountable for what happens next.* --- One application rebuild has now supplied me with three articles, which is the sort of productivity gain executives understand. The first was about [management spans](https://www.differentshelf.com/your-manager-has-six-reports-because-management-stopped-thinking-in-1933/). The second was about [delivery units](https://www.differentshelf.com/the-product-development-team-is-no-longer-the-smallest-unit-of-delivery/). This third is about what the engineer contributed while AI helped produce much of the implementation. The answer is not mainly code. It is the chain of ideas holding the work together. The engineer devised an architecture that adapts to its environment, separated failure domains, defined rules that must hold across several systems, replaced timing guesses with explicit contracts and discarded designs that evidence disproved. That is the distinction. Plausible implementation can now be generated in extraordinary volume. Coherent, trustworthy change remains scarce. Current frontier models can feel close to magic, and so did their predecessors. Give an agent the objective, repository, documentation, tools, test environments, a knowledge graph and every context-engineering technique we currently know. It can explain the system, propose designs and implement them. It cannot reliably identify the hidden contradiction that matters, build a coherent architecture around it and revise that architecture as months of runtime evidence expose its faults. Novel here does not mean unprecedented in computer science. It means discovering which arrangement works within the constraints of an existing system. A model can propose unfamiliar arrangements. It cannot reliably judge which fit the system, which belong together or which evidence should overturn the design. More context improves access to facts. It does not manufacture judgement. The software industry is arguing about the wrong thing. We ask whether AI can write code as though code were the whole contribution and the engineer merely the person previously required to type it. If AI produces more of the implementation, we assume engineering expertise must become less valuable. The opposite may be happening. AI did not replace expertise. It gave expertise a larger surface area. ## There Is No Incantation for Expertise A fair amount of prompt engineering consists of telling the machine what you want with sufficient clarity, an activity formerly known as writing a decent specification. Clear instructions, relevant context, examples, constraints and acceptance criteria improve the result. Building retrieval systems, toolchains and evaluations around a model can involve serious engineering. Choosing the right context may itself require deep knowledge of the system. None of this supplies judgement the operator does not possess. Fluency is not expertise. My ten-year-old has an excellent command of English, studies Latin and earns high marks for grammar. She can express herself to a model perfectly well. She cannot use one to reason seriously about a distributed system, much less produce a world-class implementation. She lacks neither vocabulary nor intelligence. She lacks the understanding of distributed systems needed to test the machine's answers. The same is true of adults. A beautifully written prompt can ask a model to design an architecture. It cannot tell its author whether the architecture makes sense. No incantation involving "act as a distinguished engineer" supplies the experience required to recognise that the first plausible answer is wrong. Sean Goedecke recently argued that [LLMs reward expertise](https://www.seangoedecke.com/llms-reward-expertise/?ref=differentshelf.com). His example is the mathematician Terence Tao using ChatGPT to reason about a difficult problem. Tao's prompts are short. What distinguishes his use of the model is knowing which part of an answer matters, when the proposed route is needlessly complicated and which alternative might unlock it. An expert can say, "That looks wrong," before being able to prove why. They can ask whether the system already contains a simpler mechanism, recognise that a proposed abstraction fights the architecture or notice that a locally elegant answer creates trouble three layers away. The expert does not know a secret way to speak to the machine. The expert knows when the machine is talking nonsense. A novice and an expert may use the same AI model, but they will not extract the same value from it. The tool may make both more capable, but it gives the expert a longer lever. Someone who understands the system can reject plausible nonsense and direct several strands of implementation towards one coherent design. Someone without that understanding can generate incoherence just as quickly. ## Control the Ideas, but Do Not Pretend the Code Has Vanished Where, then, should that expertise be applied? Salvatore Sanfilippo, the creator of Redis, goes further. In [Control the ideas, not the code](https://antirez.com/news/169?ref=differentshelf.com), he argues that line-by-line inspection becomes a poor use of expert attention when AI models can generate thousands of lines a day. The engineer should control the design, tests and governing ideas of the software rather than treat source code as its principal intellectual output. He is right about the direction and too absolute about the destination. Code remains the thing the machine executes. Tests sample behaviour; they do not prove every property that matters. Generated implementations can encode subtle security failures, mishandle concurrency or satisfy the test suite while violating an assumption nobody wrote down. In systems handling money, identity or regulated decisions, refusing to inspect consequential code would be a failure of accountability. But reading every generated line with equal attention will not scale either. Expert attention must move towards architecture, critical rules, interfaces, failure modes, evidence and the code paths and interactions where danger lives. The question changes from "Did a human type this?" to "Does a human understand and own the ideas this software expresses?" That is a harder standard. ## Documentation Becomes Part of the Control System That understanding must exist somewhere beyond the engineer's head. Software documentation often describes a system after the consequential decisions have been made, which is why so much of it resembles an archaeological note left by somebody who arrived after the civilisation disappeared. AI makes a first draft cheap, but cheap documentation is not necessarily useful. The valuable document records the engineer's understanding: the rule that must hold, the evidence that changed a decision, the alternative that failed, the trade-off accepted and the condition under which the design should be removed. In the work I have watched, the records form part of the control system. They state rules that code and tests enforce, record evidence that corrected the architecture and define rollout tripwires before production makes somebody wish they had. These records make the engineer's ideas available to other humans and give future AI systems better context. They are valuable only while somebody owns them, tests their claims against the implementation and corrects them when reality changes. Producing prose is cheap. Maintaining truth is not. That creates a useful discipline. If nobody can state the architecture, critical rules and accepted risks clearly, perhaps nobody controls the ideas after all. ## Expertise Includes Changing Your Mind The record of the work also contains reversals. Assumptions about runtime behaviour were checked against source and real environments, then corrected. Designs were discarded when they behaved incorrectly, consumed too many resources or obscured ownership. Some apparently clever mechanisms were deleted once a simpler rule became visible. Organisations often confuse seniority with having the answer in the first meeting. That rewards confidence and makes correction look like weakness. Real engineering judgement is often the ability to construct a test that could prove your preferred idea wrong, notice when it has done so and change direction without preserving the old mistake for reasons of status. AI reduces the cost of exploring alternatives. It can construct competing implementations, attack an assumption and help trace a failure while the context is fresh. Engineers should become more willing to be wrong briefly and quicker to correct themselves. The scarce skill is not certainty. It is maintaining a coherent understanding while evidence changes it. ## What Senior Engineers Produce Lines of code were always a poor measure of engineering contribution. They become absurd when one engineer can direct a model to produce thousands and another can remove them by finding the idea that makes them unnecessary. A senior engineer increasingly produces: - an explanation of the system's important behaviour; - rules and boundaries that keep implementations coherent; - tests and observations that can disprove the design; - records that preserve the reasons behind important decisions; - simplicity recovered from generated complexity. Code remains evidence of the work. It is no longer a reliable measure of its value. This should change how we recognise technical leadership. The engineer exercising the most valuable judgement may not write the most code, review the most pull requests or dominate architecture meetings. They may define the rule that prevents fifty future decisions, write the test that kills an attractive mistake or explain the system well enough that ten other engineers can change it safely. None of that requires reports. ## Scope Should Follow Judgement Scope should follow demonstrated judgement, risk and available support. A junior engineer can still be a unit of delivery, but should carry a smaller, more reversible outcome with close review and deliberate pairing. As judgement grows, so can the breadth and consequence of the work. The standard of accountability does not change. New engineers still need to build things, debug failures, predict behaviour and discover where their understanding differs from reality. Watching an AI model solve the problem can create recognition without understanding. The organisation must protect that learning. If every important outcome goes to the person who can finish it fastest, this quarter's throughput will consume next decade's expertise. The objective is not to preserve typing as a rite of passage. It is to develop judgement through progressively larger work. ## Reward Judgement as Leadership This account of expertise supplies the mechanism beneath both earlier arguments. Together they imply [an operating model of smaller delivery units and wider management spans](https://www.differentshelf.com/smaller-delivery-units-wider-management-spans/). An expert engineer can now exercise judgement across work once divided among several people: their understanding supplies coherence while AI accelerates implementation across the system. Principal Engineers can challenge decisions across many outcomes without implementing each one. Managers can support more engineers when visible work, shared standards and greater individual reach allow ordinary technical decisions to proceed without being routed through the line manager. Greater leverage widens the organisation only when the engineer leaves behind reasoning others can inspect and challenge, and software others can operate and change. Otherwise AI has not removed a dependency. It has made one expert a faster and more dangerous one. Technical judgement should carry status, pay and authority without requiring reports. Otherwise the people best equipped to control the ideas will be rewarded for leaving them behind. An AI model given enough context can also propose a plausible product or technology strategy. Better models can make bad judgement harder to detect because they argue the case more convincingly. In a large regulated company, an accountable expert must still test the proposal against regulation, capital, customers, operational risk and reputation, then answer for the decision. Accountability returns to the implementation. Often the person with the clearest theory should implement it; sometimes their role requires others to carry much of the work. Seniority changes how work is divided, not who must answer for the evidence, implementation risks and production consequences. Architecture does not become sound because its author is senior. If it ignores users, contradicts operational evidence or collapses under qualified review, it is simply bad architecture. Current AI models can multiply implementation. The engineer must still decide what a complicated existing system should become and recognise when evidence invalidates the design. AI does not diminish that judgement. It gives it more territory. ### The Product Development Team Is No Longer the Smallest Unit of Delivery URL: https://www.differentshelf.com/the-product-development-team-is-no-longer-the-smallest-unit-of-delivery/ Last updated: 2026-08-24T16:18:15.000Z *Team Topologies put the stable team at the heart of software delivery. AI is challenging whether the team must remain the smallest unit carrying every outcome.* --- In [the previous article](https://www.differentshelf.com/your-manager-has-six-reports-because-management-stopped-thinking-in-1933/), I described one engineer and one designer rebuilding an application end to end, work I would once have expected to require two teams or remain undone. That article asked what this implied for management span. This one asks what it implies for the product development team. The product bet is shared; one engineer is carrying the bounded engineering outcome. A senior product leader set the direction and constraints but left the solution open. Product and Design shaped the intervention and challenged it as it developed. The implementation crosses architecture, web, native, shared code, build, release, testing and operations. At the time of writing, the application is moving towards production. This is not a toy, a weekend experiment or a narrow feature. It is consequential work crossing enough boundaries that the organisation might previously have staffed a team around it, or declined to begin. AI did not supply the architecture. The engineer defined the rules the system must preserve, tested the designs and documented the failures. What matters is how much implementation that existing judgement could direct. He could probably have done this before AI. It might have taken so long that the organisation would never have chosen to begin. AI changed the work from theoretically possible to economically credible. It allowed him to direct implementation across a much larger part of the system: investigating native APIs, understanding unfamiliar build systems, constructing tests, diagnosing failures and crossing parts of the stack that would recently have required another person or another week. If you want to go fast, go alone. If you want to go far, go together. AI is changing how far one engineer can go. Together has not disappeared. It has moved from permanent staffing to selective, proportionate collaboration. Reviewers, platform teams, security specialists and engineers with collective ownership will help take the application into production and operate it. Product, Design and Engineering must still learn whether the bet improved customer behaviour or commercial results. Construction alone proves neither delivery nor value. Not every engineer could carry an outcome of this breadth. It is distinguished-engineer-scale work. A less experienced engineer should carry a smaller, more reversible outcome, with closer challenge and more opportunities to pair and observe. Scope should follow demonstrated judgement, risk and available support; seniority need not determine the size of the delivery unit. Nor is this a model built on heroes. Heroics depend on exceptional effort, private knowledge and somebody refusing to sleep. Individual delivery should depend on understandable architecture, self-service platforms, fast tests, visible decisions and accessible help. The engineer's demonstrated judgement, the risk and the available support determine whether carrying it alone is responsible. This argument concerns product development, not every kind of engineering. Platforms, infrastructure, site reliability and complicated subsystems requiring scarce specialist knowledge involve continuous operational responsibility, parallel work and incident response. Stable stewardship teams remain sensible there, even when individuals carry bounded changes within them. One unfinished example does not establish an operating model anywhere. It does expose a possibility worth taking seriously: a team may no longer be the smallest unit capable of carrying a bounded engineering outcome. ## The Best Available Baseline Team Topologies solved an important problem: how to arrange teams so that software could flow without drowning in handovers, dependencies and cognitive load. Matthew Skelton and Manuel Pais made a stable, autonomous team the fundamental means of delivery, aligned it to a stream of value and supported it with platforms, enabling teams and complicated-subsystem teams. This was a considerable advance on arranging engineers by discipline and passing work between them like luggage through an unreliable airport. Fast flow remains the objective, cognitive load remains a design constraint and Conway's Law has not been repealed by a chatbot. Nor has the model ignored AI. The [second edition, published in 2025](https://teamtopologies.com/book?ref=differentshelf.com), treats Team Topologies as evolutionary and addresses humans working with AI agents. In a [conversation with Thoughtworks](https://www.thoughtworks.com/en-au/insights/podcasts/technology-podcasts/organizational-design-team-topologies-ai?ref=differentshelf.com), Skelton and Pais consider teams covering larger domains or becoming somewhat smaller as AI reduces cognitive load. They conclude that the principles remain sound and the team remains the unit. They may be right about nearly all of it. Team Topologies is the best available baseline from which to ask the next question: must the team that owns a continuing stream of value also carry every bounded outcome within it? The word \*delivery\* is doing considerable work here. If it means durable stewardship of a product, including discovery, operation and continued evolution, the team remains a persuasive unit. If it means carrying a bounded engineering outcome from intent into working software, the unit can already be one person. Team Topologies usually joins those responsibilities inside one end-to-end team. AI gives us a reason to examine them separately. ## What Happens When the Pass Is No Longer Compulsory? Conventional product organisations built squads like football teams. Product, Design, frontend, backend, testing and infrastructure occupied positions, and work moved among them like a ball. Security was too often left in goal, expected to save whatever the formation let through. No player could carry the ball through every part of the pitch, so success depended on passing cleanly and keeping the side together. AI does not abolish the team. It turns some bounded engineering work from football into golf. One engineer can cross technical boundaries without passing the work at each one. Caddies, coaches, groundskeepers and officials still make play possible, but they do not take every shot. Execution becomes individual; product discovery, review, operation and value remain collective. An engineer can acquire temporary fluency in an unfamiliar system, interrogate code and documentation, trace behaviour, create tests and discard a bad approach before the old process has located the appropriate specialist. Expertise still matters. What changes is the number of people who must travel through the entire delivery process together. There is evidence for the mechanism, although not yet for the organisational conclusion. A [Google study of 32 developers](https://research.google/pubs/using-an-llm-to-help-with-code-understanding/?ref=differentshelf.com) found that an in-editor language model helped people complete code-understanding tasks more effectively than web search. Three field experiments involving 4,867 developers found [26 per cent more completed tasks](https://www.microsoft.com/en-us/research/publication/the-effects-of-generative-ai-on-high-skilled-work-evidence-from-three-field-experiments-with-software-developers/?ref=differentshelf.com) among developers given an AI coding assistant. The effects are not universal. A 2025 randomised trial found that [experienced open-source maintainers took 19 per cent longer](https://metr.org/blog/2025-07-10-early-2025-ai-experienced-os-dev-study/?ref=differentshelf.com) using early-2025 AI tools in repositories they knew well. No study proves that the product team should disappear. This is a prediction grounded in an observation: an individual can now carry more of the implementation before another human must join it. AI does not abolish cognitive load. The product's purpose, dangerous assumptions and consequences of failure must remain durable knowledge. Context needed only to cross a boundary, such as what a build file does or why a test is failing, can increasingly be acquired at the moment of need. The constraint becomes how reliably an engineer can acquire, verify and apply that context before collaboration would be cheaper or safer. Team Topologies anticipated part of this answer. Its interaction modes allow teams to collaborate temporarily, facilitate learning and provide capabilities as a service rather than permanently combining every specialist. Those patterns help one person carry more of an outcome. The question is whether they merely enlarge the stream-aligned team's reach or allow a smaller unit to carry each bounded outcome within it. ## The Pod Is the Squad on a Modest Diet Most product organisations still allocate work to a squad. Six or eight people share a backlog, attend the same planning rituals and have their delivery represented as a group. Individuals may carry separate pieces of work, but the squad remains the unit to which outcomes, capacity and progress are assigned. It appears on the org chart, in Jira and in the management hierarchy, which gives it the satisfying appearance of something discovered in nature. This machinery is not prescribed by Team Topologies. It is a familiar corporate operating model, with Scrum, line management and portfolio reporting contributing their own furniture. The distinction matters. Team Topologies offers a durable team boundary; organisations decide how much ceremony and hierarchy to build around it. AI is already making some squads look overstaffed for the outcomes assigned to them. Some organisations are slimming squads into pods. This is a sensible improvement. It is also the old topology in a smaller box. The pod acknowledges that AI changes how many people an outcome requires but stops short of asking whether stream alignment still requires a team. It contains fewer people while preserving the assumption that a permanent collection of humans must travel through delivery together. The pod is not harmless. Once delivery is reported only as the work of a group, it becomes harder to see where judgement, dependency and support sit. One person may make the difficult decisions while the group reports the result. The problem is not the group but collective reporting without explicit decision ownership. Shared accountability can produce collective ownership; it can also diffuse responsibility until an important outcome has no clear owner. This is not an argument for counting commits, tickets or lines of code. Knowledge work becomes absurd when reduced to individual output statistics. It is an argument for visible ownership. When one person explicitly carries an outcome and the work is public, everybody can see where judgement sits, where support is needed and whether the scope is appropriate. Responsibility has somewhere to land without turning contribution into a league table. The pod also preserves machinery invented to coordinate a group: backlog refinement, planning, sprints, stand-ups, demos and status reports. If one engineer carries the outcome, who exactly are these ceremonies coordinating? State the outcome, make the work public, review the change and publish the release note. Jira can remain an index; it need not become a daily diary proving that the engineer was busy on Tuesday. Unless that is the purpose, in which case we have left delivery management and entered a small police state with story points. ## Fifteen Engineers Without a Shared Sprint Now imagine fifteen product engineers, each capable of carrying a distinct outcome. Some work closely with Product and Design, some pair with another engineer and some briefly draw on security, data or architecture. They review one another's work, share product context and retain collective responsibility for the systems they change. They do not share a sprint merely because they share a manager. Fifteen is an illustration, not a recommended ratio or a demand for fifteen simultaneous bets. Product leadership must still choose where to invest, limit work in progress, sequence outcomes and preserve a coherent customer experience. The model depends on capable engineers, comprehensible systems, strong platforms and outcomes small enough to receive fast feedback. Where those conditions do not exist, a renamed org chart will not create them. Product and Design do not become ticket queues waiting for Engineering to summon them. Product leads prioritisation and evidence of customer value; Engineering leads delivery and operability. Design and other disciplines contribute where the outcome requires them. The idea may originate anywhere, and uncertain problems may require a sustained partnership among all three. Clearer interventions may leave one engineer considerable freedom. Participation should follow uncertainty and consequence, not a permanent pod. This is also where delivery topology meets management structure. A manager supporting fifteen engineers who are jointly delivering one thing faces a formidable coordination problem. A manager supporting fifteen engineers, each carrying a bounded outcome inside a strong product system, has a different job. They attend to development, performance, reward, fairness, conflict and private support rather than routing daily work among fifteen people. This is the delivery model underneath the wider-span argument. A wider span is not achieved by giving an Engineering Manager three squads and asking them to attend every ceremony. It becomes possible only when strong platforms, visible work, distributed technical leadership and collective ownership remove routine coordination from the manager. ## Individual Delivery Must Be Public Delivery Several outcomes will occasionally collide. The answer is not to assemble every engineer each morning in case today is the day. Use rough product and code boundaries, expose intended outcomes, record consequential decisions and make work in progress easy to find. Product leaders should know which bets are moving; engineers should know who is changing adjacent systems. When outcomes meet, the people involved can coordinate directly. Technical leaders can challenge a direction while it is cheap to change without approving every step before it begins. Crossing a codebase boundary should not require joining its permanent team. Engineers should be able to contribute through documented standards, shared code ownership and clear expectations for review and acceptance. Code owners protect coherence and operability; they should not become another gate or queue. AI compresses many changes into hours or days, which makes little-and-often delivery more important. When a decision is reversible, take it, observe the result and reverse it if necessary. The cost of occasionally undoing a small mistake may be lower than the cost of preventing every mistake in advance. Code review remains mandatory, but its purpose shifts upwards. Syntax, formatting and routine conventions should be enforced by tooling and automated checks. Human reviewers still require evidence of correctness and security, particularly when AI has helped an engineer cross unfamiliar territory. Their scarce attention should concentrate on correctness, architectural sanity, behaviour, operability, customer impact, dangerous assumptions and whether the change belongs in the system at all. That shift in expert attention is the subject of [The Code Is No Longer the Scarce Part](https://www.differentshelf.com/the-code-is-no-longer-the-scarce-part/). Routine, reversible changes can proceed with informed peer review. Cross-cutting architectural decisions should draw in a Principal Engineer. Changes affecting safety, security, regulation, customer funds or hard-to-recover data require deliberate design and independent specialist approval. Public work makes the risk visible; it does not waive the control. I currently follow every merge request in our monorepo. This is not a sustainable executive hobby and it does not make me reliably correct, but it is remarkably effective. I see what people are changing, notice patterns and occasionally connect engineers solving adjacent problems. The purpose is to understand the system, not score individuals. Following is not approving: work does not wait for me and intervention remains occasional. ## Keep the Community. Change the Delivery Unit. The stable product team has usually answered four questions: 1. Who discovers and chooses the product bet? 2. Who carries the engineering change? 3. Who challenges and reviews it? 4. Who owns the product and system afterwards? Putting all four inside one durable container was sensible when delivery required a standing collection of skills. It becomes less necessary when one person can carry more of the work. A community shapes the bet. One engineer carries the engineering outcome. Several challenge it. The community owns what follows. After a bounded outcome ships, there is no handoff to strangers. The engineer remains part of a durable product, design and code community with named product and operational responsibility. Code ownership, review history and operational participation should reflect demonstrated knowledge without reducing ownership to commit count or allowing it to remain private in one person's head. The work is not delivered merely because the code has merged. It must operate safely, produce evidence against the intended outcome and be changeable by another qualified engineer. Otherwise individual delivery has produced individual dependency. Engineers can remain co-located and belong to a product area, code community, management group and professional discipline without pretending they share one piece of delivery. In practice, the same organisation can be understood in four useful ways without creating four new departments: - **Outcome delivery:** one engineer, or a small temporary pairing, carrying a bounded engineering outcome into production. - **Product and code ownership:** durable communities responsible for customer context, product coherence and the systems beneath them. - **Management and disciplines:** line-management groups, Centres of Excellence and professional communities providing coaching, standards and career development. - **Shared expertise:** Principal Engineers, security specialists and other experts challenging and reviewing work wherever their judgement is needed. ![Four panels repeat the same six engineers: one or two carry a bounded outcome, a durable community owns product and code, management develops people, and shared specialists join only where consequence requires.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/four-organisational-views-ds-2026.png) Shared experts cannot become a hidden queue. Routine judgement must be encoded in platforms, standards and automated checks, leaving people to handle the decisions that genuinely require them. Otherwise fifteen individual outcomes will merely wait for the same exhausted Principal Engineer. Platforms make the model possible by making delivery, observability, infrastructure and routine security self-service. Public work keeps individual outcomes connected. Independent review supplies challenge. Collective ownership prevents the result becoming a single point of failure with a laptop. The topology becomes fluid at the point of delivery and stable at the point of accountability. This is why management spans can widen. Smaller delivery units and wider spans are not separate predictions; both follow from reducing the amount of ordinary work that must be routed through other people. The complete operating model is set out in [Smaller Delivery Units, Wider Management Spans](https://www.differentshelf.com/smaller-delivery-units-wider-management-spans/). Team Topologies still gives us a strong model for stewardship, platforms and specialist interaction. What must change is the assumption that the team owning the stream must carry every outcome within it. People will still collaborate, pair, review and teach. The squad or pod may not remain the smallest box through which every change must pass. Keep the team for work that needs a team. Stop requiring one for everything else. ### Your Manager Has Six Reports Because Management Stopped Thinking in 1933 URL: https://www.differentshelf.com/your-manager-has-six-reports-because-management-stopped-thinking-in-1933/ Last updated: 2026-08-24T16:18:15.000Z *Rod Liddle, the British columnist and former editor of BBC Radio 4's Today programme, died on 2 August 2026, aged 66\. He wrote with mischief, nerve and a magnificent lack of reverence for respectable opinion. He offended a great many people, often deliberately, and half the fun was waiting to see who would splutter next. This piece is a tribute to him. The tone is tongue-in-cheek; the argument is not.* --- One engineer and one designer are rebuilding an application end to end. A year ago, I would have expected two teams of six around the same outcome, not because twelve people were required to type the code, but because the work crossed enough boundaries to require them. More likely, the resulting cost and coordination would have killed the business case and the work would have remained undone. They are now doing it with some AI and almost no ceremony. There is not a Jira ticket in sight, so I have been unable to establish whether the work officially exists. It is magnificent. As VP Engineering, I shall eventually receive some credit for creating the conditions in which it happened, having contributed chiefly by not preventing it. What changed was not merely the speed of delivery. One engineer can now cross dependencies that once demanded a team. The org chart should probably notice. Meanwhile, elsewhere in the company, several highly paid adults are rearranging an organisation chart with the grave concentration of air traffic controllers. Unlike air traffic controllers, they can move every object on the screen without landing anything. The basic unit is reassuringly familiar: six engineers in a box and a manager above them. Accumulate enough boxes and we add managers to manage the managers, each layer farther from the work and more urgently in need of an update about it. Small teams work because six people can know what they own and notice when nobody owns it. Then comes the sleight of hand: because six people can build together, six must also be the number one manager can support. The number of people who should build together and the number one manager can support are different questions. The modern corporation answered both with six and went on an away day to discuss agility. AI is now ruining this pleasing symmetry: delivery units can shrink while management spans widen. So what replaces six? Nothing so tidy. My hypothesis is that mature product organisations can let bounded outcomes be carried by smaller units, sometimes one engineer, where demonstrated judgement, risk and available support allow it. A manager might then support fifteen or twenty, but only after ordinary coordination has left the role. The manager remains accountable for performance, reward, fairness and whether the place is quietly coming apart; technical leadership, coaching and ordinary coordination need not all pass through them. In a weak system, six may remain quite enough. ## The Strange Afterlife of the Number Six The number has a pedigree, which saves everybody the inconvenience of asking whether it was ever true. Narrow spans were management folklore before they acquired a formula. In 1933, the consultant V. A. Graicunas tried to explain why spans should remain narrow by counting every possible relationship between a manager and their reports. Four reports produced 44 possible relationships, five produced 100 and six produced 222. ![A dot plot shows Graicunas's theoretical relationship count rising from 44 with four reports to 100 with five and 222 with six. The growth is dominated by assumed group relationships.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/graicunas-relationship-growth-ds-2026.png) This was mathematics in the sense that the multiplication was correct. Everything interesting had been assumed. Graicunas counted every possible grouping as though it occurred, mattered equally and demanded the manager's attention. He then borrowed an argument about how many digits a person could remember, apparently on the basis that an engineer and the number seven impose comparable cognitive demands. Graicunas did not even recommend six for interlocking, non-routine work. He preferred four, with five as the maximum. His work was republished in 1937, after which Lyndall Urwick helped turn the surrounding idea into the familiar rule of five or six. By 1946, [Herbert Simon](https://web.pdx.edu/~stipakb/download/PA511/HerbertSimon-ProverbsOfAdministration.pdf?ref=differentshelf.com) had identified the joke: a narrow span creates more layers, although fewer layers were simultaneously held to be another sacred principle of efficient administration. Management had produced two commandments which contradicted each other and called both science. The commandment has proved remarkably durable. [Gallup's 2026 research](https://www.gallup.com/workplace/700718/span-control-optimal-team-size-managers.aspx?ref=differentshelf.com) found that the average American management span had risen to 12.1, yet the median remained five or six; two-thirds of managers still had fewer than ten reports. Gallup does not prove that modern companies inherited six from Graicunas, only that an unproven number can enjoy a longer corporate career than most of the people subjected to it. Graicunas worried chiefly about coordination, and software engineering once supplied it by the bucket. Frontend needed backend, backend needed infrastructure, infrastructure needed security and security had raised a ticket with the database team. The Engineering Manager found where the work had become lodged and poked it with a stick. Coordination was expensive, so we coordinated through hierarchy. Long before generative AI, [research across eight countries](https://www.nber.org/papers/w14975?ref=differentshelf.com) found that technologies which lowered the cost of acquiring information were associated with greater autonomy and wider managerial spans. Technologies that merely made communication cheaper had the opposite effect because decisions became easier to refer upwards. Slack, it turns out, is not self-government. Engineering then spent twenty years lowering the cost of acquiring information. Platforms made infrastructure self-service, observability exposed running systems, and documentation and code search made knowledge discoverable without first locating its custodian. ## The Machine Knows Where the Bodies Are Buried. It Put Some of Them There AI has accelerated the change, although everybody remains hypnotised by how quickly it writes code. Typing faster is not the interesting part. [Engineering judgement is](https://www.differentshelf.com/the-code-is-no-longer-the-scarce-part/). Wandering further is. Direct evidence that generative AI will flatten hierarchies remains limited, and pretending otherwise would turn prediction into consultancy. The coordination mechanism is clearer. In a [field experiment](https://pubsonline.informs.org/doi/abs/10.1287/orsc.2025.20702?ref=differentshelf.com) involving 791 professionals at Procter & Gamble, individuals using AI matched the performance of two-person teams without it and produced more balanced work across functional boundaries. Product innovation is not software engineering and a two-person team is not an org chart. It is nevertheless an inconveniently suggestive preview of what happens when one person's existing judgement can reach work that previously required another specialist. A frontend engineer can investigate a backend service without first locating its author. A backend engineer can revisit an unfamiliar UI framework. Either can trace a call, decipher a deployment file, read the logs or produce a plausible test before disturbing its custodian. The useful measure is not the code produced, much of which the world could cheerfully do without. Count how often the engineer must stop and find another person before work can continue. AI is frequently wrong and has the confidence of a man in a pub who has confused three newspaper stories. [DORA calls AI an amplifier](https://dora.dev/research/2025/dora-report/?ref=differentshelf.com): sound systems gain leverage, while disordered ones create disorder more efficiently. In a strong system, AI also reduces the cost of being wrong. An engineer can test a hypothesis, inspect the failure and abandon a bad approach before the old process has found the correct specialist and a mutually convenient hour. The danger is not being wrong briefly. It is committing the mistake without review or feedback. Faster coding can disappear into slow testing, security queues, review and deployment, which DORA calls downstream disorder. A faster engineer merely reaches the queue earlier. An engineer can now carry a change further before asking for help. Specialists remain valuable, but we can consult them when judgement is needed rather than whenever an unfamiliar file appears. The economics changed. The org chart was attending a workshop. Here is the peculiar bit. We entrust engineers with production systems, customer data and tools capable of generating code across the stack, then arrange them in groups of six because a seventh apparently creates an intolerable shortage of adult supervision. We call them autonomous until the org chart is opened. Managers will object that with twenty reports they could not know what everybody was doing. Good. Nobody should know what twenty engineers are doing in enough detail to interfere with all of them. ## Please Conduct Your Brilliant Leadership Somewhere People Can Find It Wider spans fail when leaders hide useful judgement in private rooms. Engineering leaders, myself included, ask engineers to take ownership and then schedule a fortnightly meeting to watch them do it. My diary contains councils, forums, steering committees, weeklies, monthlies, bi-weeklies, check-ins, connects, syncs and, my favourite, the quick sync. These are ten names for the same event. If one level of engineering leadership explains a trade-off to the next, which explains it to the next, the organisation calls this a cascade. A cascade is the daftest communication method devised since whispering across a crowded pub: each layer repeats what it thinks it heard, removes anything alarming and adds a deadline. I am often asked to cascade messages to my teams, despite the company possessing email distribution lists and public Slack channels. Apparently these technologies remain experimental, whereas asking eighteen managers to produce eighteen versions of the truth is considered reassuringly mature. The cascade concludes with an alignment meeting, where thirty people attend so that two may discover they disagree. The other twenty-eight serve as human read receipts. A sync is alignment shortened to fit the calendar. A forum has acquired columns and imagines itself ancient Rome. None produces a decision. The steering committee is grander: the people furthest from the wheel meet monthly to ask why the vehicle has not arrived. Naturally it requires a pre-steer, where the same people agree what they will pretend to decide at the real meeting. The quick sync is especially useful because the word "quick" relieves the organiser of preparing anything. If discussion threatens to produce a decision, somebody sensibly proposes taking it offline, where it can no longer endanger the meeting. Put the reasoning where people can find it. The person making a decision should explain it once, in public, rather than feed each layer a private version to misunderstand. A private conversation may serve one person; the reasoning can serve twenty. This does not mean discussing Sheila's performance rating in `#engineering-general`. Pay, illness, conflict and performance belong in private. If it is not private, do not hide it merely because arranging a meeting feels more managerial. Written reasoning can be challenged, corrected, searched and reused; new colleagues inherit the history and AI can retrieve something more useful than the title of a vanished Teams call. Writing demands preparation and an actual thought. A meeting lets you arrive with neither, think aloud at everybody else's expense and call it collaboration. AI has made words cheap, not good. Brutal editing matters more, not less. Write the decision, expose the reasoning and cut until no reader needs the meeting. ## The Holy Half-Hour, Every Tuesday Until One of Us Dies Private management time is the principal argument for narrow spans. It is therefore worth examining how casually we spend it. A meeting without an agenda is a hostage situation with a calendar invitation. Making it recurring merely books the next abduction. The weekly 1:1 began as a useful idea and became a sacrament. At half past ten every Tuesday, two adults present themselves before Outlook because one clicked "recurring" eighteen months ago. Everything is fine. The blocker belongs to another team, which is apparently barking mad, so make the change yourself. No, nobody knows when the progression window will open. Twenty reports at thirty minutes each consume ten hours before preparation, hiring and reviews. Therefore, we say, twenty reports are impossible. Perhaps twenty adults do not require the same appointment every week. A new engineer or someone struggling may need several conversations a week. An experienced engineer receiving continuous feedback may not need the same half-hour until retirement or acquisition, whichever comes first. Attention should follow need. That requires the manager to notice when somebody needs help; Outlook can reserve half an hour, but it cannot care. Formal career and performance check-ins still matter. Twenty reports, once a quarter, means eighty conversations a year: a little over one and a half a week, plus preparation and follow-up. Corporate wisdom insists that all twenty occur in the same two-week "check-in window." Managers abandon useful activity, paste evidence into boxes and receive reminders in ever more accusatory shades of amber, each threatening loss of privileges and a report to the governor. Eventually the dashboard turns green, proving not that anybody developed but that everybody complied. Calibration and reward may need a common window. Development does not. Stagger the conversations and the manufactured crisis disappears. None of this works if the manager also carries a normal sprint commitment. [Gallup's span research](https://www.gallup.com/workplace/700718/span-control-optimal-team-size-managers.aspx?ref=differentshelf.com) found lower engagement among managers who spent more than 40 per cent of their time on individual-contributor work, with the association becoming stronger as spans grew. Twenty reports and six tickets is not a modern hybrid. It is one job done badly and another done after dinner. This is not an argument for pure-play managers. Every Engineering Manager should still do engineering: review, pair, investigate incidents, prototype and improve tools. A manager's technical work should improve delivery. Delivery should not depend on it. ## The Blessed Line Manager, Into Whom the Organisation Pours Everything It Forgot to Design Autonomous adults still need support. It does not follow that every form of support must be bundled into a designated supervisor for each six of them. The line manager has become the organisational lost-property office. Anything without an obvious home gets handed in: architecture, delivery, recruitment, coaching, motivation, administration, compensation, career planning and the emotional consequences of whatever senior leadership announced on Thursday. No wonder the span remains six. We designed a job too swollen to support seven people and mistook the resulting breathlessness for a law of nature. Take the bundle apart. Much of what engineers seek from line management can be provided elsewhere: coaching, guidance, feedback and a second opinion. Principal Engineers can offer technical judgement, Product can supply direction and peers can provide feedback. Even Centres of Excellence, whose name suggests white coats and a small particle accelerator, can spread standards, coaching and difficult review without acquiring reports. Splendid. But if they become another gate or ticket queue, we have replaced the blessed line manager with a committee and called the extra waiting time excellence. The org chart is not a plumbing diagram through which wisdom must flow downhill. Principal Engineers can coach. Peers can coach. A graduate engineer who has just discovered why the build keeps failing can coach the CTO. Human Resources need not launch a reverse-mentoring programme first. Wanting to develop people is admirable. Wanting them formally placed beneath you before development can begin is something else. If the coaching requires a title, perhaps it was authority you wanted. Feedback is a gift, according to Human Resources. So is a dead pheasant. The value depends on who gives it, whether you wanted it and what precisely you are expected to do with it now. It does not become wiser because the person delivering it also controls your pay. Plenty remains for the manager: performance, reward, fairness, retention, capability, succession, conflict and change. Somebody must notice when one excellent engineer is carrying everybody else. The role moves from routing daily work to guarding the health of the system around it. Routing work is seductive. It is visible, fills the calendar and provides the pleasant sensation of being indispensable. Judgement is harder. It leaves fewer meetings behind. If a manager's value disappears when engineers coordinate themselves, the span was never the problem. ## A Team of One, Which Is Considerably Less Lonely Than It Sounds The same fall in coordination cost that permits wider management spans is changing [product delivery itself](https://www.differentshelf.com/the-product-development-team-is-no-longer-the-smallest-unit-of-delivery/). The six-person product team is a community, but it also kept dependencies inside one box: frontend, backend, testing and infrastructure specialists sat together because an outcome required all of them. AI lets one engineer cross more of those boundaries. They can move through the codebase, create tests, investigate infrastructure and carry a bounded outcome towards production. Review and specialist judgement remain necessary. Five permanently assigned people may not. My prediction is that parts of product development will converge towards delivery units of one: one engineer becoming a viable unit of delivery, not an isolated unit of organisation. This will emerge first where the engineer has demonstrated judgement appropriate to the scope, platforms are mature, systems are understandable, support is available and risks are bounded. Before Enterprise Architecture calls an emergency meeting: no, this is not an argument for leaving one exhausted engineer alone with a payment system and an on-call telephone. Safety, continuity, review and collective responsibility remain. The product bet remains shared, Design contributes where needed, a Principal Engineer challenges the architecture, Security reviews the dangerous bit and peers find what was missed. Collaboration happens when useful, not because six chairs share a heading. One engineer may deliver the change, but others must be able to understand, review, operate, change and support it. Otherwise a team of one is merely a bus factor with a laptop. Work proceeding on several fronts and systems requiring an on-call rota may still need a stable group. If every engineer queues for the same Principal Engineer, security specialist and platform engineer, we have merely rebuilt the old team badly in Slack. This is why leadership must become visible. Smaller units work only when context and reasoning travel freely and experienced judgement remains easy to reach. Otherwise we have removed the coordinators and left everybody else to rediscover the same facts alone. ## When the Work Changes, the Org Chart Should Admit It Twenty will not become the new six. A wider span is not achieved by typing twenty into Workday. It is earned by making ordinary work less dependent on the manager. Deleting alternate boxes from PowerPoint creates no autonomy. If decisions still climb the hierarchy, deployments still require favours and every difficult question joins a specialist queue, the old dependencies remain. The diagram has merely stopped mentioning them. Nor should anyone sack the middle layer and buy everybody an AI licence, the sort of transformation which begins with a burning platform and ends with a consultancy partner's new kitchen. Remove managers before redistributing coordination, coaching and technical leadership and none of that work disappears. It becomes nobody's job until it becomes everybody's crisis. Begin with [the work, not the boxes](https://www.differentshelf.com/smaller-delivery-units-wider-management-spans/). Remove routine coordination from the manager, distribute technical leadership, make work visible and ensure delivery does not depend on the manager's individual output. A span can widen only while engineers act within agreed boundaries without routine managerial permission, managers spot performance problems early and people get private attention when they need it. When those conditions fail, remove work from the manager or narrow the span. Putting a manager and five permanent specialists around every change is not autonomy. It is supervision with better branding. We built teams partly because expertise had to be gathered in one place, and hierarchies because work had to pass through somebody who could see the whole. Neither premise is as true as it was. Engineers still need direction, standards, challenge, colleagues and care; they do not need all of it routed through the same manager. The engineer will finish the application. I shall eventually present it as evidence that our operating model is working, which is how executives convert somebody else's achievement into strategy. We reduced the cost of coordination. It would be peculiar if the only thing left unchanged were the organisation designed to manage it. ### The Answer Without the Question URL: https://www.differentshelf.com/the-answer-without-the-question/ Last updated: 2026-08-22T22:55:21.000Z *It read like an explanation. Headings, categories, causes, recommendations. The sort of document that settles people down when it lands in a Slack thread. Nothing obviously wrong. Nothing that invited argument. And yet it carried the faint unease that comes when something has been resolved too neatly.* ## The Shape of the Answer The context was performance. A regression had appeared and an analysis was requested. What came back was an AI assisted review of recent work, grouped into themes. Image loading. Bundles. Lazy loading. Server side rendering. Design system changes. Each section made a reasonable case. Together they formed a narrative that sounded, at first pass, like understanding. It was clear. It was professional. It was also flawed in a way that was easy to miss and difficult to ignore once seen. The problem was not that the document was wrong. The problem was that it answered a question nobody could see. A polished answer can conceal an unexamined question. Nowhere did it say what had actually been asked. Nowhere did it show when the metric moved or by how much. Nowhere did it explain why these changes mattered more than the many others committed in the same period. The structure suggested diagnosis, but the substance was closer to categorised speculation. ## What the System Was Asked to Do AI is very good at producing outputs that look like the end of thinking. It is less reliable at doing the thinking itself, at least in the sense that engineers usually mean it. What it excels at is organising language. If you give it commit messages, it will group them. If you give it themes, it will narrate them. If you ask for causes, it will infer intent and present it back as mechanism. This is not deceit. It is compliance. Consider a plausible prompt behind the document. Something like, summarise recent performance related commits that might have made performance worse. That is a reasonable request when time is short and you want a quick sense of what has changed. The system will do exactly that. It will look for commits that appear performance related. It will cluster them by topic. It will note churn and reversions. It will produce a narrative that reads as analysis. What it will not do is examine everything else. ## What Was Excluded by Design It will not consider feature work that quietly added an extra call on the critical path. It will not consider refactors that altered render order. It will not consider new content that increased payload size. These changes are less visible in code and often more likely to affect real metrics. They may not live in the codebase at all, but in configuration or content management systems. By constraining the input to performance related work, the analysis embeds a false premise. It assumes regressions are usually caused by people touching performance. In practice, performance work often follows a regression. It clusters after the fact. That makes it visible and easy to blame. The document then treats activity as signal. Many commits in one area become evidence of instability in that area. Density is mistaken for causality. Without a comparison set, the narrative cannot distinguish reaction from cause. A category can organise an answer. It cannot establish a cause. Experienced engineers recognise this immediately. Real regressions tend to have an awkward shape. They come from changes that looked harmless at the time and did not announce risk. The AI smooths that awkwardness away and replaces it with coherence. ## What Runtime Evidence Already Shows There is another omission, and it is more practical than philosophical. Modern browsers are very good at telling us where time goes. Chrome’s performance tools do not whisper. They highlight long tasks, blocked rendering, delayed network requests, and hydration costs on a timeline that is difficult to ignore. When performance regresses, the slow work is almost always visible. You can draw a box around it. You can point to the moment it ran and see what it blocked. You can tell whether the cost was network, main thread, or rendering. The fix may still be non trivial, but the guesswork should already be constrained. Analyses that begin with commit history rather than runtime evidence reverse this order. They start with narrative and work backwards, instead of starting with a flame chart and asking what could plausibly have caused it. Without that grounding, even a careful explanation remains a hypothesis. If you cannot point to the slow bit, you are not analysing yet. You are guessing. ## Safe Conclusions and Their Cost The recommendations at the end complete the picture. Audit images. Review lazy loading. Improve monitoring. Sensible advice, but generic enough to apply almost anywhere. Their safety is the clue. They signal completeness without specificity. There is a quieter cost. By listing many plausible contributors, the document makes the regression feel like an emergent property of complexity. No clear mechanism. No accountable change. This can be comforting, but it also slows learning. None of this argues against using AI. Used carefully, it is effective at widening the search space and summarising large volumes of material. The problem arises when its output is allowed to stand in for understanding. ## Why the Prompt Matters Without the prompt, the analysis cannot be evaluated. The reader does not know what the system was asked to do, what data it was allowed to see, or what was excluded by design. The answer is judged without access to the question that shaped it. In any other analytical discipline this would be unacceptable. A model without its features. A forecast without assumptions. An experiment without method. The prompt defines the boundaries of thought as much as the data does. The prompt is part of the method, not an implementation detail. Showing the prompt does not make the analysis correct. It restores context. It tells the reader whether they are looking at hypothesis generation, summarisation, or an attempted explanation. It makes clear where the machine stopped and where human judgement was expected to begin. ## A Working Rule There is a simple practice that would remove much of this ambiguity. When AI is used to produce analytical work, the prompt should travel with the output as required context. The prompt defines what was considered, what was prioritised, and what was ignored. Without it, confidence is mistaken for method. This is not transparency as virtue. It is professional hygiene. Documents like this influence decisions, priorities, and narratives of cause and effect. A polished analysis that cannot be audited for scope or intent can mislead precisely because it appears complete. AI will continue to improve at producing plausible explanations. That is expected. The responsibility on senior teams is to ensure those explanations remain grounded in a visible question, runtime evidence, and an explicit handoff to human judgement. If the work is analytical, show the prompt. It is a small constraint, but it keeps explanation from outrunning understanding. ### What It Takes for a Web App to Feel Premium URL: https://www.differentshelf.com/what-it-takes-for-a-web-app-to-feel-premium/ Last updated: 2026-08-24T16:18:15.000Z *Performance work rarely arrives as a single insight. It tends to show up as a collection of irritations. Small delays you notice at first and then cannot unsee. A pause that feels longer than it should. A screen that forgets where you were. An interaction that technically works, but never quite feels settled.* *That has been the shape of our work for a while now. Not a grand plan, more a sequence of corrections. Each one made for a specific reason, each one exposing the next thing that felt off.* ## Arrival The earliest problem was obvious enough. The app took too long to arrive. Blank screens. Late content. Interfaces assembling themselves in front of the player. Server-side rendering was the right fix. It moved work earlier, closer to the source, and allowed the browser to present something meaningful immediately. The app arrived already formed, rather than apologising while it booted. Premium is not one breakthrough. It is the absence of small betrayals. Hydration came with it. There is no avoiding that cost. You pay it once per session and move on. That trade is now well understood, and for most serious apps it is a reasonable one. SSR fixed arrival. It did nothing for what came next. ![Five aligned stages trace a player journey from arrival to return. Each pairs a small betrayal with the engineering choice that removes it and the part of flow it protects, showing premium experience as an accumulation rather than one breakthrough.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/premium-player-journey-ds-2026.png) ## Responsiveness Once the app was interactive, a different kind of delay stood out. Interactions were mostly fast, until they weren’t. A tap would occasionally hesitate. A scroll would stutter for reasons that were hard to pin down. The cause was rarely core product code. It was everything orbiting it. Analytics. Tracking. Affiliate logic. Marketing tags. All legitimate, all useful, none of them directly involved in helping a player take their next action. Introducing [Partytown](https://www.differentshelf.com/watching-the-watchers-part-2-how-scripts-break-your-frame-budget/) was less about metrics and more about priorities. Third-party scripts still ran. Data was still collected. But that work stopped competing with interaction. The computation that mattered most stayed where it belonged. The main thread became calmer. The app stopped feeling distracted. Third-party work remained useful after it stopped competing with the player. ## Native Browser First That calm exposed something else. There was simply too much JavaScript involved in basic interaction. Over time we had accumulated code whose job was to recreate behaviour the browser already understands. Scrolling with momentum. Snapping. Touch and pointer gestures. Animated transitions between states. [All of it implemented in JavaScript, all of it running on the main thread](https://www.differentshelf.com/let-the-browser-do-the-heavy-lifting/). Every scroll became a calculation. Every swipe became a listener. Every animation became a scheduling problem. We were simulating a browser inside a browser, and then wondering why input sometimes felt late. The same was true of animation. Anything that required JavaScript to advance frames, coordinate timing, or update layout under load competed directly with interaction. Even when it looked smooth in isolation, it took priority at exactly the wrong moment. Removing this code reduced JavaScript execution, not just bundle size. More work moved into native browser paths. Scrolling became composited. Gestures became direct. Animation stopped blocking interaction. This wasn’t minimalism. It was recognising when to stop arguing with the browser. ## Run on the Fastest Browser Once that principle was clear, it applied beyond the code we wrote and into how we shipped it, particularly on Android. Wrapping the web in a WebView worked, but it came with a ceiling. Even well tuned, it is typically slower than a fully optimised Chrome instance. Not marginally. Often by twenty or thirty percent. That gap shows up in rendering, input handling, and overall smoothness. Moving to a [Trusted Web Activity](https://www.differentshelf.com/beyond-the-webview-why-angular-apps-belong-in-a-pwa-twa-world/) followed the same logic as everything else. Use the best browser available. Stop embedding a constrained one. Nothing about the app changed conceptually. The same code ran. The same features existed. But rendering was faster, input smoother, and the browser handled more of the heavy lifting. ## Anticipation With interaction steadier and JavaScript reduced, navigation became the next source of friction. Clicking into an event or a game triggered network calls that could never feel instant, no matter how well tuned they were. The problem wasn’t speed. It was timing. [Speculative links](https://www.differentshelf.com/lazy-loading-isnt-just-lazy-its-late-the-web-deserves-faster/) came from that realisation. If a link is visible, the player is already considering it. That is enough information to begin loading data, warming resolvers, and doing work before the click happens. Loading didn’t disappear. It moved out of the critical path and into idle time. Navigation stopped feeling like a pause. ## Continuity Even with data ready, navigation could still feel abrupt. Screens appeared and disappeared without continuity. View transitions helped here, not because animation makes things faster, but because continuity makes things legible. Elements that move rather than vanish preserve context. Around the same time, gesture-based navigation arrived. Swipe to go back. Drag to dismiss. Movement stopped being a command and started behaving like motion. The interface responded immediately, even if the destination was still catching up. ## Memory By this point the app arrived quickly. Interactions were steady. The main thread was quieter. Data was often ready. Transitions felt coherent. And yet, one irritation remained. Going back still felt like starting over. Angular’s default routing model destroys components on navigation. It is a reasonable default, but it is not unique. React behaves much the same unless you are deliberate about preserving state through layouts, caches, or offscreen trees. The default mental model in most web frameworks is still page-like. Native apps tend to take the opposite approach. Screens stay alive unless there is a good reason to discard them. Navigation reveals existing state rather than reconstructing it. The premium feel often comes from these lifecycle defaults rather than raw performance. [Route reuse](https://angular.dev/guide/routing/customizing-route-behavior?utm%5Fsource=chatgpt.com#route-reuse-strategy) on the web is an attempt to choose the same defaults. Not everywhere, and not blindly, but where it matches how players actually move through the app. Coming back should feel like returning, not rebuilding. Returning should reveal preserved state, not reconstruct a page. --- In our company we’ve made progress on parts of this, and we’re open about what still needs to land. That’s expected. This isn’t a finished state so much as a direction we’re deliberately moving in. One that applies to any team trying to make a single web codebase carry a genuinely premium experience across many surfaces, where duplication is permanent and improvements only really pay off if they land everywhere at once. --- ## Epilogue: Where the Feel Comes From Most of what makes a web app feel premium lives below the headline features. The big steps matter, but the lasting impression comes from texture. A steady frame rate rather than a high one. No layout shifts once the page hydrates. Interactions that acknowledge intent immediately. Scroll positions and tabs that remember where they were left. Data warmed before it is needed. Navigation that behaves the way the player expects, not the way the router prefers. Native apps benefit from these defaults by design. On the web, we choose them deliberately. That choice is sometimes uncomfortable, but it is what allows a single codebase to carry a serious experience across many surfaces. The premium feel is not a breakthrough. It is the accumulation of small decisions that protect flow. Most of them are already within reach. ### The Five Second Contract URL: https://www.differentshelf.com/the-five-second-contract/ Last updated: 2026-08-24T16:18:15.000Z *Players have an internal clock. If something they tap takes more than a few seconds it already feels uncertain. At five seconds most people abandon the flow. They do not complain. They simply adjust their behaviour. The pain shows up in conversion long before it appears in error logs.* That five second line is not guidance. It is the boundary the product has to respect. When we cross it we lose the player and we strain the backend at the same time. ## The quiet cost of waiting Most of our services still rely on synchronous calls for important work. In a synchronous model the request thread waits for the backend. Waiting is not free. It is capacity held in limbo. A timeout is a product boundary and a capacity multiplier at the same time. The relationship is simple. threads\_in\_use \= RPS × request\_time\_seconds A one second call at scale is a nuisance. A ten or twenty second call becomes the dominant cost. With no timeout the pressure has no ceiling and the system behaves accordingly. ## Surges turn delay into load Take a surge of five thousand requests per second. The thread counts below show what would be required to keep up. | Timeout or latency | Threads at 50 RPS | Threads at 200 RPS | Threads at 5 000 RPS | | ------------------ | ----------------- | ------------------ | -------------------- | | 0.1 s | 5 | 20 | 500 | | 1 s | 50 | 200 | 5 000 | | 2 s | 100 | 400 | 10 000 | | 4 s | 200 | 800 | 20 000 | | 8 s | 400 | 1 600 | 40 000 | | 16 s | 800 | 3 200 | 80 000 | | 30 s | 1 500 | 6 000 | 150 000 | | No timeout | unbounded | unbounded | unbounded | The last row is where the picture tilts. With no timeout the system has no upper limit. Threads accumulate until the runtime slows, the autoscaler reacts or the service folds under its own weight. In practice it goes bang long before the maths finishes. ## Why long timeouts rarely help A long timeout can feel gentle. It gives the backend more room to reply. The trouble is that the player has usually moved on long before it expires. The extra time rarely improves the experience. It does, however, hold the thread for longer and raise the load during busy periods. The return is small. The cost is not. ## A note on the five second line It is worth being honest about this boundary. Five seconds is not a comfort zone. A one second delay is unnoticed. Two seconds is tolerable. Around three seconds people begin to doubt the product. Four seconds erodes confidence. By five seconds the experience has already slipped. So the five second line is not something to aim for. It is the point where we can no longer pretend the interaction is intact. We use it because it is memorable, because it exposes genuine structural issues, and because anything that approaches it deserves a design conversation, not a longer timeout. ## The design question Once an operation stretches beyond five seconds the timeout stops being the interesting part. The shape of the work becomes the question. Once work outlives the interaction, change the choreography rather than extending the wait. At that point the synchronous model shows its limits. Holding a player while a backend completes a long or unpredictable task is rarely a good trade. The experience becomes fragile, retries pile up and the backend carries threads that could serve fresh requests instead. A better pattern is to change the choreography. Let the player tell us what they want to do. Confirm that we have received it. Move the long work into an asynchronous worker. When the job completes, update the player. The action still happens, but the wait no longer sits on the critical path of their session. This looks like a small shift in code. It feels larger in production. ![A synchronous timeline at 5,000 requests per second grows from 5,000 held requests after one second to 25,000 at the five-second player boundary and 150,000 after 30 seconds, while an asynchronous path confirms early, releases the request and notifies after completion.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/five-second-contract-ds-2026.png) ## Money, Money, Money There is a financial side to all of this. In AWS we pay for capacity whether it is doing real work or sitting blocked. A waiting thread ties up an instance even though nothing useful is happening. The platform carries that cost simply because the backend might eventually reply. To make this concrete, consider a `c6a.large` in São Paulo at roughly 0.1179 USD per hour, about 86 USD per month. Assume a service can handle around one thousand waiting threads per instance before context switching and memory pressure become uncomfortable. These numbers are illustrative, but the pattern is real. | Timeout or latency | Approx. concurrency | Instances needed (≈ 1 000 waiting threads each) | Approx. monthly cost | | ------------------ | ------------------- | ----------------------------------------------- | -------------------- | | 2 s | 10 000 | ≈ 10 | ≈ 860 USD | | 4 s | 20 000 | ≈ 20 | ≈ 1 720 USD | | 8 s | 40 000 | ≈ 40 | ≈ 3 440 USD | | 16 s | 80 000 | ≈ 80 | ≈ 6 880 USD | | 30 s | 150 000 | ≈ 150 | ≈ 12 900 USD | | No timeout | unbounded | unbounded | unbounded | These machines are not busy. They sit holding threads that wait. The successful RPS does not change. The only shift is how long each request occupies a thread. In a fleet with a steady baseline, this pattern can push compute cost toward a tenfold increase during heavy traffic. Waiting capacity is still capacity the organisation pays for. A design that finishes quickly, or that hands long work to an asynchronous path, keeps instance counts tied to real demand and stops us paying for capacity whose main purpose is to wait politely. ## The contract we inherit The player never sees the thread count. They see only the spinner that pushed past their patience. The five second threshold is already part of how they interact with us. Our job is to stay comfortably on the near side of it. The work is not in enforcing the timeout. The work is in designing and scaling services so the timeout rarely enters the conversation at all. ### The Other Production URL: https://www.differentshelf.com/the-other-production/ Last updated: 2026-08-22T22:55:21.000Z *Players only ever see one world. They judge the product by what reaches their hands. Everything upstream is invisible to them, and that is entirely reasonable. Engineers work in a different world. Their day can depend on shared systems. Source control. Build pipelines. Shared services. Test and preview environments. When any of these falter, progress stops. Not later. On the spot.* This is the part organisations often miss. These internal systems are production for the people who build the product. If source control is down, it is a production outage. If CI will not run, it is a production outage. If a shared service drifts from its contract because it is being bent into a shape it was never meant to hold, it is a production outage. Players may never touch these systems, but their experience depends on them all the same. Anything that stops engineers stops the company, even if no player can see the outage. The temptation is to treat internal platforms as second class because they do not appear in customer monitoring. It seems harmless. It rarely is. When the basics cannot be trusted, engineers resort to workarounds. Parallel copies. Manual steps. Guesswork. These patch the day but leak into the product. The debt accumulates quietly, then arrives loudly. Workarounds move internal unreliability downstream until players inherit it. The teams who run these systems feel this tension most. They are asked to deliver production-grade stability, yet the environments they manage are described as non production. The disconnect weakens priorities and excuses unreliability. The honest framing is simpler. Anything that blocks engineers blocks the company. The label on the environment does not reduce the impact. None of this is easy. Keeping internal systems healthy is hard with twenty five engineers. It is hard with two hundred and fifty. It is hard with two thousand five hundred. Scale changes the shape of the work but not the difficulty. The dependence is universal. Reliable software begins with reliable internal systems. Treating them as best endeavours only shifts the cost downstream where it spreads into the product itself. A stable development chain is not accessory work. It is the start of everything players feel. If we want consistency in the world players see, we need consistency in the world engineers inhabit. They are two sides of the same production line. The only difference is which side the audience stands on. ### Let the Browser Do the Heavy Lifting URL: https://www.differentshelf.com/let-the-browser-do-the-heavy-lifting/ Last updated: 2026-08-24T16:18:14.000Z *Frontend performance used to mean trimming weight on the wire. Fewer requests. Smaller bundles. Tighter compression. These habits made sense when the network was the chokepoint. They still help, but they explain very little about how a modern single page app behaves. In an SPA the expensive work happens after the shell loads. The runtime, not the network, decides how fast the product feels.* The network delivers the app. The main thread decides whether it feels alive. An SPA begins its life at first paint rather than finishing there. From that moment on, the browser’s main thread carries most of the burden. That thread is narrow, busy and easy to overwhelm. Nearly every performance issue that players feel originates there. ## What the network cannot save CDNs, caching and minification buy a quick start, but once the app boots the device becomes the real terrain. Less is often more in terms of bytes, and [speculative loading](https://www.differentshelf.com/lazy-loading-isnt-just-lazy-its-late-the-web-deserves-faster/) can mask some of the cost, but none of that changes the underlying truth. The real expense comes from the work the app keeps doing. A static site can relax once visible. An SPA cannot. It keeps reconciling, recalculating and scheduling its own activity. ## The fragile centre of the runtime Browsers route most meaningful work through one thread. That simplicity helps developers, though it also ensures that any stray computation competes with rendering, input and layout. A helper that looks harmless in isolation can become expensive if it runs often enough. A transformation that costs almost nothing per item can freeze input once a data structure grows. We saw this in a small utility that shaped carousel state. The logic was tidy and the per-item cost tiny. What hurt was how frequently it ran. Desktop machines shrugged it off. Mid-range Android devices did not. The code was fine. The behaviour was costly. A cheap function becomes expensive when the schedule calls it constantly. ## Rendering as mathematics, not decoration Frameworks treat the UI as a dynamic graph. They traverse trees, detect differences, reconcile nodes and patch the DOM. The cost grows with the size of the tree and the rate at which you disturb it. A large interface with predictable updates can feel smooth. A smaller one with erratic updates can stutter. One engineer added a neat measurement routine for text labels. It forced layout every time it ran. Laptops absorbed the impact. Phones did not. The browser spent more time measuring than drawing anything useful. ## Animations as hidden work Animations are often assumed to be free. They are only free when they run off thread. Transforms and opacity changes live in that safer space. Most other animations do not. Anything driven by timers or repeated DOM updates drags the main thread back into the picture. We once shipped an SVG spinner to announce that the app was busy. It looked tidy. It was also the busiest thing in the entire app. Every frame forced vector recalculation and style updates. When the device fell behind, the spinner stuttered, turning into a live demonstration of how hard the runtime was struggling. It was meant to reassure. It achieved the opposite. A stuttering spinner does no useful work, yet competes fiercely for the same scarce resources. ## Native features are usually cheaper than JavaScript Many performance problems come from forgetting what the browser already knows how to do. For years we used layout libraries to position items, handle scroll, coordinate touch and animate transitions. They existed because browsers had gaps. Those gaps have been closing for a decade. Modern CSS can handle grid, flex, snap points and smooth scrolling without executing a line of JavaScript. The compositor does in one step what our code once achieved through a string of operations. Each time we replace a JavaScript wrapper with a native capability, the app becomes steadier. The browser has dedicated pipelines for these tasks and uses them without touching the main thread. The best optimisation can be deleting code the browser already replaced. The lesson mirrors the spinner. The cheapest work is the work you avoid. ## When frequency becomes the real enemy SPAs do a lot of bookkeeping. Sorting. Filtering. Transforming. Reacting. Each piece looks modest until the frequency climbs. A reactive stream fires more often than expected. An update runs through a larger object graph than intended. A tiny function executes hundreds of times per second. During an especially busy period in one of our apps, incoming events spiked into the hundreds per second. Each event was handled in isolation. Every update triggered rendering and more computation. The app entered a loop it could not escape until the event rate dropped. The logic was correct. The pattern was not. Once we coalesced events into predictable batches the problem disappeared. The algorithm never needed to change. The schedule did. ## Thinking more native A large part of this job is learning when not to write code. Framework expertise matters, but the browser is not a blank canvas. It is an active system with its own instincts. It can animate, schedule, composit, layout and scroll without asking JavaScript for help. Many of the mistakes we make come from overlooking what it already handles well. The most reliable performance improvements come from asking a simple question: does the browser already know how to do this better than we do? When the answer is yes, the fix is not an optimisation. It is a deletion. ## What players actually feel Players never think about long tasks or scheduling queues. They notice how the app behaves. A blocked thread feels like hesitation. A layout thrash feels like the screen drifting under their finger. A busy microtask queue feels like a second tap that should not be needed. A struggling animation feels like doubt. These sensations are subtle but cumulative. They shape trust more effectively than anything the interface says. Responsiveness builds confidence quietly. When the interface answers at once, the player feels in control. When it hesitates, they feel tension, even if they cannot explain why. ## Responsiveness as product value A responsive lobby is browsed more deeply. A responsive betslip completes more reliably. A predictable withdrawal flow generates fewer support contacts. These are not dramatic gestures. They are small interactions that compound. The runtime shapes how the product feels, and how the product feels shapes behaviour and outcomes. We have watched conversion funnels collapse under mild jank and recover once the interface behaved predictably. Players never know what changed. They only know the product feels cooperative again. ## Performance as culture Teams living under slow runtime conditions become defensive. They ship less. They refactor less. Everything feels brittle. Once the runtime becomes predictable, team behaviour shifts. Engineers remove code instead of adding it. They simplify flows that once seemed untouchable. They experiment because the surface area feels safe. Performance is not just a technical constraint. It is a cultural one. ## The modern performance story Performance is no longer about trimming kilobytes. It is about understanding the runtime, respecting the main thread and removing everything that does not need to exist. The browser is fast. It becomes slow only when we insist on fighting its design. When we stop fighting it, the gains show up everywhere: in motion, in confidence, in player behaviour and inside the teams building the product. ### Making a Big Repo Feel Small URL: https://www.differentshelf.com/making-a-big-repo-feel-small/ Last updated: 2026-08-22T22:55:20.000Z *Walking into a monorepo for the first time can feel like arriving in a city without a map. Streets go on forever, blocks repeat themselves, and you only need a few addresses. Sparse checkout is the tool that stops you trying to live in every neighbourhood at once.* ## Why sparse checkout exists Large repos are no longer unusual. They are the normal state of modern engineering. Companies that consolidate code do it for good reason: faster refactors, one source of truth, easier inner-sourcing, and predictable release trains. But the trade-off is obvious: no individual developer can or should carry the whole weight of the codebase. Sparse checkout reduces what a developer carries without reducing what the organisation shares. Without sparse checkout, cloning a repo with dozens of apps and hundreds of libraries takes hours and fills disks. IDEs groan under the indexing load. New starters lose half a day before they even compile “Hello World.” Sparse checkout trims the working copy to only what is needed. It acknowledges that developers operate in their own neighbourhoods while still belonging to the same city. This is not exotic. It is how the best in the industry work. Google’s Android teams rely on `repo` to pull just the projects they need. Microsoft created GVFS (now VFS for Git) so Windows engineers could work productively without carrying the whole operating system tree. Meta went as far as building Sapling, their own Git-compatible client, because at their scale filtering is the only way forward. Git itself absorbed those lessons: by version 2.37, sparse checkout in cone mode is the default. What was once custom infrastructure is now just Git. ## How it works in modern Git Sparse checkout tells Git which directories to materialise in your working copy. The rest stay invisible until you ask for them. History is unchanged; you are simply choosing what to see day to day. The working tree is a view, not the repository. For Git 2.37 and later, cone mode is the only mode you need. It works directory by directory, which is what most developers want. Start light: ```bash git clone --filter=blob:none --no-checkout cd git sparse-checkout init --cone git sparse-checkout set git checkout main ``` Expand your world later: ```bash git sparse-checkout add ``` Rejoin the full city: ```bash git sparse-checkout disable ``` A new developer can be productive in minutes instead of hours. ## What it is not Sparse checkout is not a security boundary. The hidden directories are still present in history. It is not a performance cure-all either; if your repo has tangled dependencies, hiding directories will not untangle them. It is a convenience tool to keep daily work lighter. ## The cultural side The bigger shift is in how developers think. The old mental model was: each team has its own repo. The new one is: everyone shares the same repo, but uses sparse checkout to focus on their part of it. Work still flows through the same trunk, release trains still run on time, but individuals stay in their neighbourhood. The point is not to see everything. The point is to trust the whole city is there, even if you only walk a few blocks today. ### Keys to the Kingdom URL: https://www.differentshelf.com/keys-to-the-kingdom/ Last updated: 2026-08-24T16:18:14.000Z *As product engineering teams ship continuously, there has to be a way to steer what players actually see without shipping code every time. That is what configuration does. It is the layer that decides how a product behaves in real time. Small key-value pairs carry big decisions, like whether a player in the US sees a two-step identity check while a player in the UK goes straight through, or whether a new navigation layout appears in one region before another.* Configuration separates deployment from release, but it also moves production power into data. This is not exotic. There are industry-standard tools that do it: ZooKeeper at Yahoo, Etcd in Kubernetes, Consul from HashiCorp, or cloud-native versions like Parameter Store and App Configuration. The idea is the same in each case. Hold the truth in one place and make it fast to look up. ## What Configuration Really Does A configuration system is not a CMS. It does not store articles or product pages. It stores conditions and outcomes. The site checks those conditions and adjusts itself. The values can be varied. A timeout setting for a payment provider. A percentage split for an experiment. A URL pointing to a third-party script. A feature flag that enables a new flow in one market but not another. This indirection matters. Without it, every change would require a release. With it, differences are moved into a fast lookup. The live system adapts without redeploy. ## How Changes Move: Promote, Don’t Poke Once you accept that configuration drives behaviour, the next question is how changes enter the system. It can feel tempting to edit production directly, but that invites drift and mistakes. The value you tested in staging ends up different from the one you ship in production. Copy-and-paste sneaks in and you are debugging two realities. A cleaner model is promotion. Every configuration change begins in a non-production environment. Once validated, that exact change is promoted into production through a pipeline. What you saw working in test is the thing you release. Promote the exact value you tested. Do not recreate the intention in production. This is not bureaucracy. It is trust. Promotion reduces accidents, creates an audit trail, and leaves behind events that observability can correlate with behaviour. It gives you consistency and safety. ## Where the Finger Points When something goes wrong in production the first question is usually the same: what changed. Quite often the answer is a configuration change. That can make the system look guilty, but more often it is the thing switched on or off. Sometimes it is trivial, like the wrong timeout. Sometimes it is serious, like a script URL pointing to the wrong host and breaking startup. This is why the separation of deployment and release matters. Code can be deployed quietly. Release can then be controlled through configuration, in small steps and with reversibility. Blaming the configuration system for an incident is like blaming the light switch for a bad bulb. ## Keeping Change in Sight If configuration is where release is expressed then it is also where problems often show up. That is not a weakness, but it does mean we need to track it carefully. Every change should flow into monitoring and be visible in the same tools we use to watch traffic, errors, and latency. Correlation is what turns change into cause. Configuration changes are release events and should be observable as such. Visibility alone is not enough. The same system that enables a safe rollout can also take a product offline if misused. Not everyone needs the same level of control. Engineers may need to create new flags, product managers to toggle existing ones, and compliance teams just to see. A healthy system reflects these roles with permissions and records of who changed what, when, and why. Together, observability and governance ensure that configuration remains a tool for safe release rather than a source of risk. ## The Long-Term Challenges Even with promotion and good oversight, entropy creeps in. Each item feels small, but over years the list grows from tens to hundreds to thousands. The system can handle the scale because a lookup is constant time and usually cached. What it cannot handle for you is meaning. You forget what half of them do. That loss of meaning is compounded by uncertainty. Some flags are still in heavy use, others guard code paths that are never called. Without clarity, the configuration store becomes cluttered and trust in it erodes. This is where AI can help. With a configuration database and a monorepo side by side, flags can be mapped back to their code paths, usage can be surfaced, and forgotten switches given context again. AI will not remove the need for discipline, but it can accelerate clean-up, highlight risky items, and keep entropy in check. ![A six-stage provenance ledger follows configuration from authoring to removal. Each stage keeps the same change beside the evidence it creates and the authority acting on it, with promotion highlighted as the point where the tested value must remain identical.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/configuration-provenance-ledger-ds-2026.png) ## Nothing Unusual About It Configuration systems are not exotic. They are routine infrastructure. Your car uses one to load a driver profile. Your thermostat does the same on a simpler scale. On the web, the number of conditions is higher and the stakes are sharper. But the principle is the same. It is a way to keep control of a running system without constantly rebuilding it. ### What Would It Take to Go Live Next Week? URL: https://www.differentshelf.com/what-would-it-take-to-go-live-next-week/ Last updated: 2026-08-22T22:55:20.000Z *Deadlines have a strange power because they collapse theory into fact. They reveal the truth of a system faster than any meeting ever will.* We recently pulled a major migration forward by months with one question: *what would have to be true for us to go live next week?* It wasn’t bravado. It was an experiment in honesty. A near date is useful when treated as a probe, not a promise. Within days, we uncovered issues that had been quietly waiting for someone to look. Dependencies we thought were done weren’t. Interfaces we assumed were stable weren’t. But instead of hearing these second-hand in a status meeting weeks later, we discovered them by trying to move. The question worked because it forced clarity. We stopped talking about readiness and started testing it. The team checked what could run, what would break, and what wasn’t yet wired up. Work took on shape. Theoretical blockers became visible, and everyone could call out what still needed attention. Projects have a way of expanding to fill the time available. The longer the horizon, the fuzzier the edges. Urgency is often seen as risk, but a short horizon creates focus. Ask what could be true in a week, and you find out what is actually standing in the way. This approach only works when used sparingly. You cannot run every delivery in crisis mode. Teams burn out, quality suffers, and trust erodes. Some systems need deliberate sequencing. Some dependencies take time. The point is not to skip planning but to use action as a diagnostic. Trying to ship converts assumptions into failures the team can inspect. Pulling a date forward means something will be unfinished. Corners will show. Some polishing will roll into the next sprint. There is a line between untidy and unsafe. The first is often fine; the second never is. The art lies in knowing which details can wait and which cannot. Trying to go live early does more than accelerate delivery. It reveals structure, shows which dependencies are brittle, which processes are slow, and where ownership is unclear. These are truths no meeting will ever surface. But motion only helps if we stop long enough to see what it revealed. The aim is to learn, not to rush. The moment people feel it is theatre, it stops working. For strong engineering teams, this can feel uncomfortable. They care deeply about craft. Being asked to move before everything is perfect can sound like lowering standards. And often, they hesitate for good reason, because they have seen what happens when urgency is used carelessly. It can mean after-hours callouts or last-minute fixes that could have been avoided. And yes, that cost is real. But the benefit can be greater. The act of trying exposes truths that drift unseen in long projects. More often than not, what we learn in those moments saves far more than it spends. When I ask *what would it take to go live next week?*, the right answer might be *nothing, we are not ready*. That is fine. The point is to look, not to leap. The question gives us a shared view of what is true and what still needs care. These experiments also surface broader truths: the gaps between teams, the bottlenecks in governance, the missing ownership around integration points. They show that coordination is a product, not an accident. Seeing those friction points early is what makes them fixable. Engineering excellence matters, but it serves something larger. The goal is not perfection. It is delivering a better player experience sooner. Most of the time we are closer to good enough than we think, and progress depends less on certainty than on trust, in the system, the craft, and each other. A few days spent trying to ship often beats weeks of waiting to talk. ### The Myth of Multiplying Yourself URL: https://www.differentshelf.com/the-myth-of-multiplying-yourself/ Last updated: 2026-08-22T22:55:20.000Z *Two doors, one room. It looks like choice, but both lead to the same place.* It’s common to see the same app appear twice in the store. Two icons, two listings, one underlying experience. Duplication rarely begins as folly. It usually starts as caution. Someone suggests a second listing to reach another audience or improve discoverability. On paper, it sounds clever: two routes into the same experience, each tuned for its own screenshots and keywords. One invites you to play, the other to explore. Twice the entry points, twice the visibility. For a while, it works. The listings are neat. Each tells a tidy story. But over time, the split becomes a drag. What began as precision starts to look like noise. App stores reward momentum, not multiplication. Every listing competes not just with the market, but with its twin. Downloads, reviews, conversion rates, and retention are treated as separate signals. One app’s strength does not help the other. The stronger listing might pull ahead, or perhaps neither gathers enough energy to break the surface. Two halves of the same idea treading water, never reaching the critical mass that pushes a single app into real visibility. Two listings split the evidence that one product needs to compound. The algorithm doesn’t care about intent; it cares about coherence. When engagement splits, authority never compounds. Both listings sit in the middle ranks, healthy enough to exist but too diluted to rise. Two listings mean double the metadata, double the screenshots, double the privacy manifests, and double the review responses. Each small edit becomes a project. Every keyword change must be mirrored. Even ratings diverge, giving players two stories that do not align. Technically, the symmetry creates friction. [Universal Links](https://developer.apple.com/documentation/xcode/supporting-universal-links-in-your-app?ref=differentshelf.com) and [Android App Links](https://developer.android.com/training/app-links?ref=differentshelf.com) are designed for one destination per URL. When two apps claim the same routes, both iOS and Android start to guess. Sometimes the link opens one app, sometimes the other, sometimes neither. To fix it, teams carve exceptions and invent subdomains, reintroducing complexity we once removed. The truth is that deep links are literal. They are promises that one address equals one experience. Break that, and every layer above it — push campaigns, affiliate traffic, cross-navigation — starts to lose trust. What was built to sharpen discovery ends up dulling reliability. A deep link is a promise that one address leads to one experience. Players don’t think in listings. They think in moments. Two icons that open the same thing create a question that doesn’t need asking. And if those icons exist only to drop you into different sections of the same app, it is worth asking something else: why can’t the app just remember where you left off? State is cheaper than structure. You can build an experience that reopens exactly where a player last was without fragmenting the store. If context is the reason for duplication, persistence is the cleaner fix. The best entry point is not a second listing, it is continuity. Apple’s ecosystem no longer needs structural workarounds to reach distinct audiences. [Custom Product Pages](https://developer.apple.com/app-store/custom-product-pages/?ref=differentshelf.com) let you create up to 35 tailored storefronts, each with its own visuals and message, all feeding one source of truth. A campaign about matchday odds can open on a sports-themed page, another about free spins can highlight casino content, and both still contribute to a single ranking signal. [Product Page Optimisation](https://developer.apple.com/app-store/product-page-optimization/?ref=differentshelf.com) adds built-in testing for icons, taglines, and screenshots. Results roll into [App Analytics](https://developer.apple.com/app-store/app-analytics/?ref=differentshelf.com) automatically. Winning variants can go live instantly, without duplicating the listing or splitting your data. Apple’s [In-App Events](https://developer.apple.com/app-store/in-app-events/?ref=differentshelf.com) surface live campaigns and promotions directly in search results and on the Today tab. They add freshness to your presence and show Apple that the app is active and evolving. On Android, the same pattern is now possible. [Play Store Experiments](https://play.google.com/console/about/store-listing-experiments/?ref=differentshelf.com) and [Custom Store Listings](https://support.google.com/googleplay/android-developer/answer/9867158?hl=en-GB&ref=differentshelf.com) make it easy to test and tailor without cloning. One listing can adapt to audiences, regions, and acquisition channels. Segmentation has moved from structure to content. You can still talk differently to different people; you just no longer need a different app to do it. If presentation is the difference, vary the presentation rather than duplicating the product. Modern app stores reward clarity. Every click, install, and review feeds a single model. The stronger and more consistent that signal, the more confidently the system can rank, recommend, and feature you. Two identical apps might once have been the smart move. Now they mostly show how long old logic can linger. In product, as in presence, every split comes at a cost. Dilution hides more than duplication ever reveals. ### A general guide to branching in a multi-product monorepo URL: https://www.differentshelf.com/a-general-guide-to-branching-in-a-multi-product-monorepo/ Last updated: 2026-08-24T16:18:13.000Z *Trains used to pass the end of our road. One track, different speeds, one timetable. That is the feeling a healthy monorepo aims for: one trunk people trust, a few sidings when needed, departures you can set your watch by.* One trunk is the shared truth. Release branches record where each surface left it. This guide sets out a branching model for the whole frontend of a product, not just a channel. Web, Android, iOS, and the backend-for-frontend that serves them live together. The gateway shapes API contracts for the clients and belongs next to the code it feeds. When it moves with the apps, refactors get simpler, rollbacks stay boring, and features travel as one story. The promise is speed without chaos. Distributed teams can land changes without ceremony because the rules are clear and automation does the heavy lifting. Coordinate by shared tags, tested contracts, and observability. Let dates vary by surface. It will not fix everything, but it removes friction and gives a steady path to ship across surfaces with fewer surprises. Treat it as a pragmatic default you can adopt as is or tune to fit. ## Table of contents 1. [Why one product, many surfaces](#why-one-product-many-surfaces) 2. [What we mean by the Gateway](#what-we-mean-by-the-gateway) 3. [Branch roles at a glance](#branch-roles-at-a-glance) 4. [Naming is governance](#naming-is-governance) 5. [Versioning without conflicts](#versioning-without-conflicts) 6. [Alignment without lockstep, different cadences, one truth](#alignment-without-lockstep-different-cadences-one-truth) 7. [Invariants that keep you honest](#invariants-that-keep-you-honest) 8. [Scenarios with exact steps](#scenarios-with-exact-steps) - [Gateway contract change, additive](#gateway-contract-change-additive) - [Gateway contract change, planned breaking](#gateway-contract-change-planned-breaking) 9. [Decision table](#decision-table) 10. [CI policy and branch protections](#ci-policy-and-branch-protections) 11. [Metrics that prove it works](#metrics-that-prove-it-works) 12. [Anti-patterns and how to correct them](#anti-patterns-and-how-to-correct-them) 13. [Adoption plan](#adoption-plan) 14. [Reference snippets](#reference-snippets) 15. [Appendix, branch-name regex](#appendix-branch-name-regex) 16. [Conclusion](#conclusion) ## Why one product, many surfaces Users see one product. Shipping together reduces bugs that only appear when journeys cross surfaces. Web can sprint. Native can queue at the store. That is fine. **Coordinate by shared tags, tested contracts, and observability. Let dates vary by surface.** Keeping client code and the gateway close helps. Contracts are visible, refactors land together, rollbacks stay simple. Most importantly, teams in different places can add value without a long trail of approvals because the model makes intent obvious and the rules do the handholding. ## What we mean by the Gateway The **gateway** is a frontend-facing adapter between clients and backend systems, often called a backend-for-frontend. It translates and composes data for the experience. Different cadences need shared provenance, not simultaneous release. Typical duties: shape and aggregate responses for screens, adapt backend models to client contracts, pass through authentication context and enforce simple policies, rate limit and trace, and provide a thin anti-corruption layer so clients are insulated from backend churn. Why keep it in the monorepo: contracts live next to clients so changes are visible, type generation and contract tests run in CI against the same commit, additive changes can ship with web today and flow to native later. This lowers the bar for new contributors who can change a screen and its contract in the same PR, with the pipeline catching mistakes. ## Branch roles at a glance - **main** Always shippable. Green CI. Feature flags hide incomplete work. This is the shared reality that lets any team contribute without asking who owns the calendar. - **release/{surface}/{yy}.{ww}.{build}** One branch per surface, all cut from the same tag on main. Examples: `release/web/25.40.0`, `release/android/25.40.0`, `release/ios/25.40.0`, `release/gateway/25.40.0`. If the gateway is tightly coupled to a web change, include it in the web release. If it benefits from its own cadence, give it a surface of its own. Either way, contributors know exactly where to land fixes. **develop/{vertical}** Short-lived staging when a vertical or service needs to integrate several features before returning to main. Examples: `develop/catalogue`, `develop/offers`, `develop/checkout`, `develop/account`, `develop/marketing`, `develop/platform`, `develop/native-android`, `develop/native-ios`, `develop/gateway`. These sidings give local teams a safe place to coordinate work that needs a small bundle, then return to the trunk quickly.Quick audit: ```bash git branch -r | grep 'develop/' ``` ## Naming is governance Branch names are contracts between people and automation. Enforce them with push rules so policy is syntax, not opinion. Clear names mean new folks can find the right on-ramp without a tour guide. - Feature branches: `f/{area}/{short-title}` Examples: `f/checkout/payment-timeout`, `f/catalogue/new-filtering`, `f/gateway/add-recommended-field` - Develop branches: `develop/{vertical}` Examples: `develop/checkout`, `develop/gateway`, `develop/marketing` - Release branches: `release/{surface}/{yy}.{ww}.{build}` Examples: `release/web/25.40.0`, `release/android/25.40.0`, `release/ios/25.40.0`, `release/gateway/25.40.0` - System branches: `revert-*`, `cherry-pick-*` created by your git hosting UI See the full regex in the appendix. ## Versioning without conflicts Let CI derive versions from time and commit. No brittle version files to touch, which means fewer merge tangles and fewer reasons to block someone else’s work. Format: `{major}.{minor}.{build}.{revision}-{hash}` - `major` is last two digits of the year of the latest commit - `minor` is the ISO calendar week of that commit - `build` comes from branch context - `revision` is minutes since the start of that week for the latest commit - `hash` is the short git SHA Release branches such as `release/web/24.21.0` produce something like `24.21.0.3842-a1b2c3d`. Non-release branches use `build = 0`, for example `25.40.0.9123-f00ba7c`. **Calendar-style versions are identifiers, not schedules.** They tell you when an artifact was produced. They do not force every surface to ship on that day. A develop branch is a siding with an exit date, not a second railway. ## Alignment without lockstep, different cadences, one truth Web, native, and the gateway can move at different speeds. The product stays coherent because everyone aligns on the same truths. That lets a team in one office cut a train while another team is asleep, and nobody steps on toes. Principles: the truth lives on main and every surface cuts from it. Per-surface release branches are normal. Skipping trains is normal. Feature availability is a matrix so use flags per surface. Contract tests prevent surprises. Fast-follow paths are explicit. One set of release notes is grouped by tag. Cheat sheet: | Surface | Typical cadence | Release branch example | Can skip a train | Hotfix path | | ------- | --------------- | ----------------------- | ---------------- | ------------------------------------- | | Web | weekly | release/web/25.40.0 | yes | fix on release/web/..., then main | | Android | 1 to 4 weeks | release/android/25.40.0 | yes | fix on release/android/..., then main | | iOS | 1 to 4 weeks | release/ios/25.40.0 | yes | fix on release/ios/..., then main | | Gateway | as needed | release/gateway/25.40.0 | yes | fix on release/gateway/..., then main | ## Invariants that keep you honest 1. **main stays sacred.** Merge queue on. Required checks include compile, unit, smoke, static analysis, size budgets, and a fast end to end. The queue equalises access so small teams are not stuck behind loud ones. 2. **develop branches chase main daily.** If drift exceeds one day, block merges to that develop branch until reconciled. This protects new contributors from integrating against stale code. 3. **release branches are fixes only.** Scope freezes at cut time. 4. **everything reconciles to main.** When a commit lands on any release branch, open a PR and merge that release branch back into main the same day. One ledger of record. 5. **release branches persist.** Do not delete release branches during their hotfix window. Keep them open and protected. After the window, lock the branch read-only and keep the tags. ## Scenarios with exact steps ### Small, safe feature to main When the change can be flagged and covered by CI. Ideal for local teams delivering small wins without booking a summit. Steps: create `f//`, develop behind a flag, rebase on main daily, open PR to main, pass checks and approvals, merge via queue with the flag off by default. Exit: build green, smoke green, flag off in main at merge time. ### Vertical integration on develop/{vertical} When several features must land together for a vertical or service. Steps: branch from main, rebase daily, PR into `develop/<vertical>` and run vertical e2e there, then raise a short PR from `develop/<vertical>` back to main and merge quickly. Exit: develop branch no more than one day behind main, e2e passing, small PR back to main. Teams coordinate locally without blocking others. ### Shared library change with multiple consumers Steps: branch from main and add a compatibility layer that defaults to old behaviour, land that to main first, migrate consumers on `develop/<vertical>` branches, remove the compatibility layer in a separate PR when all consumers are updated. Exit: CI matrix green and owners approve removal. Smaller teams can modernise libraries without staging a company-wide rewrite. ### Security bulletin Steps: patch on main first, cherry-pick the minimal patch to any active release branches for web, Android, iOS, or gateway, tag new candidates, roll through rings. Exit: forward fix in main, backports recorded in release notes. Clear steps mean any on-call group can act quickly. ### Cut a train for web, Android, iOS, and gateway Steps: ensure main is green and baseline metrics are steady, tag and branch per surface, freeze scope. Result: each surface has a release branch from the same tag. Local teams plan their own rollout while staying aligned. ### Hotfix during validation Steps: create a hotfix branch from the relevant release branch, fix and merge into that release branch, tag `.1`, validate, then open a PR and merge the updated release branch back into main. Result: release fixed, main reconciled via the merge back. No guessing which changes made it. ### Incident after the release is live Steps: branch from the surface release tag or release branch, fix and merge into that release branch, tag `.2`, roll out, then open a PR and merge the updated release branch back into main before the next train. Result: main contains the full set of shipped changes. The next team to cut a train inherits the fix automatically. ### Most recent release housekeeping Steps: once the release is live and validated in production, merge the release branch back to main to capture any hotfixes. Keep the release branch open and protected for the hotfix window, for example 14 to 30 days. When the window closes, lock the branch read-only and leave the tags intact. Result: release branches remain available for quick fixes without cherry-pick roulette. History stays legible. ### Gateway contract change, additive When a client needs a new field or endpoint and older clients must keep working. Steps: implement in `f/gateway/add-<field>`, add contract tests so old and new shapes pass, land gateway change to main first, update web on `f/<vertical>/...` or `develop/<vertical>` and merge to main, let native adopt later in its next release. Result: old clients unaffected, web uses the new field, native catches up later. A single PR can move both sides forward. ### Gateway contract change, planned breaking When a field must be removed or renamed. Steps: add a compatibility window and support both shapes behind a flag, ship gateway with both shapes, migrate web and native, remove the old shape in a separate PR when supported clients have moved. Result: no clients break, the compatibility code is removed on schedule. Teams can change contracts without freeze weeks. ## Decision table | Scenario | Base from | Merge into | Notes | | ------------------------------- | ----------------- | ----------------------------- | --------------------------------------------- | | Small feature | main | main | Default path. Flag if risky. | | Vertical integration | main | develop/{vertical} then main | Siding, not a fork. One day max drift. | | Shared lib change | main | main | Add compatibility, remove later. | | Gateway additive change | main | main | Ship gateway first, clients follow. | | Gateway planned breaking change | main | main | Dual support, migrate, then remove. | | Security bulletin | main | main and release/\* | Forward fix, then backport. | | Release cut | main | release/{surface}/yy.ww.build | Cut web, Android, iOS, gateway from same tag. | | Hotfix in validation | release/\* | release/\* then main | Merge updated release branch back to main. | | Incident after go live | release/\* or tag | release/\* then main | Merge updated release branch back to main. | | Most recent release branch | — | main | Keep branch during hotfix window, then lock. | | Missed train | main | main | Ship next train, not this one. | | Revert | main | main and maybe release/\* | Prefer small, explicit reverts. | ## CI policy and branch protections main is protected and uses a merge queue. Required checks include compile, unit, lint, size budgets, smoke, quick end to end. Codeowners guard shared surfaces and the gateway. The queue gives everyone the same lane to production. develop branches are protected and require an owner approval. A convergence bot must be green. If drift exceeds one day, block merges until reconciled. That keeps new work aligned without coordination meetings. release branches are protected, labelled for risk, and require two approvals. Avoid squash merges and keep ancestry visible for audit. Keep release branches open during the hotfix window and lock them when the window closes. Automation to add: daily convergence from main into each develop branch. A reconciliation check that refuses to lock or close a release branch until all its commits appear on main. Contract tests in CI between gateway and clients. Auto-lock release branches after N days of inactivity, for example 21 days. Do not delete release branches. ## Metrics that prove it works Time to main for feature branches. Develop drift days per vertical or service. Back-merge latency from release branches to main. Revert rate per train. Change-fail rate during ring rollout and time to recover. Bundle size budgets and variance by surface. Contract break count between gateway and clients. Age of gateway compatibility shims. Lead time for first-time contributors. Publish a small dashboard. Reward boring, repeatable flow. ## Anti-patterns and how to correct them Perma-develop. A develop branch that never merges back becomes a shadow trunk. Set an SLA to close or merge within five working days of the first integration PR or file an exception with a plan. Release-only fixes. A fix shipped to release without reconciling main will regress next train. Block the release branch from closing until the back-merge into main lands. Long-running features without flags. If a change spans more than a week, it needs a flag or a develop branch with an exit date. Creative branch names. Breaks automation and ruins auditability. Enforce the regex and decline the push. Gateway breaking change without a window. Removing a field before clients move forces emergency releases. Require a published window and link the removal PR to client migrations. ## Adoption plan Week 0\. Turn on naming enforcement. Create develop branches for a small set of verticals and services, including `develop/gateway`. Publish a short contribution guide so new teams can self-serve. Week 1\. Enable the merge queue on main. Add the convergence bot from main to develop. Turn on contract tests between gateway and clients. Week 2\. Cut your first set of release branches for web, Android, iOS, and gateway from the same tag. Validate the reconciliation check that guards main. Week 3 to 4\. Measure time to main, drift days, back-merge latency, contract breaks, and first-time contributor lead time. Share results weekly. Reduce develop usage as teams learn to flag. Quarter end. Review develop branch inventory. Remove stale sidings. Tighten budgets and checks. Remove old gateway compatibility shims. Update the contribution guide with the best examples from the quarter. ## Reference snippets List all develop branches ```bash git branch -r | grep 'develop/' ``` Cut a release set ```bash git switch main && git pull git tag -a v25.40.0 -m "Cut week 40" git push origin v25.40.0 git switch -c release/web/25.40.0 && git push -u origin HEAD git switch main && git switch -c release/android/25.40.0 && git push -u origin HEAD git switch main && git switch -c release/ios/25.40.0 && git push -u origin HEAD git switch main && git switch -c release/gateway/25.40.0 && git push -u origin HEAD ``` Daily convergence job, pseudo ```bash for b in $(git branch -r | grep 'develop/' | sed 's|origin/||'); do git fetch origin git checkout $b git rebase origin/main || exit 1 git push --force-with-lease done ``` ## Appendix, branch-name regex ```regex ^((cherry-pick|revert)-[a-zA-Z0-9-]+) |^(develop\/[a-z-]+)$ |^(release(-candidate)?\/([a-z-]+)\/2[4-9]\.(?:0[1-9]|[1-4]\d|5[0-3])\.(?:\d+|x)$) |^(f\/[a-zA-Z0-9\/-]{6,35})$ ``` ## Conclusion This is not a monolith. It is choreography. One trunk gives a shared reality. Short-lived develop branches let verticals coordinate and still move fast. Per-surface release branches respect different cadences across web, native, and the gateway. Calendar versions and clear names make history legible. Contract tests and a simple merge-back rule keep truth aligned with what shipped. The shape is friendly to contribution. A small team in one office can add a screen, tweak a contract, and ship on the next train without waking anyone else. Fewer surprises, higher pace, steadier quality. ### Fast, Loose, and Fragile URL: https://www.differentshelf.com/fast-loose-and-fragile/ Last updated: 2026-08-24T16:18:13.000Z *The web is like an unlocked car. Anyone can get in and drive, but you still should wear a seatbelt.* The web’s strength has always been its looseness. You can shape it, stretch it, splice it together. It tolerates a degree of chaos that would break other platforms. That flexibility is part of its magic, and part of its curse. In a native app, code lives behind a wall. You build, sign, release. Every change moves through a visible lifecycle. Some frameworks, like React Native or Flutter, blur that boundary and allow faster iteration. But even there, it is far harder to alter behaviour on the fly. That separation is both a blessing and a curse. It slows you down, but it also protects you from yourself. On the web, the wall is porous. Code can slip in through a CMS, a tag manager, or a partner integration. It works just enough to be convincing, but not enough to be safe. It feels fast. You “just add a script”, see it appear, and move on. But that kind of speed comes without scrutiny. Injected code passes through no review, no static analysis, no type checking. It is invisible to test coverage and caching strategies. It sidesteps the release pipeline that exists to protect the player. What looks like agility is usually a form of bypass. Code that bypasses the delivery system also bypasses its ownership. And often, the person adding it is outside the normal product development cycle. They might be working in a CMS, unaware of how far the change will reach or how to verify it. A snippet intended for one brand or locale can quietly spread across environments through shared templates or caching layers. Without review or observability, there is no easy way to know where the code landed or what it did once it got there. Once those fragments run, they ignore the normal lifecycle of the application. They can attach listeners without removing them, allocate memory without release, or mutate the DOM during paint. Over time, the page starts to falter. Frames drop, transitions shudder, and the experience feels slightly off. Jank is how players describe it. The cause is almost always the same: work that was meant to be temporary has become permanent. In [*Watching the Watchers, Part 2*](https://www.differentshelf.com/watching-the-watchers-part-2-how-scripts-break-your-frame-budget/), I wrote about how external scripts quietly eat into the frame budget. Each watcher and polling loop competes for the same 16-millisecond window that keeps motion smooth. Injected code does not just slow the interface; it changes its rhythm. Memory leaks deepen the problem, holding references to nodes and listeners long after they should have been released. You can often see the symptoms in RUM. INP rises, long tasks cluster, heap usage grows. What you cannot easily see is ownership. The script never went through the normal process, never appeared in a changelog, and never had a clear maintainer. By the time dashboards turn red, the code has drifted out of memory and out of mind. People rarely do this out of carelessness. They are trying to move quickly, to meet a campaign date, to ship something small without the overhead of a full deployment. When the main path feels slow, the side door starts to look reasonable. In truth, these shortcuts are a signal. They point to where process has become friction. The right path must become easier before the wrong path can be closed. The answer is not to lock everything down. It is to make the right path easier than the wrong one. If a team can propose, review, and deploy change within hours, they will not reach for an injection. If experiments have a safe and visible framework, they can run without leaving debris behind. The web does not need fewer hands. It needs steadier ones, and systems that help them move quickly without breaking trust. ### Monkey Dust and the Pale Blue Room URL: https://www.differentshelf.com/monkey-dust-and-the-pale-blue-room/ Last updated: 2026-08-22T22:55:19.000Z *The glow of late-night television once carried a kind of danger. When the rest of the house was asleep, you could stumble on something illicit, anarchic, and half-imagined. It wasn’t designed for you, but it found you anyway. Monkey Dust was that kind of show: a fever dream broadcast through the back-channels of early digital Britain, bleak enough to unsettle, funny enough to stick, and strange enough to feel like you’d imagined it the next morning.* I first discovered *Monkey Dust* not in Britain, but from a farmhouse in the south of France. I was in my early twenties, and me and my buddy were building a startup in that house while renovating it at the same time. Two halves of the same life: plaster dust and cracked walls by day, glowing screens and precarious ambition by night. The place was half building site, half home. The walls were thick stone, plastered in something that seemed more like mud than plaster. Days were spent filling cracks, smoothing surfaces, preparing them for paint, long painful days where the walls seemed to swallow more filler than they gave back. Then, without really stopping, we would pivot back into startup mode: crude prototypes, long emails over dial-up, ambitions bigger than the house itself. By a twist of misjudged politeness, I had recently built a website for a company selling digiboxes and, instead of money, ended up being paid in digiboxes. Ten of them, maybe twenty, I’ve lost track. Some were Amstrads at the bargain end, others carried the badges of so-called premium makers like Pace or perhaps even Panasonic. There was talk of me selling them on to other Brits in the south of France, but that was never going to happen. I could barely bring myself to admit I’d accepted them in the first place. ![A still from Monkey Dust showing a bearded man and a uniformed official facing a bank of microphones.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2025/09/monkey-dust-x.png) I knew I’d been had, though I tried to look on the bright side: with enough boxes and a multi-output LNB hanging off the dish, everyone in the house could have their own satellite TV. It almost looked like I had planned it. The dish itself hung outside my window, heavy and awkward, cables spilling into the rooms it fed. Four CRTs flickered to life, each tuned to its own slice of British TV, beamed illegally in from orbit. The farmhouse no longer felt like a renovation project or a half-built startup. It looked like a clandestine broadcast centre, patched together from plastic boxes I hadn’t wanted in the first place. My room had little else. A portable CRT, a pile of surplus digiboxes, and a few Kylie Minogue calendars, her early-noughties comeback frozen in glossy smiles, presiding over mud-filled walls. The walls themselves were painted pale blue, baby blue really, a colour chosen in haste and never repainted. They are still that colour today. ![A still from Monkey Dust showing a commuter passing a row of televisions and sale signs.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2025/09/monkey-dust-3.png) And it was there, after long days choking on plaster dust and long evenings of coding offline (imagine!), that I stumbled across *Monkey Dust*: a show so bleak and surreal it felt like a hallucination, piped illicitly from Britain into the French night. On paper, *Monkey Dust* was a sketch show: animated shorts stitched together in a half-hour slot. In practice, it was closer to a cultural séance. Recurring figures weren’t really characters so much as avatars of decline: Omar and his West Bromwich terror cell carefully planning jihad around *Stars in Their Eyes* and Angel Delight; Ivan Dobsky, the “Meat-Safe Murderer,” forever insisting he hadn’t done it; the Paedofinder General, spotting paedophiles in *Fiddler on the Roof* or a boy playing with his dog; the Homeless Poet mumbling bleak laments into the void; the coke-addled media executive raving at warp speed; and Jerry Berman, the sleazy PR man, forever rebranding disgrace into “opportunity.” Most sketch comedy works by building tension and then letting you exhale with a punchline. *Monkey Dust* refused to give you that oxygen. Jokes would spiral downward without catharsis. Punchlines were often non-punchlines: suicide, degradation, corruption. ![A shadowy figure in a broad hat gestures in a dark still from Monkey Dust.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2025/09/monkey-dust-4.png) In one recurring sketch, a young office intern would quietly open a window and step out, plummeting to her death while colleagues barely noticed. There was no setup, no payoff, just the bleak repetition of collapse, until it became funny again in the way gallows humour sometimes is. Even family life was fair game. One strand followed a divorced father desperately trying to please his son. Each time the boy rejected him with cutting spite, until the father killed himself. Only then would the son admit he had wanted what was offered all along, and had just been told to say otherwise. Most of these loops never broke, which made them feel like nightmares replaying on a tape. Ivan Dobsky was released at the start of every sketch, only to immediately commit another crime and find himself back where he began, still insisting *“I didn’t do it, I only said I did.”* The father-son strand would sometimes sharpen into a cruel full stop. Endings, when they came, were never a relief — they were bleaker than the loops themselves. ![A still from Monkey Dust showing a woman in pink opening curtains between two police officers.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2025/09/monkey-dust-11.png) When you watch it back now, the variety of animation styles is impossible to miss. Some sequences look scrawled and jittery, others more painterly, others like crude cut-outs. The differences are stark, yet you realise how little it matters to the message. Lo-fi often suited the material. I’ve always assumed this patchwork look came from splitting the work between different studios, maybe even to get the show made more quickly. I don’t know that for a fact. It’s just what I inferred watching it — the seams felt visible, but they were part of the effect. Whether it was deliberate or pragmatic, it worked. That fractured aesthetic became part of the DNA: a reminder that the grotesque doesn’t need gloss. Lo-fi was not a lack of craft. It was the surface the material needed. The music gave it another dimension. *Monkey Dust* didn’t settle for stock loops or cartoon stings. It curated a soundtrack as deliberate as the writing: Goldfrapp, Eels, Aphex Twin, Saint Etienne. Pulp’s *Sunrise* was used in one of the longer montages, the instrumental build looping as the animation descended into chaos. No Jarvis Cocker vocal, no lyrical guide, just guitars and strings straining upwards while the visuals sank further down. For me it carried added force because Pulp have always been my favourite band. ![Three people sit stiffly around a table with wine and coffee in a still from Monkey Dust.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2025/09/monkey-dust-7.png) Sometimes the choices were sly rather than grand. In one sketch, a North London dinner party serves up the head of a tramp as a delicacy, all while David Gray plays softly in the background. The bland crooning undercut the horror and made it bite harder. *Monkey Dust* understood that music could be part of the joke, part of the wound, part of the aftertaste. The cruelty was democratic. Politicians and media barons got it, but so did hoodie-wearing lads, pseudo-intellectuals, Geoff the first-time cottager, the Mail-Order Bride Man, Alex the serial dater, even the Nazi Grandfather. And then there were the lazy gags, like the “women reading a map” sketch — crass, misogynistic, but of a piece with the show’s refusal to draw safe lines. Some jokes landed so hard they left aftershocks. I still can’t give my own kids a Mr Hoppy space hopper. If you watched *Monkey Dust* you’ll know why. Once that innocent orange toy had been dragged into the show’s orbit of grotesques, it stopped being innocent forever. ![A still from Monkey Dust showing a suited man sitting on a large orange head in a dark street.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2025/09/monkey-dust-2.png) One of the sharpest sketches showed Omar’s mum fussing over his jihadist plans as though they were nothing more unusual than football practice. She knew exactly what he was plotting but folded it into routine: Findus Crispy Pancakes for tea, Angel Delight for pudding, and a reminder not to schedule martyrdom during the *Stars in Their Eyes* final. Omar’s cell weren’t just distracted by TV talent shows or cheap puddings. Sometimes it was West Bromwich Albion fixtures, and in those days Russell Hoult was the lauded shot-stopper. Hoult died only weeks ago, aged fifty one. Seeing his name in the news pulled me straight back to that sketch. Absurdity and tragedy still bleeding into one another. That mix of jihad and Angel Delight has echoes in Chris Morris’s *Four Lions*. *Monkey Dust* aired between 2003 and 2005, before the London bombings of July 2005\. *Four Lions* appeared in 2010, written in the shadow of 7/7\. Morris’s film had to reckon with real horror, while *Monkey Dust* had satirised the same themes in the years just before, treating them as grotesque absurdity rather than aftermath. Seen together, the two works feel like parts of the same conversation: one anticipating, the other responding. ![A still from Monkey Dust showing a woman carrying a tray with a red jelly and egg cups.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2025/09/monkey-dust-6.png) Other sketches read now like previews of the present. The Newsletter Man thrusting leaflets at uninterested strangers feels eerily like today’s corporate email culture. Ivan Dobsky’s endless denials look like modern politics. Jerry Berman’s staged redemption arcs are influencer apology tours. The coke-addled executive has become the content algorithm. The Paedofinder General is every comment-thread zealot, convinced he is saving civilisation one denunciation at a time. For those who never caught it, the closest comparisons only get you part of the way there. It had the queasy, relentless bleakness of Chris Morris’s *Jam*, the bile and prescience of Charlie Brooker’s early columns, and a whiff of *League of Gentlemen’s* grotesques. But where those shows felt scripted, even theatrical, *Monkey Dust* was a scattershot hallucination: an animated *Brass Eye* stripped of studio polish, closer to a bad dream than a comedy. The nearest modern equivalents such as *South Park* or *Rick and Morty* still look glossy by comparison. Britain only brushed against this form once, and then it disappeared. Harry Thompson, who co-created the series, died of lung cancer in November 2005, aged just forty five. His death sealed *Monkey Dust* in amber. Shaun Pye, the other co-creator, went on to write and produce other sharp work, but *Monkey Dust* remains his most uncompromising creation. Between them, Thompson and Pye smuggled onto the BBC something that felt more like a pirate transmission than a comedy show. ![Four women talk in a crowded, green-lit bar in a still from Monkey Dust.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2025/09/monkey-dust-9.png) Three series aired, then nothing. No reruns, no streaming deals, no revival. In an era when everything is endlessly recycled, its absence feels shocking. Music rights probably buried it, but so did fear. Television no longer wants the risk of something so poisonous. *Monkey Dust* survives now only in fragments, uploads, poor-quality rips. Some culture survives because nobody can make it respectable enough to preserve. And yet its sketches still linger. Ask anyone who watched it two decades ago and they can probably still quote Ivan Dobsky, picture the tramp-head dinner party, hear the intern’s window opening. The images and catchphrases became cultural shrapnel. Even twenty years later, most Brits who saw it would still recognise them instantly. I still watch it back. Even after dozens of viewings, sketches I’d forgotten resurface like half-buried corpses. Each rediscovery hits as hard as the first time. I could go through them all, but part of the point is stumbling on them yourself. *Monkey Dust* is not easy to find now, but the fragments are out there if you look. Just be warned: the show will get under your skin. And if ever there was a legitimate case for a trigger warning, this might be it. ![The Monkey Dust title appears over an animated park scene.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2025/09/monkey-dust-1-1.png) ### The Tide of Review URL: https://www.differentshelf.com/the-tide-of-review/ Last updated: 2026-08-22T22:55:19.000Z *The sea moves whether you are ready or not. Twice a day the tide comes in, twice a day it goes out. The pattern is steady, the timing reliable, and over time it shapes the coast. Code review should work the same way. Without rhythm it is a scramble, with rhythm it becomes a tide.* Too often review is treated as overhead. Pull requests pile up, reviewers are pinged at random, and feedback arrives days late or not at all. The result is either rubber-stamping to unblock work or sprawling changes that stall until tempers fray. Neither is excellence. Excellence is predictable flow: work that moves at the right cadence, in the right size, with the right attention. Review is a flow system with human attention as the constrained resource. ## Mechanics: channels and flow The mechanics of that flow begin with ownership. Google never merges a change without at least one code-owner approval[1](#user-content-fn-1). The rule sounds bureaucratic, but it has the opposite effect. When ownership is explicit, authors know exactly who will review, and reviewers know what they are accountable for. No more guessing, no more desperate pings. Cross-cutting work can be routed with labels like “security” or “migration” that automatically summon the right specialists. Ownership gives the tide a channel. Size keeps that channel clear. A 1,200-line pull request is a beached whale. Empirical studies show review latency grows with change size[2](#user-content-fn-2). Google and Meta encourage small, reviewable changes, typically 200 to 400 lines, focused on one intent[1](#user-content-fn-1)[3](#user-content-fn-3). Larger work is stacked: a refactor, then the feature, then a cleanup. Each part lands quickly, each easy to review. This echoes what I argued in [*Modern Engineering Practices*](https://chatgpt.com/modern-engineering-practices?ref=differentshelf.com) and [*Release Trains*](https://chatgpt.com/release-trains?ref=differentshelf.com). Shipping small is not a trick, it is the difference between drift and control. Automation clears the debris. Static analysis has long caught trivial issues[4](#user-content-fn-4). AI can now summarise diffs, suggest test coverage, and flag risky areas[5](#user-content-fn-5). None of this replaces judgement, but it removes noise so reviewers can focus on design, intent, and risk. The tide does not wait for someone to pick litter off the sand. Machines do that so the flow is uninterrupted. Automation should remove review noise, not human judgement. ## Culture: rhythm and signals But mechanics alone cannot hold the rhythm. Even the tide fails if people ignore the signals. Culture determines whether review feels like learning or punishment. Conventional Comments give reviewers a shared language: `blocking:`, `suggestion:`, `nit:`, `question:`, `praise:`[6](#user-content-fn-6). Authors no longer wonder if a remark is required, and reviewers can highlight what is good as well as what needs fixing[7](#user-content-fn-7). The tide is calmer when everyone reads the signals the same way. Timing reinforces culture. Reviews should not trickle in as random interruptions. They should happen like the tide — twice a day, everyone knowing when the pull will come. In those windows, engineers batch their review work. Outside them, they can focus without constant pings. Some teams add a weekly “review lead” to keep the queue healthy. It is not glamorous, but it stops the drift. Consider one team building a new search feature. They once opened a single 1,200-line pull request mixing refactor, backend queries, and UI wiring. It sat untouched for two days, then drew dozens of scattered comments. The author rewrote large chunks and merged late with defects. Under a stacked approach, the same work split into three smaller pull requests: a 200-line refactor, a 350-line backend change, and a 150-line UI patch. Each carried a clear test plan. Each was reviewed within hours during scheduled tide windows. All landed within two days, cleaner and safer. The difference was not the code. It was the tide. ## Measurement: listening back Culture sets the rhythm, but you cannot keep time if you never listen back. Measurement makes the tide audible. First-response time should be measured in hours, not days[1](#user-content-fn-1). Time to merge and merge latency should be tracked, because studies show much delay happens after approval, waiting for integration[8](#user-content-fn-8). Teams should see their review cycles, their pull-request size distributions, and overlay DORA metrics such as lead time, deploy frequency, change-failure rate, and time to restore[9](#user-content-fn-9). If the numbers slip, the rhythm is off. Without measurement, the tide is invisible and the coast erodes unnoticed. Examples show this is practical. Google trains reviewers with “readability” certification: you cannot approve code in a language until you prove you can mentor others in it[1](#user-content-fn-1). Meta requires every engineer to state a test plan in their review submission, forcing thought about risk before anyone else sees the change[10](#user-content-fn-10). Open-source research shows reputation and trust shorten review latency[8](#user-content-fn-8). Different contexts, same lesson: review is not a gate, it is a flow. ## The honest trade-off Here is the honest part. None of this is free. Small changes mean authors must split their work with discipline. Scheduled windows mean resisting the urge for instant feedback. Metrics mean confronting uncomfortable truths about bottlenecks. But the alternative is worse: ad hoc reviews, random pings, mega pull requests. Drift instead of rhythm. Jam instead of tide. If review remains ad hoc, you will always be interrupted, always juggling context, always frustrated. Design it as a predictable system — with clear ownership, small changes, shared language, tide windows, machine guardrails, and honest measurement — and it becomes the opposite of friction. It becomes the tide of modern engineering: twice a day the pull comes, work flows in and out, and over time it shapes the entire coast. *Ship small. Review fast. Be kind. Measure flow. Trust machines to catch lint so humans can catch design.* *The tide waits for no one, but it carries everyone.* ## Footnotes 1. Google Eng-Practices: *Speed of Code Reviews* and *How to do a Code Review*. [↩](#user-content-fnref-1) [↩2](#user-content-fnref-1-2) [↩3](#user-content-fnref-1-3) [↩4](#user-content-fnref-1-4) 2. Yu et al. *Predicting Code Review Completion Time in Modern Code Review*. arXiv, 2021\. [↩](#user-content-fnref-2) 3. Graphite Engineering Blog: *Stacked Diffs* and PR size guidelines. [↩](#user-content-fnref-3) 4. Johnson et al. *Static Analysis: An Empirical Survey of Industrial Usage*. IEEE Software, 2013\. [↩](#user-content-fnref-4) 5. Barke et al. *Grounded Copilot: How Programmers Interact with Code-Generating Models*. arXiv, 2023\. [↩](#user-content-fnref-5) 6. Conventional Comments specification: [conventionalcomments.org](https://conventionalcomments.org/?ref=differentshelf.com). [↩](#user-content-fnref-6) 7. Atwood, J. *Stop Fighting in Code Reviews*. Dev.to, 2020\. [↩](#user-content-fnref-7) 8. Kononenko et al. *Mining Code Review Data to Understand Waiting Times Between Acceptance and Merging*. arXiv, 2022\. [↩](#user-content-fnref-8) [↩2](#user-content-fnref-8-2) 9. Forsgren et al. *Accelerate: The Science of Lean Software and DevOps* (DORA metrics). [↩](#user-content-fnref-9) 10. Phabricator/Meta Engineering Guide: mandatory “Test Plan” fields. [↩](#user-content-fnref-10) ### Beyond Squads: The Case for Centres of Excellence URL: https://www.differentshelf.com/beyond-sqauds-the-case-for-centres-of-excellence/ Last updated: 2026-08-22T22:55:19.000Z *Every muscle in the body can contract on its own. Squads are the muscles of an organisation, powering features and outcomes. Guilds are the nervous system, transmitting signals, sharpening reflexes, and helping the body learn. But muscles and nerves alone cannot hold a body together. Without a spine to support and coordinate them, the movement collapses. Centres of Excellence can be that spine, providing the strength and stability that autonomy alone cannot.* ## Squads and Autonomy The product operating model puts squads at the centre. They are cross functional, durable, and autonomous. They own outcomes, not outputs. *You build it, you run it, you own it* is a corrective to the bottlenecks and handoffs of project delivery. But muscles can only contract. They need support to coordinate. Left alone, they twitch in isolation. Autonomy without connection is incomplete. ## Guilds and Knowledge Sharing Guilds are the nervous system. They pass signals across the organisation. A testing guild swaps patterns for stabilising pipelines. A design guild shares tokens and typography. A developer experience guild refines tooling and process. A guild spreads knowledge. A Centre of Excellence carries weight. Inner sourcing extends this idea. Frameworks and libraries opened to contribution allow squads to improve shared assets as if they were open source. A bug fixed in one context becomes a benefit for all. This works well for incremental improvements and small fixes. Signals and contributions matter. But nerves alone cannot hold weight. A guild can spread awareness and an inner source model can capture contributions, yet neither will consistently solve the deepest problems. Knowledge moves, but energy does not. ## When Centres of Excellence Need to Do the Work Centres of Excellence are often described as enablers, creating dashboards, libraries, and paved paths so squads can move faster. That helps, but systemic problems rarely move through enablement alone. Some issues demand ownership and direct intervention. Autonomy scales only when someone owns the work every squad can postpone. Performance is the clearest case. A memory leak is not a one-off bug. It takes deep expertise to profile the heap, trace the leak, and prove the fix. Even then, the answer is often not a patch but a new pattern that requires refactoring code across the entire base. No single squad will take on that work. It is too large, too invisible to players in the short term, and too easy to defer in favour of features that promise more immediate business value. Inner sourcing can help with smaller contributions. A squad that optimises a flow or patches a dependency can share that fix and spread improvement. These contributions are valuable. But systemic performance issues are different. They demand sustained focus, broad coordination, and expertise that does not exist in every squad. They require not just a fix, but the discipline to apply it across every code path, every product, and every brand. They often require research too: benchmarking, profiling at scale, and experimentation with patterns that may disrupt existing code. Action matters, but without investigation the wrong fixes spread quickly. Squads under pressure to ship features rarely have the mandate for this depth of work. In theory every squad could do this work. In practice it rarely happens at scale. Players do not see the benefit immediately, business priorities push features to the front, and developers feel the weight of complexity without the air cover to act. Teams do improve over time, and some do take on systemic problems when conditions allow. But the pace is uneven, and the gaps left in the meantime can accumulate into serious issues. Centres of Excellence exist to carry the deep, wide work that cannot wait for every squad to reach maturity. ## Why Organisations Resist Organisations often resist Centres of Excellence. Sometimes it is cost: why fund a team when squads could, in theory, solve their own problems? Sometimes it is fear: many remember central teams that turned into gatekeepers. And sometimes it is optimism: given enough cycles, squads do improve. These concerns are valid. Cost matters. Gatekeeping must be avoided. Squads do improve when given space. But can you afford to wait? In a large organisation, drift compounds. Every month without a spine is a month where performance slips, pipelines clog, and compliance weakens. Certainty today often matters more than waiting for every squad to reach maturity tomorrow. ## Avoiding Learned Helplessness If Centres of Excellence do too much of the work, squads may start to abdicate. *Why debug the memory leak if the performance team will eventually fix it?* That way lies centralisation in disguise, where responsibility drifts upward and autonomy becomes hollow. The answer is not to retreat from CoEs, but to design them carefully. They should tackle the hardest systemic work while still ensuring squads remain accountable for their outcomes. Three practices make this balance possible. Engineers should rotate between squads and CoEs so knowledge flows both ways. Responsibility should always return to squads once the fix is made. And adoption should be the measure of success. A tool or library unused is wasted effort. A process that spreads across squads is impact. Stewardship should return capability to squads, not collect dependency at the centre. Some squads will still neglect performance or testing, hoping a core team will step in. When that behaviour repeats, it is a signal that product and engineering leadership need to reset expectations. Sometimes that means re-education, sometimes firmer prioritisation, but always with the goal of reinforcing shared responsibility. *The spine provides structure, but the muscles must still move. If the muscles stop, the whole body weakens.* ## Where CoEs Belong Placement matters. A performance CoE belongs in the frontend framework domain, where shared libraries live. A developer experience CoE sits under platform or enablement, improving pipelines and tooling. Accessibility and i18n can sit with design, but must contain engineers who ship code, not only write guidance. *Place each CoE where its work naturally connects to the systems it needs to change.* ## Lessons from Others Other organisations have already confronted this tension. Amazon is known for two pizza teams, but those teams sit on top of a thick layer of central services. AWS formalised this with **Cloud Operations and Platform Enablement (COPE)**: product teams run what they build, but platform teams provide secure defaults and reference architectures ([AWS COPE](https://docs.aws.amazon.com/wellarchitected/latest/operational-excellence-pillar/cloud-operations-and-platform-enablement.html?ref=differentshelf.com)). The lesson is clear: autonomy only scales when central teams make the deep, hard work repeatable. Atlassian has reached a similar balance. Their engineering handbook describes “radically autonomous, aligned teams,” showing how autonomy is possible only when underpinned by shared systems and practices ([Atlassian Engineering Handbook](https://www.atlassian.com/blog/atlassian-engineering/handbook?ref=differentshelf.com)). Their public **Team Playbook** codifies these practices, creating paved paths that give squads freedom without fragmentation ([Atlassian Team Playbook](https://www.atlassian.com/team-playbook?ref=differentshelf.com)). The UK’s Government Digital Service offers another example. The **GOV.UK Design System** is often held up as a model of federated contribution. Yet it only works because a small central team curates contributions and fixes issues directly ([GOV.UK Design System](https://design-system.service.gov.uk/?ref=differentshelf.com)). Without that spine, the design language would fork endlessly. Nathan Curtis captures this point directly in [*The Fallacy of Federated Design Systems*](https://medium.com/@nathanacurtis/the-fallacy-of-federated-design-systems-23b9a9a05542?ref=differentshelf.com). His argument is blunt: a purely federated model is a fallacy. When responsibility is spread thinly across busy squads, invisible work stalls. Contributions are valuable, but they only flourish when a central team carries the core. Research points in the same direction. McKinsey has written about the “product and platform operating model,” showing that the highest performing organisations explicitly invest in enabling and platform functions alongside product squads ([McKinsey on product and platform models](https://www.mckinsey.com/capabilities/mckinsey-digital/our-insights/modern-technology-operating-models-for-companies?ref=differentshelf.com)). Each of these cases carries the same lesson. Federation works best when it is anchored by a staffed, central team that takes responsibility for the deep, systemic problems. Autonomy thrives, but only because a spine exists to hold it together. ## Rules of Thumb - *When neglect risks creating invisible debt that later explodes, it may be better placed with a Centre of Excellence.* - *When variation across products genuinely adds value, it is usually best left with the squads.* - *When the work is best spread through stories and peer learning, a guild or inner sourcing can be enough.* - *When a problem demands sustained expertise, broad coordination, or structural refactoring across the codebase, it often belongs with a Centre of Excellence.* - *When benefits are invisible in the short term but compound over time, a Centre of Excellence can carry the work until squads catch up.* - *CoEs work best when placed where their efforts naturally connect to the systems they need to change.* - *Stewardship should not mean substitution. CoEs may need to fix the hardest problems, but responsibility should still return to squads.* - *If neglect repeats, it is more a signal of a leadership challenge than of a CoE challenge.* ## Completing the Model The product operating model has transformed how software is built. Squads, durable and autonomous, are a better unit of delivery than the projects and programmes they replaced. But the model was never the whole story. It describes how to deliver features, not how to sustain performance, pipelines, or developer experience at scale. What is needed is not a rival model but a completion of the original. Squads at the core. Guilds and inner sourcing to share practice. Centres of Excellence as the spine that provides structure and clears the blockages no one else will. That balance keeps design honest, pipelines unblocked, reviews flowing, apps fast, and outcomes resilient. Some of the hardest problems will require research as well as delivery, and that research is part of the work. Action clears blockages, but deep thought reveals new patterns. The product operating model does not need defending. It needs finishing. ### A Modern Approach to Engineering URL: https://www.differentshelf.com/a-modern-approach-to-engineering/ Last updated: 2026-08-22T22:55:19.000Z *Turning a ship is not about sudden acceleration but about steady momentum. The rudder moves, the hull leans, and the course shifts slowly at first, then decisively. Engineering at scale feels the same: hard to start, but once it turns the force is unstoppable.* The reality of building at this scale looks nothing like the project plans of the past. Markets move daily, competitors release constantly, and players expect experiences that are fast, reliable, and trustworthy. Delay is expensive, not only in revenue but in relevance. The highest-performing organisations such as Google, Meta, Netflix, Amazon, and Microsoft have proven models for how to meet this reality. They release continuously, integrate daily, treat data as a product, and design for resilience. Netflix tests failure in production with Chaos Monkey. Amazon deploys thousands of times a day. Google runs a single trunk-based monorepo that forces integration and consistency across tens of thousands of engineers. Microsoft shifted Azure to trunk-based development to break the drag of long-lived branching. These are not experiments. They are the operating models behind some of the most resilient and profitable companies in the world. A practice is not modern because a famous company uses it. Evidence must still justify the local choice. Engineering organisations with decades of history face different challenges from startups. Systems are heavier, habits are more ingrained, and progress can feel slower. They are ships, not speed boats. But when a ship turns, its momentum carries far. Modern engineering is not about discarding rigour. It is about putting rigour where it matters. Safety and speed are not opposites. They reinforce each other when teams build continuously. Modern engineering puts rigour in the feedback loop, not in the delay before it. ## Player and Product Outcomes Players do not care about planning frameworks. They care about what works in their hands. Amazon’s “working backwards” principle is a reminder that value is only real when players can use it. A working login, a smooth bet, a faster navigation — these matter more than polished plans that never leave the building. Quality is proven by use. Facebook’s shift from “move fast and break things” to “move fast with stable infra” captured the balance. Speed is essential, but so is trust. Measurement is how you know if you have succeeded. Netflix runs thousands of experiments every year because evidence beats assumption. With feature flags and controlled rollouts, the same discipline is available to any team. Privacy and trust are engineering outcomes, not compliance afterthoughts. Apple has shown that building for privacy can become a competitive advantage. Discovery is as important as delivery. Spotify’s prototypes and staged rollouts show how testing early reduces wasted effort. The point is clear: product, design, and engineering must work together as one, owning outcomes collectively. ## Developer and Technology Outcomes For engineers, the way of working defines not just what gets delivered but the kind of engineer they become. Google’s trunk-based monorepo shows that integration at scale is not only possible but powerful. Engineers who learn to work this way — integrating continuously, reviewing quickly, keeping main always releasable — gain skills that are valued in every leading engineering organisation. Microsoft moved Azure to trunk-based development because long-lived branches slowed delivery. The lesson is clear. Shorter feedback loops are safer and more effective. Contribution must be open. Microsoft, PayPal, and GitHub all built cultures where anyone can propose changes across team boundaries. Guarding code and blocking outsiders does not protect. It isolates. Engineers who embrace inner sourcing grow influence and prove themselves against the standards of the best organisations in the world. Platforms multiply impact. Google’s Borg became Kubernetes because shared infrastructure reduced toil and unlocked focus on product. AI should be treated the same way: not as a threat but as a force multiplier. Reliability must be designed in. Netflix’s Chaos Engineering turned failure into a strength. Monitoring, alerting, and observability are not chores. They are part of the craft. Architecture must evolve with teams, as Spotify has shown. Data must be treated as a product, as LinkedIn does, because reliable data compounds value everywhere else. ## Commercial Outcomes DORA’s research is unambiguous. Elite performers release more often, recover faster, and fail less. They are also more profitable. Speed to market is not just an engineering virtue. It is a commercial one. Amazon’s two-pizza teams show how independence drives speed. Netflix invests in resilience so failures do not become churn. Google bakes compliance into CI/CD pipelines so that audits accelerate rather than delay. Microsoft reduced waste by enforcing observability and accountability for cloud spend. The commercial case is the same across all of them: flow through systems is flow through the business. For engineers, this connection matters. The practices that make someone a better engineer — short feedback loops, measurable outcomes, resilient systems — are also the practices that tie directly to business results. Working in large batches and hiding behind process is not protection. It is exposure. The old way will be found out sooner or later. ## Organisational Outcomes Agile should mean intent, not ritual. Atlassian has been open about this. Sprints are tools, not cages. What matters is continuous delivery. No project is a special case. GitHub scaled by applying the same contribution model across every project. Special cases are usually habits, not necessities. Failure is inevitable. Etsy proved that blameless postmortems turn failure into organisational learning. Fear makes teams brittle. Learning makes them resilient. Leaders shape culture. Amazon’s leadership principles emphasise removing blockers and focusing on outcomes. Growth is essential. Google offers dual career tracks because engineers should not have to leave the technical path to advance. Distributed work is reality. Automattic shows that async, written-first practices can work at global scale. Spotify shows that aligning teams with system boundaries reduces friction. Microsoft and GitHub’s SPACE framework reminds us that productivity is not just throughput. Satisfaction, collaboration, and flow matter as much. Sustained performance requires balance. ## Final Note and Call to Action This is not theory. It is the proven way to build software that players trust, businesses rely on, and engineers are proud of. Google shows the power of trunk-based monorepos. Amazon shows the impact of rapid, independent deployment. Netflix shows how resilience grows from testing failure. Microsoft shows the scale of inner sourcing. DORA’s research shows that elite performers are not only faster but safer and more profitable. For developers, the choice is clear. The practices of modern engineering are not only better for the companies adopting them. They are better for the people doing the work. They make you more valuable, they accelerate your growth, and they prepare you for the expectations of the best organisations in the world. Working the old way will not protect you. It will hold you back. Modern engineering only works if everyone takes part. It demands discipline, openness, and courage. The reward is faster learning, stronger systems, and better experiences for players. If you are unsure, ask. If you are blocked, raise it. If you see a better way, try it. Do not wait for certainty. Progress comes from action, from small steps, and from learning together. This is the standard. Not tomorrow. Not next year. Now. ### Federated Design Systems: Global, Brand, Product URL: https://www.differentshelf.com/federated-design-systems-global-brand-product/ Last updated: 2026-08-22T23:53:42.000Z *In a multinational the real challenge is scale. Dozens of brands, each with its own history. Products that range from simple to sprawling. Markets that stretch from são paulo to sydney. Without a design system, the result is a patchwork of interfaces that are inconsistent, follow varying standards, and repeat the same work in different places.* ## What a Design System Is and Isn’t A design system is a shared library of tokens and components. Tokens are variables such as colour, spacing, type scale, and motion curves. Components are assembled parts such as buttons, inputs, cards, and menus. Together they form the grammar of the product and give teams a common baseline. A design system supplies grammar, not the sentence. It is not a shortcut to finished products. You cannot use a design system to spin up new journeys or business logic. Those still need to be designed, coded, and tested. The system provides the building blocks, not the blueprint. Its purpose is to strip away duplication so teams can focus on the work that is genuinely new. ## Federation in Practice Global, brand, and product teams face different pressures. Global wants consistency and quality. Brand wants cultural fit and voice. Product wants speed and room to adapt. A single global system tilts too far one way. Letting every team improvise tilts too far the other. Federation balances the tension. It creates a hierarchy of layers where each level imports and extends the one above it. Storybook is the tool that makes this visible. It is an environment where designers and engineers can build, browse, and test components in isolation. Each button, card, or form can be shown with its different states and variations. When federated, storybook becomes more than a gallery. It becomes a map of the system across global, brand, and product layers. At the global level, rules are strict. Accessibility, spacing, and motion are non negotiable: ```tsx <button variant="primary">submit</button> // global baseline: enforces accessibility and spacing ``` A brand layer inherits this baseline but applies its own palette and typography. The markup looks identical, but tokens differ depending on which brand is consuming it: ```tsx <button variant="primary">submit</button> // brand layer: same markup, styling context changes by brand ``` A product layer builds on top to meet domain needs. In a sportsbook the value is the odds themselves: ```tsx <button variant="odds">2.5</button> // product specific: tailored for displaying betting odds ``` In a casino, the priority is action rather than numbers: ```tsx <button variant="spin">spin</button> // product specific: adapted for casino interactions ``` Each layer looks simple in isolation, but the hierarchy matters. Global enforces non negotiables. Brand adapts for cultural voice. Product shapes behaviour for specific journeys. Together they keep the system coherent while still leaving room for difference. ## Contribution and the Triad Federation only works if it flows both ways. Local invention must have a path back up. A new component for right to left text may start in one product. If it proves valuable, it can move into the brand layer. If it has global application, it can be standardised at the top. Standards should flow down. Useful exceptions should be able to travel up. ![Three stacked layers show global standards flowing down through brand to product while reviewed local contributions travel upwards. Global sets accessibility, spacing and motion; brand adapts palette and typography; product composes journey behaviour.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/federated-design-system-flows-ds-2026-v2.png) That contribution model relies on a triad of disciplines working together. Design brings the eye for accessibility, coherence, and visual language, making sure new components do not break the grammar of the system. Product brings the player view, asking whether a change addresses a real need and fits into the broader journey. Engineering brings the technical lens, checking that the component performs well, can be tested, and will hold up at scale. No single discipline can carry a change on its own. When the triad works in balance, improvements spread quickly and responsibly. How to govern this in practice is still an open question. Should global teams act as curators, should brands have councils, or should the system behave like open source with maintainers and reviews? The answer matters less than making sure the path upward exists. ## Already Standard Practice Most large organisations already compose global tokens, brand overrides, and product specific variations, even if they do not label it federation. Storybook and figma support composition. In a monorepo this becomes straightforward, because every team works from the same source of truth and has a clear place to contribute. The real choice is whether to federate deliberately. Forking design systems looks like autonomy in the short term but leads to divergence and decay. Federation is the path to scale without collapse. ## Reaching the Tipping Point In many large organisations a design system is not introduced on a blank canvas but retrofitted into products that already exist. That makes scepticism inevitable. A colour palette in figma or a shared css file is not a design system, and calling it one too early undermines trust. The tipping point comes when tokens are centralised, components exist in code, and adoption reaches a critical mass. A system can be said to exist the day new work defaults to it. Adoption, not announcement, is the design system's first release. The early phase often feels like overhead, another library to maintain. The proof arrives when a team delivers faster by reusing rather than rebuilding. From that point the pattern spreads. Products begin to adopt the system, and new features are built from its components where they exist. Legacy journeys take longer to retrofit, but forward momentum becomes clear. Once that momentum is established, rules can be introduced to stop teams from bypassing the system. Linting checks, review gates, and design sign off make sure new components are added to the system rather than created in isolation. The shift is important: what began as guidance becomes the default path, and the exceptions need justification. ## Why It Matters for Players The impact is visible at the edge. Players move between products and even brands without being jarred by sudden changes in behaviour. Improvements made in one market spread quickly to others. Journeys break less often because fixes ripple through the hierarchy instead of being patched separately in each fork. Consistency is not about making everything look the same. It is about making the experience predictable and trustworthy while still allowing each brand to feel distinct and relevant in its own market. ## Beyond the Lowest Common Denominator A federated design system provides structure without stifling. Global safeguards quality. Brand asserts identity. Product adapts to journeys. Each layer has clear responsibility and a way to influence the others. The deeper value is cultural. Teams stop asking “what is the one safe option we can all agree on” and start asking “what can we contribute that others might use.” Improvements flow upward. Standards flow downward. The system becomes a living mesh rather than a frozen rulebook. This is the opposite of lowest common denominator thinking. Instead of trimming difference until nothing remains, federation encodes difference into the architecture. The result is coherence. Products are consistent enough to be trusted, but distinct enough that each brand still feels like itself. ### Boarding the Release Train URL: https://www.differentshelf.com/boarding-the-release-train/ Last updated: 2026-08-22T22:55:18.000Z *Every organisation faces the same puzzle: how to move fast without derailing the journey. One answer is to run a regular train. A train that leaves every week, on time, regardless of who is ready. Teams take turns driving the train. If your work is ready, it boards. If not, it waits for the next departure. The rhythm matters more than the timetable.* This shift changes culture. Instead of panic merges and one-off pushes, there is a shared cadence. Everyone knows when the next departure is. The theatre of “when can we ship” gives way to “is it good enough to board”. Cadence removes negotiation only when quality decides who boards. The rhythm is predictable but not rigid. If an urgent fix is needed, an extra train can be scheduled. The weekly service sets the pace, yet emergencies do not have to wait. Predictability does not mean inflexibility. The result is practical and visible. Players see fewer surprises. Engineers have more control. Teams feel part of the same flow. ## The Station and the Track If the train is the rhythm, the station and the track are the codebase and the way teams move on it. A monorepo provides one place to do the work with shared leverage and clear boundaries. Trunk-based development keeps changes small, merges frequent, and the main line close to production at all times. Together they make the platform orderly and the track clear so trains can depart on schedule. Monorepo without trunk-based discipline tends to sprawl. Trunk-based without a shared codebase still stumbles on coordination. The combination removes friction. It turns many local efforts into a single, rolling programme. ## Guardrails on the Platform Trains only run smoothly when the platform is orderly. In a monorepo with trunk-based development, order comes from rules that are clear and mechanical rather than negotiated in meetings. Boundaries are drawn so one team’s code cannot wander into another’s without permission. Builds touch only what has changed, keeping the system lean and the main line green. Ownership is explicit. Every change needs two sets of eyes, and the repository itself knows who those eyes should be. Even release is staged. Features can be switched on for one market, one brand, or one cohort before the rest. Early carriages roll out first, with the rest of the train following only once signals from live telemetry show the track is clear. Together these guardrails mean that weekly departures and the occasional extra train happen without drama. Safety is embedded in the system, not dependent on coordination rituals. A release train coordinates readiness. It should not turn waiting into policy. ## Quality as a Passenger Quality is a ticket to board, not a sticker added later. Automated tests act as inspectors on the platform. Feature flags define who should experience a change and who should not. Real user monitoring closes the loop from live journeys and reports back from the tracks in real time. No meaningful tests, no clear flag strategy, no monitoring in place means no seat on the train. The rule is simple and fair. The train runs for those who are ready. ## Microfrontends: The Expensive Detour Microfrontends are often presented as a way to give each team more independence. In practice, they do not create extra carriages on the same line, they create extra trains altogether. Each one comes with its own bundle, its own stack, its own timetable. What looks like autonomy is really duplication. Microfrontends move coordination from repositories into runtime seams. At runtime, players feel the overhead of multiple trains stitched together. More scripts to load. More libraries repeated. More time before the journey even starts. The junctions where these trains meet are fragile, and coordination does not disappear. It simply moves to the points where lines must connect. There are situations where this trade-off is justified. Acquisitions sometimes bring products built on very different technology stacks. Regulatory rules can force strict separation. In some markets, consistency across journeys is less important than speed of delivery, and the price of overhead is worth paying. Most of the time, though, the drawbacks outweigh the benefits. A monorepo with trunk-based development and release trains offers one reliable line. Independence comes from flags and ring rollouts. Autonomy comes from clear boundaries and explicit ownership. Blast-radius control comes from telemetry gates. The service is shared, the line is simple, and the trains run on time. ## What Trains Do Not Solve No system is perfect. Weekly trains rely on credible test environments. If those do not reflect reality, cadence only makes mistakes repeat faster. Investing in environments that mirror production is as important as the trains themselves. ## Choice, Not Compulsion The weekly train leaves on schedule. No team is forced to board. A team can hold back, fix another bug, or run another test. The train leaves anyway. That freedom removes deadline panic. The focus becomes joining a flow rather than hitting a narrow window. When needed, an extra train runs. Urgent fixes or critical launches are never held hostage by the calendar. Predictability is preserved while responsiveness is retained. The effect is unifying. Weekly departures and occasional extras bring teams together and create a more stable system for players. ## The Payback of Flow The financial saving is immediate. Without a timetable, every release becomes a negotiation. Calendars fill with coordination calls. People juggle one-off slots. Engineers burn hours packaging work and re-testing the same paths. With a standing timetable, that churn evaporates. Teams board when ready and the train leaves on time. Focus returns to building and fixing rather than arranging when. The long-term gain is compounding. Each avoided meeting, each earlier merge, each fix shipped without delay looks minor in isolation. Taken together they add momentum. Coordination costs are not only expensive, they are corrosive. They interrupt flow, scatter attention, and delay learning. By removing them, the system allows improvements to stack. Engineers learn faster because feedback loops are shorter. Teams trust more because cadence is predictable. Players notice because issues are resolved in days rather than weeks. The payback comes not from one dramatic event but from hundreds of small efficiencies that build until the whole organisation feels lighter. ## From Heroics to Habit Big-bang releases encourage heroics. Late nights. Risky merges. Firefights after launch. Weekly trains reward steady contribution. Each step is smaller, safer, and easier to understand. The psychological effect is real. Engineers know the next departure is never far away, so there is less pressure to push work that is not ready. Trust builds around the cadence. Safety comes from habit rather than bravado. ## Future Direction The operating model continues to evolve. Monorepo, trunk-based development, and release trains strengthen each other as practices mature. Automated tests make boarding safer. Real user monitoring adds feedback loops from live journeys and gives immediate insight into how a release performs for players. Together these elements shrink risk and build confidence. The timetable itself could shorten. What is weekly today might become daily. More importantly, the culture shifts further from negotiation and ceremony to flow and evidence. The system provides the rhythm and people focus on outcomes. ## The Larger Lesson Release trains, a monorepo, and trunk-based development are not just technical constructs. They are behavioural patterns. They turn individual work into collective momentum without stripping away agency. There will be exceptions. Acquisitions and strict regulatory separation may still require other patterns. For many teams, microfrontends are an expensive detour. The station is already built. The weekly train runs on time. Extra trains are available when needed. The real question is not whether a team can ship independently, but whether it needs to, when the faster, simpler ride is already waiting at the platform. ### Email Signals URL: https://www.differentshelf.com/email-signals-to-cc-bcc-and-the-lost-art-of-replying/ Last updated: 2026-08-24T16:18:12.000Z *An email is a meeting you run without leaving your desk. The sender sits at the head of the table, deciding who joins. Those in the “To” line are seated alongside, expected to listen closely and sometimes speak. Those in “CC” are chairs against the wall, present for awareness but not meant to drive the conversation. “BCC” is the hidden observer behind the glass. Mix the seating, or fail to make the roles clear, and the meeting collapses into noise before it begins.* Email underpins almost every organisation, but most people were never taught how to use it well. The result is cluttered inboxes, blurred responsibilities, and wasted time. The mechanics are simple. The intention often is not. The address fields are an instruction set, not a list of witnesses. The “To” line signals priority. Sometimes it means act, sometimes it simply means *pay attention, this matters*. What matters is clarity. If you mix those who must act with those who are only being informed, and then fail to specify which is which in the body, you have created a problem for everyone. In practice, everybody’s problem means nobody’s problem. “CC” stands for *carbon copy*, a hangover from the days when typing on paper with carbon sheets produced duplicates for record-keeping. The copy was never for the person to act on. It was for awareness. That is the spirit CC still carries today. At best, it keeps people in the loop. At worst, it turns email into theatre, with long lists of copied names adding no clarity. Many leaders now filter CCs into a separate inbox, treating them as background noise. If they go unread, that is not failure. It is triage. “BCC” means *blind carbon copy*. On paper, the duplicate was created but invisible to others. In email, it serves the same purpose: protecting the privacy of a long distribution list, or looping someone in without signalling to the wider group. Used well, it is efficient. Used badly, it looks like secrecy. The difference between discretion and distrust is rarely visible in the moment, but it always colours how a message is received. How we reply matters as much as who we write to. Top-posting, replying above the quoted thread, became the default. It keeps responses quick but often lazy, with long chains of untrimmed history burying the point. Better to cut the noise: quote only what is needed, and place answers where they add clarity. Every unnecessary screen of scrolling is another tax Most email missteps do not come from malice. They come from good intentions and a lack of training. In large organisations, a simple act, choosing who belongs in “To,” who belongs in “CC,” and who does not need to be copied at all, can go a long way. As with [*Shouting in the Digital Office*](https://www.differentshelf.com/shouting-in-the-digital-office/), this is about respect for others’ time and space to do deep work. Each clear signal reduces noise, and the compound effect is organisational focus. Clarity is not just who you address but what you write. If it takes three exchanges to explain, sometimes the faster and kinder move is to pick up the phone. Not every problem belongs in an inbox. The choice of medium is as much a signal of respect as the choice of addressees. The medium is part of the message's demand on another person's time. ### Boiled Frogs and Ringed Deployments URL: https://www.differentshelf.com/boiled-frogs-and-ringed-deployments/ Last updated: 2026-08-24T16:18:11.000Z *The boiled frog story is familiar for a reason. Risk creeps in slowly, then suddenly you look up and a “simple weekly deployment” spans dozens of brands and regions. Nothing dramatic changed on any single day. The water boiled while nobody noticed. The way out is not retreat but sequencing. Roll out in rings. That gives you operational safety and simplicity without the cliff-edge.* ## Deploy and verify A modern pipeline can run this end to end: deploy to a ring, verify against live telemetry, then either promote forward or roll back. Each ring is split into deploy and verify, and verify alone unlocks the next step. Deploy targets the brand or region in the ring, tagging every log, trace, or telemetry event with `ring`, `brand`, `region`, and `version`. Comparable data is the operative phrase. Verify checks the signal that probably matters most: errors per user. It must stay green and stable for a defined window, usually fifteen minutes inside the hour. If it rises, the sequence stops. Verification is the gate. Deployment is only the action before it. ## Discipline and cadence It is tempting to dismiss a small bump in error rate as low impact. A player does not distinguish. If something breaks for them, it breaks. A high-performance organisation treats those signals as stopping points, not nuances. No green verify, no promotion. Rollback remains a single action: redeploy the last known good version. Rings unfold in hours, not days: a morning canary, low-risk brands before lunch, regulator-sensitive rings early afternoon, then the rest before close. That pacing gives telemetry a chance to settle and gives you clarity. ## Failure contained If Ring 1 or 2 goes red, there are three disciplined options: rollback, hold observation, or hotfix if you know the fix is safe and your redeploy mechanism is reliable. The point is failure is bounded and recoverable in shared gaze, not buried across continents. Deployments work best when everyone sees them unfold. Each stage can announce itself in shared chat: “Ring 0 deployed,” “Ring 0 verified green,” “Ring 1 in progress.” The pipeline enforces discipline, but those messages enforce accountability. If error rates creep, the pause is public, not hidden in silent logs. ## Flags and deployments Feature flags remain powerful tools. They decouple exposure from deployment, but code still moves. Flags bring costs: vendor lock-in, toggle sprawl, and in regulated markets approval is tied to the deployed version. Rings orchestrate deployments, flags manage exposure. Neither replaces the other. Flags control exposure. Rings control where the code exists. ![Four rollout rings advance from canary to remaining brands. Each ring separates black code presence, green player exposure and oxide verification, while promotion arrows show that stable evidence, rather than deployment alone, unlocks the next ring.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/ringed-deployment-evidence-ds-2026.png) ## Other models, other limits Staging environments prepare. They catch obvious bugs before production, but they lack scale, real traffic, and regional quirks. Blue/green flips environments. It is elegant, but for many brands it recreates the cliff-edge and doubles infrastructure. Traffic ramps compare versions in parallel. Statistically clean, but a player can straddle versions: a mobile session might hit one build, a desktop session later hits another. That inconsistency erodes trust faster than an outage. Canary pools warn. A small slice of live traffic hits the new build. Shadows and dark launches rehearse. Useful supplements, never substitutes. Each model adds something, but each falls short in practice. Staging prepares. Blue/green flips. Ramps compare. Canaries warn. Shadows rehearse. Rings orchestrate. In a multi-brand, multi-regulator context, orchestration matters more than purity. ## Trunk, rhythm, and confidence This approach finds its power when you combine it with trunk-based development, as I argued in [*The Trunk Is the Team*](https://www.differentshelf.com/the-trunk-is-the-team/). When code lives in trunk and integration happens daily in the open, deployments become predictable. When you can deploy ten times in a week, and each time maintain player experience, it is not just rate of change, it is reliability. The question stops being how often we can release, and becomes how consistently we can protect experience. That is the mark of a high-performance organisation. ## Climbing out of the pot The lesson of the frog is not that boiling is inevitable. It is that you only get cooked if you sit still. Rings keep you moving in visible, reversible steps. They are how you climb out of hot water before it kills the player experience. ### Watching the Watchers, Part 2: How Scripts Break Your Frame Budget URL: https://www.differentshelf.com/watching-the-watchers-part-2-how-scripts-break-your-frame-budget/ Last updated: 2026-08-24T16:18:11.000Z *In* [*Part 1*](https://www.differentshelf.com/watching-the-watchers/) *we explored how third-party scripts watch everything players do, often at the expense of performance. Pixels and tags that were once invisible are now part of the experience itself. The conclusion was simple: every watcher takes something from the player. This second part looks at the mechanics. How scripts consume the frame budget, why long-lived apps expose their weaknesses, and how tools like PartyTown, CAPI, and the Measurement Protocol can help.* A modern app should feel effortless. Routes change without reloads, content updates mid-scroll, menus expand the moment they are tapped. At 60 Hz the browser has just 16.6 ms to keep that illusion intact. That is the whole budget for rendering a frame, handling input, and running any scripts in flight. At 60 Hz, every observer competes with rendering and input for the same 16.6 milliseconds. When the budget is blown during an animation, the result is a visible stutter. When it is blown during an interaction, the screen simply pauses before responding. Both break the spell of immediacy. Into that narrow window we invite analytics, monitoring, and marketing scripts. They hook events, patch APIs, and queue work. Each promises to run politely. On slower devices, politeness does not exist. ![A 50 millisecond main-thread task spans three 16.7 millisecond frame budgets at 60 hertz, delaying input, layout and paint.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/browser-frame-budget-ds-2026.png) ## What the profiler shows and what it misses Performance profiles will happily attribute time to tags. A tag that consumes 400 ms of a two-second journey looks like 20 percent overhead. That sounds tolerable. What the profiler does not reveal: - **Framework churn**: a callback can trigger change detection across the tree, but the cost is shown as “app work.” - **Leaking listeners**: an observer attached to a DOM element can survive navigation. In a long-lived app the heap grows and the app slows. - **Duplicate initialisation**: SDKs that re-init on every route change, rebinding handlers and wasting cycles. The visible overhead is bad enough. The hidden overhead is worse. ## The myth of “no impact” Vendors often insist their scripts have *“no impact,”* *“no perceivable impact,”* or that they are *“lightweight and quick to download.”* That logic stops at the network. A script may be small in bytes but large in consequences. Once loaded it attaches listeners, observes mutations, patches APIs, and queues work. These costs scale with every interaction. A small script can have a large runtime shadow. The truth is straightforward. If a script genuinely has no impact on performance, it is unlikely to be doing meaningful work. If it is doing meaningful work, it must consume resources, and those resources come from the same 16.6 ms frame budget as rendering and input. A claim of “no impact” is usually a contradiction: either the script adds overhead, or it is not delivering what is being sold. ## Two worlds: with and without isolation [PartyTown](https://partytown.qwik.dev/?ref=differentshelf.com) is a small runtime from Builder.io that changes where scripts execute. Instead of running in the main thread alongside rendering and input, compatible scripts are moved into a web worker. That shift matters because work done in a worker cannot stall a frame. Analytics beacons, network requests, and marketing logic all run off the main thread, leaving rendering and input responsive. It is not a silver bullet. Scripts that depend on the DOM still run in the main thread. Session replay, element visibility tracking, and other observers remain where they are. But for many analytics and marketing tags, isolation removes much of the cost. In practice, audits show the difference is stark. Journeys that once took two to four seconds dropped to half or a third of that with PartyTown enabled. Even heavy transactional flows could complete in under half a second. ## Replay and monitoring It comes back to a point from the first article: these tools were built for a world of page loads. They expect a fresh reload to tear everything down. In a long-lived app, there is no such reset. Listeners survive navigation, observers persist, and references linger in memory. ## Two tools, one DOM, twice the cost Session replay has been measured adding 40 percent overhead on some flows. That makes sense: complicated journeys are complicated to record. Monitoring tools are lighter, but they watch many of the same surfaces such as performance observers, error listeners, scroll events, and mutations. When the two come from different vendors, the overlap doubles. Each attaches its own listeners, its own observers, and its own serialisers. A single scroll is captured twice, a mutation dispatched through two callbacks, and a network error patched in two layers. Even if replay is sampled at one percent, the duplicated instrumentation remains for every session. It is not just time. Two tools mean two sets of references held in memory. Listeners bound to elements that no longer exist leak twice as fast. In a long-lived app that compounds into real heap pressure and slower sessions. If both features live in the same tool the story is a little better. The listeners exist once and the vendor can coordinate. But across different tools there is no coordination. The overhead compounds, the leaks multiply, and the frame budget vanishes. The right approach is mutual exclusion. Record one percent of sessions in detail, observe twenty percent for timings. Never run both together, and certainly not from separate vendors. Sampling is not lost insight when full observation changes the thing being observed. ## Analytics without the baggage Campaign optimisation lives and dies on tracking. Without reliable conversions, ad platforms cannot optimise bids or audiences, and marketing spend leaks away. This is why every platform has built a “server-side” channel: - **Conversion APIs (CAPI)** from social platforms - **Google Analytics Measurement Protocol (MP)** Both replace the familiar browser tag with a direct HTTP endpoint. Instead of the client firing a beacon from JavaScript, the event is packaged and sent from infrastructure. The pitch is clean: lighter client, better privacy, resilient delivery. The reality is subtler. ## Measurement Protocol in practice Measurement Protocol is not magic server-side analytics. In a long-lived app, the client still has to decide what happened such as a route change, a purchase, or a form submission. The difference is only transport: instead of GA’s script attaching listeners, the app or a backend proxy sends a POST to GA’s endpoint. The overhead of the script is gone, but the need for deliberate instrumentation remains. What you do not explicitly send, GA will not see. Server-side GA containers take this a step further. The app emits events, a proxy enriches them with campaign or cohort metadata, and forwards on. This consolidates logic and reduces duplication, but it is still fundamentally client-signalled. ## CAPI in practice CAPIs solve the same problem for social platforms. Instead of firing a pixel in the DOM, a backend call transmits the conversion. The advantages are similar: no DOM overhead, no reliance on third-party cookies, and fewer points of failure. For campaign optimisation, this preserves accuracy while lifting load from the main thread. ## Where the line sits - **Campaign optimisation** needs high-fidelity signals. CAPI and MP provide them, but only if we instrument cleanly. - **Product analytics** is less brittle. Sampling, RUM, and partial replay give enough direction without trying to replicate every GA event. The rule is simple: treat campaign optimisation as first-class, instrument deliberately, and move it off the client. Treat product analytics as directional, sample heavily, and accept imperfection. Instrumentation is one form of overhead. Experimentation is another. Both ask the app to carry work not directly tied to the journey, and both benefit from being designed into the codebase rather than layered on afterward. ## Two styles of experimentation Experiments divide into two styles. - **Script-injection tools** overlay content after render. They flicker, delay, and attach listeners that often linger beyond their lifetime. In a long-lived app, that means memory leaks. Because they live outside the codebase, they bypass TypeScript and its guardrails. Flags and variants are strings, not checked values. The result is more fragile lifecycle, unreliable teardown, and weakened state integrity. - **Embedded SDKs** treat flags as part of the app itself. Variants are declared in TypeScript, integrated into components, and torn down with them. In a long-lived app, this style is safer. Lifecycle is respected, teardown is consistent, and state integrity is preserved. The first style corrodes these foundations. The second style reinforces them. ## When scripts break more than speed The risk is not only stutter. Monitoring scripts can break apps outright: - **CORS and preflight**: if headers are misconfigured or proxies strict, failed requests can throw uncaught errors that bubble into the global handler, destabilising bootstrap - **Monkey-patching**: replay tools patch `fetch`, `XMLHttpRequest`, `addEventListener`, and `MutationObserver`. Collisions or recursion can freeze navigation or halt bootstrap - **CSP and ad-block**: blocked `connect-src` or filter rules can leave libraries half-initialised. Some fail noisily - **Resource pressure**: replay buffers DOM snapshots in memory. On constrained devices this can trigger reloads or crashes These are not exotic bugs but ordinary misconfigurations amplified by fragile hooks. **Security surface**: Every external script is another execution context with access to the same DOM and APIs as the app itself. Even if the vendor is trusted, the dependency chain may not be. A compromised tag or CDN can exfiltrate data as easily as it can measure performance. Guardrails must cover security as much as stability. ## WebView makes it worse All of these problems are amplified inside Android WebView. Chrome on Android runs on V8 with its full set of optimisations. WebView lags behind and often runs with restricted JIT for security reasons. The same script can take twice as long. Garbage collection is less aggressive, so detached DOM nodes hang around longer. Long tasks in JavaScript block not only rendering but also native-side gestures. What feels sluggish in Chrome feels worse in WebView. A fragile long-lived app becomes more fragile still when squeezed into a weaker runtime. ## Guardrails If scripts must run, they need guardrails: - Never run replay and monitoring simultaneously - Enforce sampling: one percent for replay, twenty percent for monitoring - Wrap initialisation in try/catch and defer until after first meaningful paint - Use canaries and kill switches so vendors can be turned off instantly - Harden CORS and CSP rules - Canary releases at one percent, with CI tests that load the app alongside vendor snippets and run core journeys ## Conclusion Client-side scripts are not free. They take time, trigger framework churn, leak memory, and collide in ways profiles rarely show. PartyTown helps by isolating some into workers. Conversion APIs and the Measurement Protocol help by moving signals off the client. Sampling and exclusion help by limiting what remains. The rule of thumb is simple. Slow journeys get slower, fast ones do not get faster. The player feels it most in the moments that matter, such as checkout, search, or a menu open on a low-end device. And when the same app is forced through WebView without V8’s optimisations, the cost is amplified again. These tools were built for a world of page loads. Long-lived apps accumulate listeners and never reset. WebView tightens the constraint even further. That mismatch is why memory leaks multiply, why duplicate hooks pile up, and why frame budgets are missed. The fix is not to abandon measurement, but to re-architect it. Campaign optimisation deserves high-fidelity signals: instrument deliberately, send them through APIs and proxies, not pixels. Product analytics deserves restraint: sample, monitor, record sparingly. Experiments belong in the codebase, not injected afterward. And everything that remains must run with guardrails and kill switches. Scripts should serve the player, not the other way round. *If* [*Part 1*](https://www.differentshelf.com/watching-the-watchers/) *showed that every watcher takes something from the player, Part 2 shows exactly how they do it.* ### The Integration Dilemma URL: https://www.differentshelf.com/the-integration-dilemma/ Last updated: 2026-08-24T16:18:11.000Z *Better a seatbelt worn on every journey than an airbag that sometimes works and sometimes doesn’t. A seatbelt is simple, predictable, and compounds its value with every use. An airbag without a seatbelt, and unreliable at that, creates the illusion of safety while delivering little in practice. The same holds for tooling: consistency outperforms brilliance applied sporadically.* That is why many enterprises lean towards integrated platforms that bring multiple capabilities under one roof. They may not be the sharpest tool in every category, but they are coherent, accessible, and predictable. Out of the box, they give teams one login, one workflow, one way of working. Best-of-breed tooling, by contrast, has an obvious pull. The static code analyser that never misses. The security scanner that claims unbeatable coverage. The analytics suite that promises every conceivable developer metric. Each dazzles on its own. Every specialist tool arrives with a second product: the integration you must now own. Yet unless teams deliberately try, the promise rarely lands. Code goes unassessed. Security scanners are never wired into pipelines. Developer metrics never materialise. Procurement is completed, rollout announced, and the harder work never starts. The stitching into a coherent flow is left for later, and later rarely arrives. Even when integration is attempted, it is fragile. Every connector is meant to smooth flow but becomes a seam. Every seam is meant to hold firm but splits under load. And when seams split, the result is not abstract debt but concrete gaps: bugs that slip through, vulnerabilities that remain unscanned, a lack of insight into velocity. Sometimes, as in [*Trading Without a Ticker*](https://www.differentshelf.com/trading-without-a-ticker/), the problem is not integration but access. Tools exist, yet the people who need them cannot reach them. Different cause, same effect: insight scattered instead of shared. ## The Bombardment of Tools Product and engineering teams live under a constant barrage of SaaS offerings. Each demo looks like the future: polished interfaces, clever automation, dashboards alive with insight. Many are genuinely brilliant. The harder question is whether they are brilliant enough to justify the cost of integration, rollout, and long-term care. At small scale, the overhead can be absorbed. At enterprise scale, each new tool ripples into governance, procurement, compliance, and support. Even the most impressive platform can sink beneath the weight of coordination it demands. ## Why “Good Enough” Wins Even if the seams are smoothed, scale brings another challenge: consistency. With thousands of developers, the issue is not whether the tool works but whether it is used in the same way. Integrated suites bias towards uniformity. One place to commit, one dashboard to check, one release flow. Best-of-breed fragments. One team embraces the full feature set, another barely scratches the surface, a third bends the tool beyond recognition. On paper, the organisation has common capability. In practice, it has a patchwork of local customs. And even “uniformity” is relative. In a large enterprise, persuading everyone to use the same tool in the first place is hard. Adoption takes leadership, incentives, and persistence. The value comes not from buying the tool but from aligning behaviour around it. Without that, even the most integrated suite will fracture at the edges. Uniformity is not procurement. It is repeated behaviour. One-size-fits-all also rarely fits perfectly. A standardised tool may feel blunt to specialists or lack the depth needed for a specific context. Yet at scale, the trade-off often favours a common platform. The cost of a few dissatisfied teams is still lower than the cost of endless fragmentation. This is why “good enough” wins. A tool that meets most needs but is used consistently delivers more value than one that promises perfection but is applied only sometimes. Predictability compounds; capability that is not exercised might as well not exist. Judge capability by adoption and consequence, not by the best demonstration. ## Knowing When Sharpness Matters Still, there are moments when the sharper edge is the only choice. In a regulated environment, the difference between “almost” and “complete” is not marginal; it is existential. In systems where downtime costs millions, the right analytic view can pay for itself many times over. Engineers’ appetite for the shiny is not vanity. Curiosity uncovers new approaches. Experiments with the latest SaaS often set the conditions for the next leap forward. Without that instinct, many breakthroughs would never reach production. The tension will never disappear. Integrated flows give predictability; specialist tools give precision. One compounds value through consistency, the other through edge-case brilliance. The art is not in rejecting the shiny, but in asking whether the marginal gain in precision outweighs the cost of adoption and integration. Most of the time, it does not. Better a tool that delivers broadly, predictably, and is actually used, than one that dazzles in the corner while the rest of the organisation carries on without it. In the end, what matters is not the brilliance of the parts, but the fluency of the whole. Just as with safety, it is the seatbelt worn every journey that saves lives, not the airbag that deploys only some of the time. ### Show Your Working URL: https://www.differentshelf.com/show-your-working/ Last updated: 2026-08-24T16:18:11.000Z *Every fix is a puzzle solved. Each one shelved in the open becomes part of the organisation’s library, a catalogue others can search, learn from, and build upon.* When someone raises an issue, they have already invested in the system. They noticed, they documented, and often they brought clues with them. In many cases they are also blocked, unable to continue their own work until the problem is resolved. As explored in [*Shouting in the Digital Office*’s](https://www.differentshelf.com/shouting-in-the-digital-office/) “Bring the clues with you,” that effort is a building block of organisational learning. After a fix, responding with a clear explanation of what changed achieves three things. It confirms shared understanding of the problem. It demonstrates skill and care in the resolution. And it creates a record others can learn from. *Each closed loop is another solved puzzle added to the shelf, compounding into resilience over time.* A fix restores a system once. An explanation makes the recovery reusable. To be useful, the explanation must be explicit. Not just that the issue is fixed, but what was done to resolve it: which component was touched, which behaviour corrected, which decision made. Without that clarity, others cannot verify the fix, learn from it, or challenge whether it is sustainable. > A fix is not complete until it is explained - clarity turns resolution into knowledge, builds trust, and multiplies business value. Invisible fixes do none of this. A simple “try again” leaves uncertainty, prevents knowledge from compounding, and discards the chance to test whether the resolution is correct or durable. Visible fixes invite verification. Others can confirm the change, assess whether it will hold, and share their perspective on whether the issue might return. That scrutiny strengthens both the fix and the organisation’s confidence in it. Competence becomes trust only when others can inspect it. Openness is not about blame. It is about showing competence, making care visible, and building trust. Communication can matter as much as technical skill, because the way fixes are shared often decides whether they help once or many times. The same instinct matters outside the walls. Release notes that say “bug fixes and improvements” teach nothing. A note that names the issue and the change builds confidence that the system is under control. Better: “Fixed login timeouts on older Android devices by correcting token refresh logic and adding a retry on 429.” That shows understanding, shows care, and reduces the chance of return. Respect in engineering is not only in restoring function, but in making visible the steps taken to keep things reliable. When organisations work this way, issues turn into durable knowledge, trust strengthens across teams, and customer confidence grows. The business value is clear: faster recovery, fewer repeats, unblocked teams, and a culture where every fix multiplies its impact. ### Trading Without a Ticker URL: https://www.differentshelf.com/trading-without-a-ticker/ Last updated: 2026-08-24T16:18:10.000Z *Imagine a trading floor where analysts must apply for permission to see the market feed. By the time the request is approved, the opportunity has passed. No trade is made, no value captured. Markets demand speed and visibility. So does digital product.* Every player leaves a trail of signals: how quickly a page loaded, whether a transaction completed smoothly, if an interaction failed. Collectively, these signals are our most direct measure of experience. They are also one of our clearest levers for growth. But real user monitoring (RUM) is not free. As I argued in [*Watching the Watchers*](https://www.differentshelf.com/watching-the-watchers/), every beacon and data point consumes bandwidth, processing, and ultimately the player’s time. It is a tax on the very experience we aim to improve. It is also a direct financial cost to the organisation in infrastructure and tooling. If we accept those costs, then failing to use the data fully is wasteful twice over. The player pays, the business pays, and still teams are left in the dark. Observation is already a tax. Restricting access makes the organisation pay it twice. In many organisations, access to these signals is treated like access to payroll. Product teams, engineers and designers are asked to raise tickets, justify, and wait. The intent is reasonable: compliance, security, governance. The effect is limiting. Decisions are made without knowing what players actually experience. Issues are spotted late. Opportunities are missed. **When Things Go Wrong** The impact is clearest in moments of stress: a spike in errors, a drop in conversion, a release that misbehaves under load. In those situations, the last thing anyone should be doing is scrabbling for access. Time spent working out who to ask or which form to fill in is time not spent fixing the problem. And inevitably, the burden falls on the same helpful few who end up acting as human routers for everyone else’s requests. That is fragility. Leadership’s role is clear. Just as a trading floor equips everyone with the same market view, our job is to equip teams with the player’s view. That means building compliance and audit into the system, not into each individual request. Access should be the default. Data should be available out of the box. Governance should protect without slowing the organisation down. Governance belongs in the access model, not in a queue of individual requests. The business case follows directly. When teams cannot see error rates or load times, they optimise blindly. When they can, they close the gap between what we intend to deliver and what players actually feel. That difference shows up in retention, conversion and revenue. **A Growth Lever, Not Housekeeping** The point is the same as in enterprise architecture. A [flock does not fly in formation](https://www.differentshelf.com/shaping-architecture-through-behaviour/) because someone draws it on a whiteboard. It does so because each bird has live awareness of the others. Architecture without live signals becomes theatre. Data governance without accessibility becomes friction. To lead at scale, the organisation needs both: a shared structure and a flow of information that reaches everyone shaping the player experience. As argued in [*Keep the Heart Beating*](https://www.differentshelf.com/keep-the-heart-beating/) and [*Inner Sourcing*](https://www.differentshelf.com/the-code-belongs-to-everyone/), visibility and shared responsibility accelerate the whole system. The same applies here. The more people who can see what players see, the faster we can act on it. Democratising data is not internal housekeeping. It is a growth lever. Every barrier removed shortens the path from player experience to business outcome. That is what “player first” looks like in practice. ### The Right Shoes? URL: https://www.differentshelf.com/the-right-shoes/ Last updated: 2026-08-24T16:18:10.000Z *If* [*Part 1*](https://www.differentshelf.com/the-wrong-shoes-for-the-race/) *was about running a marathon in leather brogues, Part 2 is about their opposite. The right shoes are a pair of the latest running trainers, the ones with so much bounce they are almost illegal. They don’t just avoid pain; they give you an edge.* Most developers still sit on Windows or macOS. Both can be made to work. Neither matches Linux for speed and predictability. The closer your workstation is to production, the shorter the feedback loop. The right workstation follows the workload, not the corporate default. ## Where the gaps show The biggest differences surface in file I/O. Modern development involves sprawling dependency trees and thousands of small files. Builds and reloads hammer the filesystem. On Linux, inotify handles file events. It is efficient, scalable, and designed for real-time monitoring. On macOS, FSEvents sits above APFS. It works, but with more latency and less consistency under load. On Windows, developers rely on the USN Journal, built for backup and recovery, not continuous reloads. Security multiplies the cost. Every open, close, stat, or watch request triggers a filter driver. NTFS already serialises access with oplocks, and antivirus hooks add another detour. What should be a sub-millisecond syscall becomes a millisecond operation. That is why unpacking a tarball takes two minutes on NTFS, nine seconds on WSL2, and under six seconds on Linux. > *Unpacking a tarball: two minutes on NTFS, nine seconds on WSL2, under six on Linux.* Developers sometimes try to work around it. On Windows, that can mean keeping projects inside the WSL2 filesystem rather than NTFS, excluding development directories from antivirus scans, disabling Windows Search indexing, or adjusting NTFS oplocks. On macOS, common tweaks include using case-sensitive APFS volumes for projects, disabling Spotlight indexing on build directories, or increasing file watcher limits with `launchctl`. The problem is that in most enterprises these are exactly the changes corporate IT locks down. Antivirus rules are centrally enforced. Indexing policies are uniform. Filesystem settings are off-limits. The knobs exist, but no one can touch them. And even if they could, in an organisation of two thousand engineers most will not. They will work with the defaults. If the defaults are slow, every build and test is slow. Linux avoids this. ext4, xfs, and btrfs handle scale without tweaks. The defaults work. Fluxbox was the same: a window manager so light it could run on hardware where antivirus had gone wild and still not drop a frame. Transparent terminals before they were a thing. No decoration, no hacks, just speed. Simplicity as strength, not compromise. ## The shim is not invisible On Linux, containers are just processes. cgroups limit resources. Namespaces isolate them. The same scheduler that runs your editor threads runs your containers. On Windows and macOS, every container is a guest. Docker starts a Linux VM, mounts a filesystem, and places containers inside it. File operations cross from NTFS or APFS into ext4 through a translation layer. That shim intercepts syscalls and forwards them. Every open, close, and stat pays the toll. A shim can be convenient and still belong in the performance budget. The cost is measurable. Run a database in Docker on Windows and watch latency spike. Bind-mount a large dependency tree and performance collapses. What Linux treats as routine syscalls, Windows and macOS translate into interprocess RPC. There are workarounds. On Windows and macOS, teams sometimes use Docker Desktop’s delegated mounts to reduce filesystem thrash, run everything inside the VM instead of bind-mounting to the host, allocate more CPU and memory to the Docker VM, or disable file sharing with antivirus and indexing services. These measures help, but most are blocked by corporate IT policy. Antivirus exclusions are rarely granted. VM settings are centrally locked. Indexing rules are uniform. At enterprise scale the defaults are the only thing most developers ever see, and the defaults are slow. This is also why developers on Linux often carry more credibility. The bar is higher. Running Linux means dealing with shells, filesystems, and processes directly. It requires a comfort with complexity that Windows hides. Developers who only know Windows may be capable, but they have not had to cross that threshold. Their feedback is shaped by shims. Linux developers see the thing itself. Operating-system choice is workload design, not a proxy for engineering ability. It is the same parable you hear in systems programming: if you never touch assembler, you can write code, but you will never really understand memory management. Abstractions are useful, but they hide truths. And when the truths leak through, as they always do, only those who have seen under the hood can respond with clarity. Local Kubernetes clusters highlight the divide. Developers often reach for tools like kind, k3d, or minikube to simulate production. On Linux these talk directly to the kernel. On Windows and macOS they route through a hypervisor. Overlay filesystems, volume mounts, and networking all pass through the shim. Docker makes it look portable. On non-Linux hosts it is an emulator, and the emulator shows. The shim is not invisible. It consumes CPU. It adds latency. It introduces variance between dev and prod. Ignoring it is the costliest mistake an enterprise can make. ## Virtual desktops are worse Enterprises sometimes reach for virtual desktops. The logic is familiar: centralise, control, standardise. But VDI belongs in the noughties. It is the worst of both worlds. Developers share backend infrastructure. Performance depends on network round-trips. Latency creeps into every keystroke. Builds contend for central resources. Instead of fixing the workstation, VDI pushes it further away. Security teams turn to VDI to shrink surface area. That makes sense, but it also hard-codes the wrong behaviours: shared environments, slow feedback, normalised delay. If local loops are the cure, VDI prescribes more of the disease. Better options exist. Isolated Linux workstations or personal Kubernetes namespaces provide the balance. Security and speed do not have to conflict. ## The way forward Corporate IT often hesitate at Linux desktops. Manageability has been the concern. That gap is closing. Modern MDM tools patch, encrypt, and monitor Linux. Endpoint protection, VPN clients, and zero-trust agents ship Linux builds. Uniform fleets keep IT simple, but they are expensive for engineering. Treating every laptop the same costs tens of millions in lost productivity. Security teams deserve credit for what they defend against, but they need to account for the trade-offs they impose. Developers need choice. Some will prefer macOS for its polish. Some need Windows for specific integrations. But when workloads involve heavy file churn or containerised services, Linux is the better fit. That is not preference. It is measurable. ## Closer to production, closer to speed The right shoes are the ones closest to production. For teams whose production runs on Linux, Linux workstations close the loop. They cut the twentyfold slowdown. They remove the emulator. They make local clusters behave like production. VDI belongs in the past. It fixes yesterday’s risks while locking in today’s bad habits. Developers need predictable, isolated environments, not streamed desktops that jitter on every keystroke. Security teams have reasons for reaching for VDI, but engineering pays the price. And the wider context matters. The desktop is no longer the constraint. Slack, Teams, Zoom, even PowerPoint: everything is web-first. The argument that Linux is not ready no longer holds. The real question is whether enterprises are ready to treat developer workstations as production-grade equipment, not generic office laptops. Fluxbox may be nostalgia, but its principle holds. Keep it simple. Keep it close to production. Every choice is a trade-off. The mistake is pretending you are not making one, or worse, making the wrong one in full view of the bill it generates. **And if you happen to read this, and happen to work for me or know me, ping me. Let’s get you over to Linux. This piece is provocative by design. Sometimes it takes an aggressive argument to shake loose old defaults.** ### The Code Belongs to Everyone URL: https://www.differentshelf.com/the-code-belongs-to-everyone/ Last updated: 2026-08-24T16:18:10.000Z *Big organisations are good at building walls without intending to. Code splits across multiple repositories, each with its own customs. Pipelines differ, tests run differently, even the folder structure changes. Working across teams becomes less like walking down the corridor and more like having to learn a new alphabet each time.* ## Why Inner Sourcing Matters Inner sourcing resists that drift. It treats internal software as open, no matter who “owns” it. A bug seen by one squad can be fixed by another. A shared component can travel more easily. Knowledge and responsibility start to circulate instead of pooling in one place. An open door is not an invitation if every room has a different lock. For this to work, change has to be simple. [*Keep the Heart Beating*](https://www.differentshelf.com/keep-the-heart-beating/) explored why continuity matters in systems. The same principle applies to engineering culture. If the cost of change is high, fixes wait. Lower the barrier, and the organisation heals itself faster. Standards are what make that possible. They are not decoration. They are the phrasebook everyone shares. Once you know how to add a test, run a pipeline, and raise a merge request in one project, the next feels familiar. Engineers can spend their energy on the problem, not on re-learning the grammar of each repo. Documentation and discoverability matter just as much. If others cannot find the work, they cannot help with it. Contribution guidelines, backlogs, and clear READMEs are the signs that make inner sourcing legible. Without them, an open door can still feel closed. As I argued in [*Signs Worth Reading*](https://www.differentshelf.com/signs-worth-reading/), these artefacts are not bureaucracy. They are how intent is made visible, and how teams signal where help is welcome. Onboarding is another signal. The classic measure of engineering health is whether a new hire is able to make a commit on day one. But why stop there? A stronger test is whether an engineer can make a meaningful change to code for a team they are not part of. If the answer is no, the team might be healthy in isolation but the wider company is not. The stronger onboarding test is contribution beyond the team that hired you. Inner sourcing is also part of the antidote to what I described in [*Lowest Common Denominator Thinking*](https://www.differentshelf.com/lowest-common-denominator-thinking/). When every contribution follows the most restrictive path, the organisation slows to match its weakest link. By opening up code and making it easy to contribute safely, we lift the baseline without flattening everything to the lowest standard. ## Lowering the Barrier Permission does not need to be granted in advance. The ability to raise a merge request is itself the invitation. From there it passes through the same protections as any other change: automated tests, continuous integration, and peer review. Review protects ownership without requiring permission to participate. That makes participation safer than it might first appear. A merge request is unlikely to break anything. At worst it sparks a conversation, provides food for thought, or becomes the chance to educate. **“I saw something I could help with, and here’s my attempt.” That message should be embraced.** A [monorepo](https://www.differentshelf.com/what-is-a-monorepo-and-why-does-it-matter/) strengthens that effect. A single place to look, a consistent way to build. [Trunk-based development](https://www.differentshelf.com/the-trunk-is-the-team/) reinforces it by making integration the default. Code moves in small increments to the same mainline, reviewed and tested quickly. Observability tools such as Datadog are sharper in that context too. When errors can be traced to the exact line of code, it helps if every project lives in the same place and follows the same model. In [*Refactor, Rewrite, Repeat*](https://www.differentshelf.com/refactor-rewrite-repeat/) I pointed to how AI may soon take that further, moving from diagnosis to patch to release. Being ready for inner sourcing extends to AI as well. If we can easily accept human fixes today, we will be better prepared to accept machine-generated ones tomorrow. ## The Wider View For leaders, the value is straightforward. Inner sourcing scales engineering without scaling cost at the same rate. It reduces duplication, spreads knowledge, and makes the organisation less dependent on any single team. The result is resilience: more work moves forward, fewer bottlenecks form, and the company gets a higher return on the talent it already has. It also makes for better engineers. The chance to learn, the satisfaction of progress, and the feeling of trust that comes from freedom are basic human drivers. A culture that supports them will always outperform one that restricts them. The signs are easy to read. When work is pushed across tickets rather than fixed directly, when new hires take weeks before contributing, or when knowledge is held too tightly by a few, the health of inner sourcing is already in question. The opposite is equally visible. Engineers able to commit on day one, making safe changes beyond their own team, and seeing those contributions welcomed, are all signals of a system that works. Not everyone finds that shift easy. It can feel safer to stay within familiar boundaries, even if it slows the wider organisation. Yet the real security comes from making it easier for others to contribute, which leads to a stronger product, lower operating costs, and happier players. The effort no longer spent on guarding the walls can be redirected to adding new value elsewhere, the part of the work that players actually notice. Players never know which team fixed the bug or polished the flow. They only notice that the product works more smoothly. Inner sourcing is one of the quiet ways to make that outcome more likely, turning a workplace of many languages into one where participation is understood everywhere. So it is worth asking: why should something so common in open source feel like an imposition within corporate walls? ### The Wrong Shoes for the Race URL: https://www.differentshelf.com/the-wrong-shoes-for-the-race/ Last updated: 2026-08-24T16:18:09.000Z *You can run a marathon in leather brogues. You will just finish slower, in more pain, and need to call the medics every other mile. Developers can build on standard corporate laptops. They will just spend half their time waiting, and the organisation pays for every minute.* The cheaper laptop is expensive when it lengthens every feedback loop. Corporate IT is not to blame. Their mandate is enterprise protection and scale. Uniform fleets are the safest way to defend against ransomware, phishing, and the daily churn of threats. For most employees, working with spreadsheets, presentations, and calls, that model is exactly right. Product engineering is different. A developer’s workstation is not office stationery. It is a local build farm, a miniature data centre. When that workstation is too weak, or too tightly controlled, the work spills into integration and test environments. That is where the trouble starts. ## The vicious circle Integration and test environments always begin with good intentions: centralisation, efficiency, standardisation. In practice, they are the worst place to do daily development. Imagine dozens of teams deploying to the same cluster. Yesterday your feature worked. Today it fails, not because of your code but because someone else restarted a service or rewrote the data. Debugging becomes guesswork: is the failure mine, or is it just background noise? Developers adapt by rerunning tests, redeploying repeatedly, and wasting hours stabilising an environment never designed for constant experimentation. The weaker the local workstation, the more teams lean on integration and test environments. The more they lean, the less stable those environments become. Instability feeds dependency, and dependency feeds instability. A shared test environment converts local uncertainty into organisational noise. ## Two poles, one pattern The loop is visible from both ends of the technology spectrum. - **Modern JavaScript ecosystems** involve thousands or even millions of small files. On Windows, the slowdown is not marginal but staggering. In one benchmark, a routine task took more than two minutes on NTFS, the default setup for most enterprise developers, compared with under six seconds on Linux. Even with WSL2 it was nearly nine seconds, and with antivirus enabled Windows stretched to twenty-five seconds. That is a twentyfold slowdown between the default and the target platform. What Linux finishes in a breath, Windows turns into a coffee break. - **Enterprise Java or .NET** services are heavy, stateful, and tied to broad databases. Local workstations strain or are blocked outright. Teams fall back on integration and test environments, where deployments collide and failures blur between defects and environment. Different ecosystems, same outcome: weak workstations force reliance on shared systems. Shared systems degrade under the weight. ## The invisible invoice An hour a day lost feels manageable. But across two thousand engineers, it is more than half a million hours a year. Even at a cautious twenty-five pounds an hour, that is £12.5 million a year in invisible cost. And that is before you consider the twentyfold slowdown seen on common developer tasks, a multiplier that magnifies waste across every build, reload, and test. The figure is invisible because it never appears on a procurement invoice. It appears instead in the firefighting that dominates integration and test environments, in delivery dates that slip, and in production systems that inherit fragility from the way they were developed. It also appears as an opportunity cost. Leading vendors in feature flagging and progressive delivery are already asking whether lower environments are needed at all, replacing them with controlled rollouts and instant feedback. If local development remains weak, organisations miss the chance to evolve toward that future. > A twentyfold slowdown on everyday tasks turns instant feedback into a coffee break. Multiply it across 2,000 engineers, and the cost exceeds £12.5 million a year. ## Why local matters The best way to stabilise integration and test environments is to use them less. When developers can run services predictably on their own workstations, those environments become calmer. Changes arrive tested. Experiments happen locally, where failure is cheap. Shared clusters return to their intended purpose: integration, not survival. Isolation is the key. Whether the environment runs on a workstation or in a personal Kubernetes namespace, what matters is that it is not shared. Daily development in a shared cluster is like running a marathon on a collapsing track. Predictable environments, even if lightweight, restore focus and stability. This is the same divide I explored in [*Shaping Architecture Through Behaviour*](https://www.differentshelf.com/shaping-architecture-through-behaviour/). Enterprise Architecture fits when the goal is stability and control. It matches the rhythm of corporate IT, where uniformity is the defence against risk. But product engineering thrives on different behaviours: fast feedback, local experimentation, continuous integration. Apply EA’s slower logic here and you get the same outcome as with integration and test environments: drift, instability, and mounting cost. There is also a cultural signal. If a system is easy to run on a workstation, it is usually easier to run in production. The same seams that make hot reload fast also make deployments reliable. Complexity that frustrates developers today has a way of resurfacing at three in the morning tomorrow. ## Who should own the workstations? It is not fair to expect Corporate IT to solve this. Their focus is scale and safety, not compiling monorepos or running clusters of services. Their instincts, uniformity and control, are right for the majority. But in a product operating model, developer workstations need different ownership. Platform Engineering, SREs, and Principal Engineers must take the lead together. - Platform Engineering sets the standards: workstations treated as production equipment, automation that keeps local and production aligned, guardrails that enable speed without compromising safety. - SREs, embedded in squads, see the friction first-hand. They notice when reloads slow, when containers thrash, when local setups drift. They carry those signals back into the platform. - Principal Engineers are building code every day. Their judgement keeps decisions grounded in practice, not abstractions. No ivory tower, just experience guiding direction. Corporate IT safeguards the enterprise. Platform, SRE, and Principal Engineers ensure workstations are fit for purpose. Together, they break the cycle: security without paralysis, speed without fragility. ## The business lens Uniform fleets reduce risk. Fit-for-purpose workstations reduce waste. The better question is not what does a laptop cost, but what does a feedback loop cost? Procurement sees the workstation price. Product engineering lives with the feedback-loop cost. Break the vicious circle, and integration and test environments stabilise, production steadies, and delivery accelerates. That is what the business values: resilience, predictability, and speed to market, achieved without compromising security. And the direction of travel is already visible. Feature flag platforms such as LaunchDarkly argue for dropping lower environments altogether, replacing them with controlled rollouts and instant feedback in production. That model may not work in regulated industries, but it shows where the frontier lies: stronger local development, lighter reliance on integration and test environments, and faster, safer delivery. And if production runs on Linux, why not our workstations? That is a question for Part 2. ### Shaping Architecture Through Behaviour URL: https://www.differentshelf.com/shaping-architecture-through-behaviour/ Last updated: 2026-08-24T16:18:09.000Z *A flock of birds has no master plan. Each bird follows a few simple rules – keep close, do not collide, match direction – and the result is a pattern that looks choreographed. Try to draw the outline and it will already have changed. Enterprise architecture often tries to do just that: capture a living system on paper. The picture slips out of date almost as soon as it is made.* A catalogue records architecture after the fact. Repeated behaviour produces it. The intent is usually sound: leaders want visibility and teams want clarity. Yet silos appear all the same. The challenge is that if documentation is stale, dashboards are ignored, or incident tickets are vague, the chart becomes theatre. It reflects not the health of systems but the absence of the behaviours that would make them visible. ## First the habits, then the tools That is why starting an EA function with tool selection is risky. A platform promises order, an inventory promises insight. But choosing a tool is rarely the best first move. It may come later, once behaviours are established, or it may follow naturally from the standards already in place. Without those foundations, the tool is decoration. The tool becomes useful only after the organisation creates reliable signals for it to carry. The stronger starting point is to decide what behaviours matter. How is work described? How are systems documented? How are incidents recorded and metrics trusted? With those practices in place, almost any EA tool will do. Without them, no tool is enough. ## A catalogue of good intentions The gaps become clearest when things fail. Bugs may appear in an incident service, in a backlog, or in monitoring data. Some link to code changes, many do not. Others live only in chat threads or calls that leave no record. The story of what happened is fragmented. The question is whether EA can ever surface that mess. In an organisation with thousands of engineers and services, the fragments are too numerous and too fluid. Even if collected, they are too far from the work to act upon. At best, EA produces a catalogue of issues. At worst, it presents a tidy picture while the reality remains unresolved. Services are renamed, pipelines fork, flows change hands, but the inventory remains static. By the time a review comes around, the neat list no longer matches what anyone is running. From time to time organisations attempt to baseline the entire architecture. On paper this has value: a fresh view can cut through local detail and give a shared frame of reference. The harder truth is that the odds are against it. In complex organisations signals are inconsistent, flows change quickly, and inventories drift before the ink is dry. It is not impossible – with intent and discipline it can work – but the larger the scale, the faster the picture slips out of sync. This is more than a tooling gap. It is a continuity problem, the same theme explored in [*Keep the Heart Beating*](https://www.differentshelf.com/keep-the-heart-beating/). Without reliable behaviours, the system cannot heal itself, and signals are lost just when they matter most. ## Illusionary control The consequences are tangible. Teams launch products without load tests. Services collapse under traffic, leaving central infrastructure groups scrambling to provision capacity. EA may respond with more approvals and review boards, but it can slow delivery while obscuring accountability. A more effective response is to move responsibility closer to the work. Site Reliability Engineers embedded within product squads change the incentives. When the same team that builds a service is accountable for its resilience, shortcuts are felt immediately. Combine that with platform engineering – paved roads for deployments, observability, scaling – and you get both autonomy and guardrails. This does not mean architecture has no place. ADRs and technology advisory forums remain important, particularly where decisions cut across teams. Shared components, data platforms, and security standards demand coordination. Not everything can be “you build it, you run it.” The role of forums is to enable decisions, not replace them. They should lift the floor without pressing down the ceiling. The greater risk is control that exists only on paper. EA can present coherence while the underlying systems drift. Healthy architecture is visible in the work before it is visible in the inventory. ## When product meets architecture The product operating model puts accountability with teams. They own outcomes, they own technology choices, they move at the speed the market demands. Traditional enterprise architecture pulls in the other direction, promising coherence through standards, catalogues, and central oversight. At first glance, the two are in conflict. A modern EA function looks different. Its role is less about drawing diagrams and more about creating conditions for product teams to succeed without losing coherence. That means: - **Platforms as the enterprise layer.** Shared infrastructure, deployment systems, and observability form the common ground. Teams can move quickly, but they do so on a surface that is already hardened and cost-aware. This is the paved-road approach I have written about elsewhere: opinionated paths that still allow flexibility. - **Advisory forums, not approval boards.** Architecture discussions happen across teams, not above them. Decisions are still debated, but the aim is enablement, not gatekeeping. This is where the spirit of [*Refactor, Rewrite, Repeat*](https://www.differentshelf.com/refactor-rewrite-repeat/) applies: align on when change is worth the cost, and when to stay the course. - **Principals, not distant architects.** Architectural accountability sits inside domains. Principal engineers design and deliver in the same breath, carrying the trade-offs in their own systems. This echoes [*The Trunk is the Team*](https://www.differentshelf.com/the-trunk-is-the-team/): coherence comes from people and code evolving together. In this form, EA is not in conflict with the product model. It becomes the counterpart that makes it sustainable at scale. The tension does not disappear, but it becomes productive. Autonomy is preserved, coherence is maintained, and neither side has to pretend the other does not matter. ## Architecture as an outcome Perhaps the sharpest way to think about EA is not as a function at all, but as an outcome. The catalogue, the visibility, the flow of decisions across teams – these are what emerge when product engineering is healthy. When documentation is alive, dashboards are trusted, and incidents are linked, you already have architecture. It is visible without needing a separate group to describe it. None of this is to dismiss enterprise architecture itself. The aspiration to see the whole, to make sense of complexity, is a worthy one. The intent is good, but it is so hard to keep true that it rarely succeeds in practice. Fixing behaviours so they match the practices described in this blog is not simple either. It is one of the hardest jobs in engineering leadership. The difference is that it creates sustainable value. When teams write clear tickets, keep documentation alive, and trust their dashboards, the system changes. The product operating model reinforces this by placing accountability with the teams themselves. EA can show the picture, but without those behaviours the picture does not alter the reality. ## Where it fits, where it doesn’t Enterprise architecture does find steadier ground in more traditional IT domains. Areas such as infrastructure, office systems, and enterprise applications change at a slower pace. Standardisation here brings clear benefits: fewer platforms, simpler support, and lower cost. Catalogues can be kept current, and central oversight can add real value. In product engineering the fit is weaker. Releases are frequent, autonomy is essential, and dependencies shift quickly. Here coherence emerges through behaviours, not through central diagrams. The same mechanisms that make EA useful in traditional IT – catalogues, baselines, inventories – struggle to keep pace in a product model. ## Holding the balance As I wrote in [*Shouting in the Digital Office*](https://www.differentshelf.com/shouting-in-the-digital-office/), you know you are doing it right when you do not need to communicate explicitly. A healthy system does not need a chart to declare it green, or a committee to certify compliance. Its health is already visible in how it runs: in the reliability of deployments, the trustworthiness of dashboards, the clarity of commits and tickets. The role of leadership is to hold that balance. The intent to improve must not slide into control without substance. The desire for visibility must not become theatre. Enterprise architecture will almost certainly exist in some form. The question is not whether, but what shape it takes. The healthier shape is not a tower of charts, but an enabler of behaviours. ### As Fast As It Feels URL: https://www.differentshelf.com/as-fast-as-it-feels/ Last updated: 2026-08-24T16:18:09.000Z *If you are unfortunate enough to be caught in a traffic jam, knowing the reason why and getting regular updates makes the wait bearable. The journey takes just as long, but perception changes everything.* Applications face the same challenge. They carry dynamic content, live updates, and potentially time-sensitive transactions. All of it is expected to fit in a pocket, open instantly, and feel polished from the first touch. The delivery mechanism is irrelevant. Experiences must feel premium across multi-channel contexts, as explored in [*Beyond the WebView*.](https://www.differentshelf.com/beyond-the-webview-why-angular-apps-belong-in-a-pwa-twa-world/) A poor experience is poor everywhere. Players notice stutters, blanks, and resets. They also notice when every action responds immediately, every transition carries context, and every session restores seamlessly. Speed is not split into “real” and “perceived.” They are the same experience. If an app feels slow, it is slow. If it feels fast, it is fast. Metrics and perception collapse into one. The player cannot separate measured delay from perceived delay. Neither should the team. The good news is that nothing stands in the way. Modern web capabilities make these experiences entirely possible today. View Transitions, GPU-driven motion, and edge caching are widely supported. The question is no longer *can it be done*, but whether product engineering applies the patterns consistently. ## Principles of fast Three principles change everything. The first is to **optimise and anticipate**. The app should feel like it has loaded instantly, which is what SSR and selective hydration are built for, as explored in [*A Level Start*](https://www.differentshelf.com/a-level-start/). Real polish comes from anticipating what happens next. Prefetching and prerendering keep the following screen warm so navigation feels instant. This is what LCP, the Largest Contentful Paint metric, is intended to reflect. The second is to **respond immediately**. Every tap must trigger feedback within 100ms, whether that is a pressed state, a colour change, or a haptic tick. Without it, the player hesitates, wondering if their command was received. This is what INP, Interaction to Next Paint, measures directly. The third is to **stabilise the layout**. If text or images jump while loading, continuity is broken. Reserving space for assets is not a polish task, it is the foundation of speed. This is exactly what CLS, Cumulative Layout Shift, is designed to catch. Other principles support these three. Cache wherever possible, both at the CDN and in service workers, and treat anything that cannot be cached as a product decision rather than a default. Carry context forward with the View Transitions API. Keep all motion off the main thread using GPU transforms and opacity. At 60 frames per second, you only have about 16ms to do your work. Any JavaScript that interferes with rendering risks dropping frames and breaking the sense of smoothness. Libraries like Lottie are especially heavy because they run animations on the main thread. If most of the frame budget is spent there, there is little room left for input handling or rendering. The result is visible stutter. Make sure critical assets load first, and break long tasks into smaller ones so they never block responsiveness. ## What to do with waiting Waiting comes in flavours, and each needs a different response. The mistake is treating them all alike. For interactions under 100ms, the player should see and feel acknowledgement straight away. For waits up to a second, optimism wins. Show the change immediately, even if the network is still resolving. When delays stretch beyond a second, structure matters. SSR should give you a fast initial load, then a View Transition into a skeleton layout that mirrors the final page. It is always better to navigate to the next context instantly and show a skeleton than to hold the player in place behind a spinner. A skeleton says "you are here, and the details are on their way." A spinner says only "you are stuck, wait until we decide to move." One preserves momentum, the other arrests it. A skeleton preserves location. A spinner merely advertises waiting. Longer waits, three to ten seconds, need honest progress. Reveal content in stages, or show a progress bar that maps to real work. And anything beyond ten seconds must be treated with respect. A clear progress bar, ETA, or staged steps is essential. Offer an escape route, such as cancel or back. Silence is not an option. ## Things that slow you down Some patterns linger long past their usefulness. They make even fast systems feel fragile. The worst is the centre-screen spinner. Blocking the interface with a wheel tells the player only that time is passing, not what is happening. It halts navigation when the better choice is to move forward instantly and show the structure, even if the details are still arriving. Close behind are flashes of white. A moment of blankness between screens breaks continuity completely. SSR, skeletons, and the View Transitions API exist so players never see nothing. Then there are layout-shift hacks. Dropping in skeletons to cover unstable layouts is a bandage on a broken system. The only real fix is to reserve space and load predictably. And finally, the repeat offenders: JavaScript animation libraries that run on the main thread. Lottie is the most obvious, but GSAP, Anime.js, and scroll hijackers like Locomotive Scroll or ScrollMagic make the same mistake. They promise polish, but compete with rendering and input, dragging down responsiveness. For premium apps, GPU-driven CSS transforms and the View Transitions API are the safer path. Other mistakes worth cutting include placeholders that loop endlessly, shimmer effects that fake activity, and transitions stretched for style rather than clarity. These do not speed anything up. They only mask delay while eroding trust. ## Boundaries that keep you fast Guardrails are the boundaries that protect speed when choices get messy. Two stand above the rest. Flashes of white must be designed out completely. Sessions must restore, not reset. If a player foregrounds the app, it should be waiting where they left it, not restarting from scratch. Other boundaries matter too: fix layout shift at the source, keep motion off the main thread, preserve navigation context like scroll and filters, anchor scrolling so async content never jumps, be honest about long work with structured progress, measure in the field with INP, LCP, CLS, and RUM, align with accessibility settings, and keep behaviour consistent across flows. ## Continuity is memory Performance is also about memory. If the system forgets, the player pays in wasted effort. Continuity is what speed feels like after the first screen. The most obvious case is scroll position. Going forward and back should return you to the same spot. Resetting to the top feels like being thrown out of context. The same applies to forms and filters. Input and selections should remain unless explicitly reset. Inline expansions should stay open, and scroll anchoring should keep the viewport stable as new rows arrive. Above all, sessions should restore on foregrounding. Refresh what is stale, but do not wipe the state. A premium app should feel like it has been waiting, not starting over. Motion plays a role here too. If a screen slides in from the left, the natural gesture is to slide it back to the right. If that gesture is missing, the motion is misleading. Animation and interaction must align. ## Where trust breaks first Some flows can absorb delay. Login and payments cannot. They are judged with the least forgiveness. Login is the handshake. The form must appear immediately, not after a pause or a flash of white. Once submitted, the button must answer instantly with pressed state and haptic tick. And when login completes, the player should land back where they were, not at the front door. Payments are when money changes hands. Layout must be stable from the outset, so totals and fields do not jump. Actions like “Deposit” must acknowledge immediately, even if the network takes longer. And because payment gateways are rarely instant, structured progress is essential: Verifying, Authorising, Complete. If the flow is interrupted by backgrounding, the player must return to the same point, not restart. ## How time is told Speed is not only mechanical, it is also narrative. The way you frame and pace a wait changes how it is felt. The first lever is framing the pause. Microcopy such as “Checking availability” reframes a wait as progress. Skeletons that mirror real data make reveals feel aligned. Carrying a colour, icon, or heading across states ties the story together. The second lever is bending time. A micro-delay of 150ms before a screen swap can make the transition legible instead of abrupt. Progress bars that move steadily feel faster than jerky jumps. High-stakes actions like payments sometimes benefit from a slightly elongated acknowledgement, signalling care without dragging. And the final lever is owning the stall. Networks stall, gateways pause, services hiccup. Pretending otherwise makes players assume failure. Better to signal “This is taking longer than expected,” offer a cancel or retry, or escalate gracefully from skeleton to progress bar to explanatory text. Awareness preserves confidence. Micro-interactions play their part too. Buttons that depress, toggles that flip instantly, accordions that expand crisply, cards that follow the finger, and pull-to-refresh that rebounds smoothly. These details are not decoration. They are the rhythm of responsiveness. Anticipation and idle preloading buy real speed, as explored in [*Lazy Loading Isn’t Just Lazy It’s Late*](https://www.differentshelf.com/lazy-loading-isnt-just-lazy-its-late-the-web-deserves-faster/). Framing and shaping ensure that speed lands in a way players can follow and trust. ## How product engineering makes it real These patterns are not new. Most have been tried, tested, and proven across the industry. The difficulty is not in inventing them, but in applying them consistently as part of product engineering. Designers must frame motion and states with discipline, not flourish. The choice between a spinner or a skeleton is not decoration, but an expression of trust. Those who craft CSS must carry the weight of responsiveness. Every pressed state, every transition, every animation needs to run on the GPU and respect reduced-motion settings. Consistency here is as important as colour or typography. Engineers must treat continuity as part of correctness. Preserving scroll, restoring sessions, anchoring layout are not extras, but part of what it means for the system to work. Leadership must connect these threads. Performance principles should sit in design systems, component libraries, and engineering guidelines, not as tribal knowledge. Tooling, linting, and review processes should enforce them the way accessibility and security are enforced. As I argued in [*Trunk Is the Team*](https://www.differentshelf.com/the-trunk-is-the-team/), orchestration and discipline are cultural before they are technical. Premium is not achieved by heroics. It is achieved by ensuring the patterns for speed are known, accessible, and non-optional. When every part of product engineering holds up its side, the result is an application that feels fast because it is fast. ## As fast as it feels Real speed and perceived speed are the same thing. The job is not to decorate slowness, but to remove it, then carry attention forward with continuity. SSR gives an instant first view. The View Transitions API keeps context flowing. Haptics confirm input was heard. Skeletons and progress indicators set honest expectations. Remembering scroll, filters, gestures, and sessions prevents wasted effort. Micro-interactions carry responsiveness into the smallest touches. Framing and shaping make time comprehensible rather than empty. These are not illusions, and they are not future promises. The modern web already provides the machinery: SSR, View Transitions, CSS transforms, service workers, and caching strategies that scale. When teams use them with intent, the result is an application that feels fast because it is fast. ## Postscript With only 16ms available for every frame, can we really afford client-side tracking, real user monitoring, session-replay tools, and the weight of third parties on top? The answer is rarely straightforward, but the trade-offs deserve as much scrutiny as the animations and transitions themselves. As explored in [*Watching the Watchers*](https://www.differentshelf.com/watching-the-watchers/), the systems we add to measure experience can just as easily become the reason it slows down. ### Refactor, Rewrite, Repeat? URL: https://www.differentshelf.com/refactor-rewrite-repeat/ Last updated: 2026-08-24T16:18:08.000Z *A garden rarely needs razing to the soil. Beds can be reshaped, weeds pulled, and plants lifted and replanted. The temptation to clear everything and start fresh is strong, especially when paths are overgrown and the planting has grown tangled, but razing also strips away the nutrients in the soil that took years to build.* Netscape Navigator is the cautionary tale. Faced with a codebase they considered beyond repair, the company paused, threw it all away, and began again. By the time the rewrite was ready, the market had shifted. Microsoft kept iterating. The result is well known. The lesson is not as blunt as “never rewrite.” After years of compromises and mounting debt, a fresh start can look reasonable. At times it is. What has shifted is the balance of those choices. Many of the hardest parts of refactoring are now tractable with AI. Tests can be scaffolded, dead code removed, and migration plans drafted in days rather than months. More than that, the work can become continuous. Code can be scanned, tickets generated, fixes proposed, even errors from real user monitoring surfaced automatically and used by AI to generate bug fixes. This is already edging into reality today, not a distant prospect. Refactoring turns from an extraordinary effort into part of the rhythm. AI makes both paths cheaper. It does not make their consequences equal. The same loop can extend beyond hygiene. The principles set out in [*As Fast As It Feels*](https://www.differentshelf.com/as-fast-as-it-feels/) \- continuity, seamless state, polished transitions, perceptual speed - can be applied directly to a codebase. AI agents could scan for violations, raise tickets, and link them to the relevant components. Another agent could pick them up, generate fixes, and run them through CI. Frustrations that once devalued a system can be codified and channelled into improvement. Continuity no longer means tolerating flaws; it means using them to drive progress. AI also lowers the barrier to rewriting. A new system can be scaffolded quickly, with content migrated and domain rules mined from the old code. The difference lies not in what is possible, but in what follows. Economically, rewriting almost always means running two tracks at once. The old system still carries players while the new one takes shape, and the overlap usually lasts longer than imagined. Refactoring avoids this split by folding improvements into what is live, keeping focus single-threaded. A rewrite accelerates the beginning. It does nothing to shorten the years of overlap. ![Two timelines compare refactoring on one live path, where small changes reach players continuously, with a rewrite that runs an old product and new system in parallel until rules and edge cases are migrated and a final cutover occurs.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/refactor-rewrite-overlap-ds-2026.png) From a risk perspective, a rewrite is an all-or-nothing bet. Success may bring a fresh foundation, but failure can set back years of effort. Refactoring moves in smaller steps, each reversible if it proves wrong. The downside is limited, the upside accumulates. From a human perspective, existing systems carry knowledge that is easily lost. However untidy, they embody years of quirks, regulations, and edge cases. Some can be surfaced by tools, but not all. Refactoring keeps that knowledge close and, by delivering improvements into the live product, gives teams visible wins. Shorter loops encourage engineers to tidy as they go. Visible results sustain both momentum and morale. The old code contains decisions nobody remembers making. The trade-off is not between progress and stasis but between continuity and reset. Refactoring compounds improvements into the running product. Rewriting resets the clock. There are moments when a reset is necessary, especially if the architecture is broken beyond repair or the product’s shape has moved too far from its foundations. Yet those moments are rarer, and they deserve more scrutiny than they once did. AI lowers the barrier for both rewriting and refactoring. Only one of those paths preserves the soil that has been enriched season after season, while continuing to deliver new growth to the players already in the garden. The choice is less about technology than about momentum. Stop, and the market moves on. Keep improving, and the product moves with it. ### Not Just Upwards URL: https://www.differentshelf.com/not-just-upwards/ Last updated: 2026-08-22T22:55:16.000Z *Promotions draw the spotlight: simple to announce, easy to celebrate, designed to be visible. Without performance management, though, a company can drift into becoming a de facto pyramid scheme. No one designs it that way, but the effect can be much the same.* Succession is often framed in terms of who is ready to step up, who is being prepared for more. But succession only works if the whole system is healthy. If attention falls mainly on those moving up, the organisation risks losing strength elsewhere. Succession without performance management moves people upwards while weakness accumulates underneath. Many organisations frame performance management as an HR process. Reviews, calibrations, ratings. These provide fairness and consistency, and they are essential. But HR alone rarely catches the real signals. They sit too far from the day-to-day work. Only in partnership with leadership can those signals be surfaced and acted on. Neither side is able to do it on their own. ## Latency in the system Underperformance often behaves like latency in a system. Ten milliseconds here, another fifty there, each delay seems tolerable, but together they accumulate into something players notice. In engineering, the response is to look for the bottleneck, refactor the code, or adjust the design. Underperformance deserves the same treatment. Not punitive, but systemic. The engineering examples are familiar. Builds that fail again and again. Defects that resurface release after release. Branches destabilised by merges. Environments that take too long to provision, slowing every attempt to test and ship. Left unresolved, these bottlenecks shape the whole system’s pace. And just as systems suffer from repeated drag, so too can people. That is where the signals shift from technical to human. ## Signals in the noise If technical latency slows a system, the human signals show up in how people work together. They are rarely dramatic, more often small behaviours that wear on momentum. Work that changes hands too many times before it is finished. Meetings that stretch on without producing a decision. Effort poured into tasks that stall before they are usable. Ownership that drifts rather than being taken. On their own, these moments may seem minor. Taken together, they shape the rhythm of a group. Patterns emerge: reluctance to step outside a narrow role, defending process instead of progress, raising problems without bringing enough clarity for others to help. These rarely surface in quarterly reviews. They are felt in the daily flow, in the way one person’s habits affect many. ## The price of progress Failure itself is not the problem. Trying and failing is often the price of progress. Features that miss, prototypes that break, experiments that do not hold up. These are how teams learn and how products improve. Failure teaches once. Underperformance repeats the lesson without learning it. Underperformance is something else. It is not the one-off failure that teaches, but the repeated behaviour that drags. It is not an experiment that did not work, but the unwillingness to adapt after it did not. Progress is built on the first. The second quietly erodes it. ## The work of lifting people Leadership is not only about removing obstacles for the team. It is about lifting people up, one by one. Coaching, direct feedback, and deliberate development are the first steps when performance falters. Many engineers do not lack ability. What they need is context, guidance, or the confidence that comes from being trusted with more. The role of a leader is to make those opportunities visible and to support improvement when it is possible. That is part of developing people, and it is as important as designing systems or delivering features. That support often works, and when it does not, it is not necessarily a failure. There comes a point where coaching has been tried, where feedback has been clear, and where the pattern does not change. At that point the responsibility shifts. Sometimes that means recognising it is time to part ways. Continuing to avoid the decision does not develop the individual, and it harms those around them. ## The drag of avoidance Avoiding a performance conversation with one person may feel like kindness. But avoidance often creates a quiet unfairness. The strongest performers absorb the extra load, the team’s confidence thins, and the individual at the centre is left in uncertainty. Avoiding one hard conversation quietly assigns the consequences to everyone else. Slippage often starts here. An engineer may be capable in their domain but refuse to work outside it. Every cross-cutting change falls to someone else. Meetings stall because more time goes into defending process than addressing problems. Colleagues shoulder the work for a while, but patience wears down. Without a reset, everyone remains in limbo. The reverse is also true. Some individuals face the same friction but respond to clear expectations. Once the standard is visible, behaviour can shift quickly. What was a source of frustration becomes manageable, and the group regains rhythm. At scale, inconsistency becomes its own form of unfairness. If some leaders avoid the hard calls while others do not, the organisation teaches two different lessons at once. Direct conversations make the standards visible. They give someone the chance to adjust. If adjustment is not possible, they make the outcome clear. Avoidance does neither. ## Flow to the player For a product engineering organisation, this is not a side concern. It sits in the same category as architecture, developer tooling, and product direction. Each affects flow, and each ultimately reaches the player. When underperformance is ignored, output slows, trust weakens, planning becomes unreliable, and momentum itself can grow fragile. What begins with an individual spreads into the team. Handled well, the opposite happens: standards are clearer, weight is shared more fairly, and products arrive with fewer surprises. Scaling performance management is less about adding process and more about rhythm. When expectations and conversations happen predictably, they stop being exceptions and become part of how the organisation works. That consistency is more than a cultural good, it is often a competitive advantage. The point is not comfort. Done well, performance management creates momentum for the organisation and the players. Momentum can come from a promotion, a change in behaviour, or an exit. ### Lowest Common Denominator Thinking URL: https://www.differentshelf.com/lowest-common-denominator-thinking/ Last updated: 2026-08-24T16:18:07.000Z *Imagine a dish cooked for a long table of diners. One cannot tolerate heat, so the spice is left out. Another avoids salt, so the seasoning is dropped. By the time it is served, everyone has something on their plate. For the few with strict diets, it works. For the many, it is less satisfying.* That is lowest common denominator thinking. You design for the most restrictive case, and everyone else pays the price. A shared platform should make difference cheaper, not sameness compulsory. For global companies this is not a theoretical risk but a daily trade-off. Expanding into new markets demands both speed and local nuance, while internal processes must balance safety with agility. Without strong product engineering foundations, the choice often collapses into two extremes: flattening everything into something consistent but less distinctive, or forking repeatedly and carrying the long-term cost of divergence. Both are understandable, but both are limiting. They appear in two places: in how products scale across markets, and in how internal processes slow delivery to the pace of the weakest part. Product engineering is the mechanism that turns leadership intent into outcomes. ## Markets Made Safe Global products often lean toward the lowest denominator to make scale possible. Local quirks are set aside, campaigns adjusted, and pricing simplified into a single model. This can be efficient, but it risks reducing distinctiveness and sometimes mismatching local conditions. Digital experiences follow the same line. By designing for the poorest connections, players on fibre and 5G are never offered the richer experiences their networks could support. The outcome is usually safe and consistent, but rarely as engaging as it could be. This is not the result of weak ambition. More often it reflects the constraints of the underlying foundations. When the technology cannot support variation at scale, leaders sensibly choose simplicity. What looks like a brand decision is often a product engineering decision in disguise. ## Engineering That Enables Localisation at Scale When products expand across many markets, the choices often fall along a spectrum. Flattening creates consistency but reduces local resonance. Forking gives autonomy but risks duplication and divergence. Both can make sense in context. Selective forking, in some cases, may be the least damaging option. A market might need its own frontend or integration to meet local conditions, and in those moments forking can be the pragmatic choice. The challenge is that forks carry costs: duplication, divergence, and added maintenance. Whether they remain exceptions or become permanent fixtures depends on how the organisation chooses to manage them. A fork is a decision with interest, not a moral failure. Modern product engineering makes it possible to stay in the healthier middle ground, where local teams can innovate without losing coherence. Feature flags and experimentation prove what works before scaling. Modular architecture, design systems and well-governed APIs create a consistent base with space for variation. A [monorepo](https://www.differentshelf.com/tag/monorepo/) and inner sourcing allow local teams to contribute directly to the shared platform, strengthening the whole while retaining local capabilities. Shared telemetry, automation and observability provide stability at speed. Platform teams enable rather than block. And underpinning it all is the courage to retire brittle systems, so the new practices do not sit on top of old fragility. Handled this way, localisation does not force flattening or unchecked forking. It allows variety without chaos, distinctiveness without sprawl. The commercial pay-off is clear: faster entry to new markets, higher likelihood that local patterns convert, fewer outages and lower operating costs over time. With the right foundations, product engineering becomes the enabler of localisation at scale. ![Four stages show product difference moving from one flattened output, through a shared foundation with named variation, to a deliberate fork for a binding constraint and finally to unchecked paths where every shared change multiplies.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/variation-continuum-ds-2026.png) ## Internal Processes That Slow Us The same pattern appears in processes and workflows. Rules written for rare exceptions end up applied to everyone: a brittle service halts releases across the estate, a sign-off designed to avoid risk stretches review cycles into weeks, compliance forms multiply risks that will never apply. Tooling can make it worse, with each team insisting on its own flavour of platform until the organisation bends under the friction of sprawl. Talented engineers are slowed to the pace of the least capable workflow. What begins as a desire for fairness or flexibility ends up as inertia. The way forward is not carelessness but discipline. [Trunk-based development](https://www.differentshelf.com/tag/trunk-based-development/) keeps code moving without endless merge bottlenecks. Automated pipelines replace manual gates with fast, reliable checks. Observability makes it possible to fix issues quickly rather than slowing everyone to prevent them in advance. Practices like these shorten lead times, stabilise quality and reduce rework. That compounds into faster time to market, better customer experience and leaner operating costs, while keeping the safety bar intact. ## Respect Without Uniformity Whether in product markets or internal processes, lowest common denominator thinking often disguises itself as compromise. It looks like balance. It looks like fairness. But compromise can leave everyone worse off. Chris Voss once pointed out the flaw: if one traveller wants Paris and another Rome, splitting the difference lands you in Geneva airport, a place nobody asked for. There are moments when the denominator must set the pace. Accessibility, inclusivity, safety, regulatory compliance. In those cases, serving the few serves us all. The denominator should follow the constraint, not the least capable implementation. Many other times the real driver is inertia. Fragile systems are preserved not out of respect but out of fear, and processes end up slowed as a result. True respect is not giving everyone their own way. It is giving each what they are capable of, within a clear frame. That means differential builds rather than infinite variation, common platforms with space for local nuance, and teams trusted to move at their natural pace while staying aligned on standards and direction. Good governance sets the floor. Good product engineering sets the pace. ## Choosing the Denominator Leadership is about deciding where to place the denominator on the spectrum. At one end sits complete flattening, where everything looks the same and local resonance is reduced. At the other sits unchecked forking, where every market goes its own way and the organisation carries the cost. Somewhere in between lies the balance: a shared base with room for variation, enabled by modern product engineering and strengthened by contributions from local teams. The same choice plays out in processes. Rules can be written to protect against the rarest failure, but if applied to everyone they slow the entire company to its weakest point. The art of leadership is recognising when protection is essential, such as safety and regulation, and when it is inertia in disguise. The lowest common denominator is tempting because it feels safe. Nobody is excluded, nobody is criticised. Yet it leaves you with meals that are filling but unremarkable, products that are consistent but less distinctive, and processes that teams stop believing can change. The meals that linger in memory are the ones with flavour, shaped to each individual yet shared at the same table. The real opportunity is optionality. Invest in product engineering and you expand the menu of choices. Local teams can innovate while still strengthening the platform. Forks can be used when needed, but deliberately and with awareness of their cost. And leadership can choose the denominator with confidence, knowing the organisation has the flexibility to adapt, the distinctiveness to stand out, and the efficiency to keep operating costs under control. ### Cassette Logic URL: https://www.differentshelf.com/cassette-logic/ Last updated: 2026-08-24T18:02:54.000Z *On my desk is equipment that sounds poor by any objective measure. Which makes it a reminder that the human experience is not objective either.* A pair of Audio Innovations 2nd Audio monoblocs glow softly, valves running hot enough to warm the air. They are driven by a Series 1000 pre-amp, a piece of kit from the late eighties when British valve design was quietly resurging. The sound is alive, not clinical. The valves hum faintly, flare into life, and remind you that electricity is moving through glass. The speakers are even less ordinary. They were built by a colleague at Nokia, who kept a sideline in loudspeakers. He called them Wave Design. They are triangular half-space cabinets, cut to sit flush against a wall so the geometry itself becomes part of the acoustics. They are the only set of full-range drivers I own, and I love them not because they are flawless but because I know who made them. The source for all of this is a Nakamichi RX-505E cassette deck. It has what must be the most elegant auto-reverse mechanism ever made: Unidirectional Auto Reverse, where the tape does not just switch direction but actually flips itself over and slides back into place. It is slow, balletic, mechanical theatre, good enough to earn a cameo in *9 ½ Weeks*. Inside are three discrete Crystalloy heads and Dolby B and C noise reduction, all tuned for fidelity. By the numbers it is still behind digital. The frequency range is narrower, and the dynamic range is compressed compared to a stream. No metric puts it ahead. Fidelity to a memory is not the same as fidelity to a signal. And yet the experience is different. I can tell streaming services exactly what to play, and often do. I listen to plenty of niche recordings that the algorithm would never surface. But tapes pull me in another direction. A car boot box, a forgotten mix, a release I would never have chosen online. The selection is not better, it is simply different, and that difference shapes the way I hear it. Context matters most. The music I listen to on cassette was produced with cassette and FM radio in mind. That was how people first heard it. The mixing, the mastering, even the dynamic range were shaped by those limits. To play those recordings today in lossless quality is not wrong, but it removes part of the frame they were created for. Psychologists would say this is no accident. Music is one of the strongest triggers of autobiographical memory, sharper than photographs or words. These memories arrive with detail: where you were, who you were with, how it felt. Often they come uninvited, pulled out by a song you would never have thought to choose. That is not just nostalgia, it is how memory works. Music ties context to identity, stitching the present back to the past. I remember it with Oasis too. *What’s the Story Morning Glory?* came out when I was thirteen, and for a short-lived phase I had a long walk to play basketball at a nearby American airbase. It must have been forty minutes each way, though at the time it felt longer. The walk didn’t matter, because the album was the journey. I could nearly listen to it end to end there and back, and I probably did every single time. What I heard then, decidedly lo-fi, is still how I recall it now. Ritual plays its part too. Valves warming, the clunk of a tape loading, the pause before sound begins. These are small ceremonies that give weight to listening. I like similar moments in other corners of life: a safety razor on the skin, a newspaper folded across a table, a record arm lowered by hand. They are not efficient, but efficiency is not the point. Still, ritual alone can tip into performance. It is context that keeps the experience grounded. Ritual earns its place when it restores context, not when it performs taste. It makes me think of film. I adore watching *Lawrence of Arabia* remastered from the original 65mm in all its glory on a 4K laser projector. That feels right, because nothing has been altered. It is simply a magnificent print of what was always there. *Star Wars* is different. The constant revisions change what people originally saw. The most obvious case is Han Solo in the cantina. In 1977 he shot Greedo without hesitation. Twenty years later the scene was re-edited so Greedo shoots first, softening Han’s edge. The picture is sharper, the effects cleaner, but the character is no longer the same. A version of history gets overwritten. It is the same with albums. A good remaster can be like *Lawrence of Arabia* on a fresh 65mm print: clearer, cleaner, but faithful to what was always there. You hear details that were hidden before, but the intent remains intact. Other remasters feel more like Han Solo in the cantina. Compression gets pushed, dynamics flattened, mixes reshaped for loudness or fashion. What comes out the other end is not simply the same album in higher resolution but a different statement altogether. Cassette sidesteps that entirely. What it preserves is not only the music but the hand of the people who made it. The engineers who mixed for radio, the producers who worked within the limits of tape, the friend who pressed record on a mixtape. Their handwriting still lingers on labels, their choices remain etched into the signal. To hear it now is to hear them, not just the song. That unpredictability is part of the appeal. I have just ordered a job lot of “rock” cassettes from the seventies and eighties. One hundred and twenty-five albums, bought on eBay under a listing that simply read: *Large Collection Of 125x Rock Album Cassettes Job Lot 70’s 80’s 90’s*. No track list, no photographs beyond a couple of blurry spines. I recognise some names, but most will be a surprise. And I do not mind. That is the joy of it. When else would you buy a hundred records with such abandon? Right now the cassette playing is Annie Lennox’s *Love Song for a Vampire*, on a mixtape my wife made when she was a child, or maybe her brother did. Either way it has been sitting in a box for thirty years. A song about something that never dies but is already dead, still playing now through valves and tape. It sounds spectacular, and I would never have thought to listen to it otherwise this morning. That is the point. Cassette brings you things you did not choose but which arrive all the same. A box find, a forgotten mix, a soundtrack from the past. Like this tape, they survive not because they are constantly remade, but because they keep showing up in the form they first took. ### Beyond the WebView: What’s Next? URL: https://www.differentshelf.com/beyond-the-webview-why-angular-apps-belong-in-a-pwa-twa-world/ Last updated: 2026-08-24T17:32:58.000Z *WebViews are like training wheels or stabilisers if you're British. They kept apps upright, got them into the Play Store, and helped teams ship quickly across devices. But training wheels are not built for speed. They wobble, drag, and hold back the ride. Today the web is balanced on its own. PWAs and TWAs are racing bikes - fast, responsive, and able to go where WebViews never could.* The wrapper should distribute the experience, not become a second place to implement it. For a long time, wrapping a website in a WebView was the default way to get a mobile app into the Play Store. It was pragmatic, cross-device, and allowed teams to “ship an app” without maintaining separate code. But the web has matured. Between Progressive Web Apps (PWAs), Trusted Web Activities (TWAs), and differential builds, we can now deliver web applications that feel native, update instantly, and tap into features once exclusive to bespoke native code. This is especially true on Android, where WebViews lag behind Chrome and hold back performance. On iOS the picture is different: Safari’s WKWebView underpins every browser, so performance is more consistent. The benefit there is less about speed and more about avoiding duplicate logic across two codebases. On Android, it’s both: better performance and a single implementation. ## From WebViews to the Web Itself A WebView is essentially a cut-down browser embedded in a native shell. It works—but comes with baggage: inconsistent performance, missing APIs, and the need to ship APK updates for every change. Heavy web apps—full of JavaScript and dynamic UI logic—often falter under this approach. PWAs let your app run with purpose: enhanced by a manifest, service worker, offline support, installability, and background sync. Package that PWA for the Play Store with a TWA, and it runs in full-screen Chrome—free of browser UI. You inherit Chrome’s performance optimisations, security, and modern APIs. The PWA is your enhanced app. The TWA is the means of distribution, delivering it with native-quality experience. ## Native Where It Matters You may worry that PWAs and TWAs forego native integrations. They don’t. A TWA is still an Android app project. That means tools like AppsFlyer (for attribution), GeoComply (for regulatory geolocation), or Firebase Crashlytics (for crash reporting) work exactly as they would in a native app — side by side with your web UI. Native hooks can remain native without making the whole player journey native. In effect, the PWA maintains the user-facing experience, while the TWA wrapper quietly supplies native hooks for apps where compliance, measurement, and analytics still matter just as much as performance. ## The Modern Web Can Do More Than You Think Misconceptions persist—teams often underestimate what PWAs can achieve. Today, they can: - Authenticate with biometrics, using WebAuthn and device-level features - Send push notifications and leverage background sync - Provide haptic feedback with vibration APIs - Accept payments through Google Pay and Apple Pay - Function offline thanks to intelligent service-worker caching It isn’t second-class—it’s first-class, even before you consider how much performance you can unlock with modern build strategies. ## Even Faster: Differential Builds + TWA = Near-Native Speed That’s not just theory. [*Moving Beyond the Lowest Common Denominator: Differential Builds for Near-Native Web Performance*](https://www.differentshelf.com/moving-beyond-the-lowest-common-denominator-differential-builds-for-near-native-web-performance/) explains how single-build strategies—targeting ES2015 for compatibility—punish modern devices. Instead, serving ES2022 bundles to Chrome (and therefore TWAs) unlocks near-native execution speeds. Running modern bundles in TWAs means faster parsing, lighter JavaScript, quicker time-to-interactive, and smoother UI rendering. On capable devices, web apps feel as fluid as native. ## The Performance Case on Android On real-world Android devices—around the 75th percentile, not the latest flagships—moving from a WebView wrapper to a PWA inside a TWA delivers a conservative 20% improvement in perceived performance. That 20% shows up as faster start-up, smoother navigation, fewer frame drops, and snappier responses. With differential builds layered on top, the experience pushes further still: near-native smoothness on mid-range hardware. On iOS, the equation is different. Every browser is powered by WKWebView, so performance is less of a concern. The gain there is simplicity: a single web implementation powering both platforms, rather than mixing separate web and native logic. On Android the runtime changes. On iOS the ownership model changes. ## When WebViews Still Belong WebViews still serve narrow use cases: ultra-strict native–web bridging, apps that must be fully offline from day one, or full control over the network stack. These are edge cases. For most consumer-facing web apps, the advantages of PWA + TWA with modern builds are overwhelming. ## Conclusion PWAs + TWAs let web teams deliver applications that are faster, safer, more capable, and more future-proof than their WebView equivalents. The PWA is your enhanced, installable web app. The TWA brings it into the Play Store, while preserving access to native SDKs like AppsFlyer or GeoComply. Differential builds on top of that unlock near-native speed. In short: on Android, moving beyond WebViews makes your app feel \~20% faster for most users, while on iOS it avoids duplication and ensures a single implementation of logic across platforms. For web teams at scale, the future isn’t in wrapping the web—it’s in embracing it. And if you’re wondering what this shift means for native developers, the answer is not redundancy but re-focus. Their work is becoming more important in a different way: embedding products into the surfaces of the operating system itself. That’s the subject of the next essay: [*Why Native Still Matters*](https://www.differentshelf.com/why-native-still-matters/). ### When the Numbers Get Worse but the App Feels Faster URL: https://www.differentshelf.com/when-the-numbers-get-worse-but-the-app-feels-faster/ Last updated: 2026-08-24T17:32:59.000Z *We like to think performance numbers are simple. Train harder, the scale should drop. Yet anyone who has exercised knows the story is not so neat. You can feel fitter while the numbers move the wrong way. App performance is the same. Sometimes the metrics get worse at the very moment the experience gets better.* *That is what happens when we move from many sub-domains to a single domain.* ## The Old World For years our products lived apart. Sports, Gaming, Cashier, Account, Promotions. Each sat on its own sub-domain, each with its own lifecycle. When a player moved between them, the app treated it as a fresh start. Everything was loaded again. Code, images, styles, scripts. On a mid-range device the first load might take eight seconds. Switching to another product triggered the same process, only a little shorter. Three to six seconds on each move before the new screen was ready. The metric known as Largest Contentful Paint, or LCP, tracked these loads. LCP is simply a measure of how long the app takes to load. Every switch left a footprint. The data matched what players felt: long waits that repeated again and again. The old metric counted repeated waits because the architecture kept creating them. ## The New World Now imagine all those sub-domains joined together. Sports, Gaming, Cashier, Account and Promotions under one roof. The player can move between them without starting over. The result is immediate. Transitions are at least forty percent faster, and in some cases closer to sixty. What once took three to six seconds can often fall to two, sometimes less. The app reuses what is already in memory. The connection stays open. The experience feels smoother and less brittle. But here the paradox begins. Because those movements are no longer seen as new loads, the metric no longer counts them. Only the very first entry into the app produces an LCP. ## The Paradox of Fewer LCPs By joining the sub-domains together we have stripped out wasted effort. Players are no longer forced through repeated reloads. The transitions are quicker, the experience is better. Yet the metric suggests the opposite. Why? Because LCP only measures how long the app takes to load. In the old world every switch between sub-domains was counted as another load, so the dataset was full of them: the heavy first entry plus a series of smaller reloads. In the new world only the first entry is counted. All the later gains, the faster moves between products, vanish from the data. They are erased from view. **That has a strange effect. Before, the heavy first load was softened by the smaller reloads. Averaged together, the number looked manageable. Now there is nothing to balance it. The only figure left is the largest one. On paper the number rises, while in practice the app is faster.** The optimisation removed waits that the metric could no longer see. ## A Quick Example Numbers vary. They change with devices, networks, and even time of day. But the shape of the story is the same everywhere. In the old world, the metric was dominated by smaller reloads. In the UK we saw p75 LCP at about **2.7 seconds**. In the US it was closer to **3 seconds**, and in LATAM around **9 seconds**. Those were the figures that made it into the chart because most navigations were short reloads. After consolidation, only the heavy first load is counted. The chart now shows bigger numbers: in the UK about **4.6 seconds**, in the US closer to **5**, in LATAM around **13–15**. On paper, the app looks slower. But the total journey is shorter. With five navigations between products, the waiting adds up differently: - In the **US**, it fell from roughly **18 seconds** to between **9 and 13**. - In the **UK and Europe**, from about **16–18 seconds** to **9–15**. - In **LATAM**, from about **36–40 seconds** to **22–28**. The savings are real: around **4–9 seconds in the US**, **5–9 in the UK and Europe**, and **8–14 in LATAM**. The paradox is simple. The reported LCP goes up, yet the experience is faster and smoother. ![Three regional comparisons show that a slower reported page metric can coexist with less total waiting across five product switches. Total waiting falls by 5 to 9 seconds in the US, 2 to 8 seconds in the UK and Europe, and 10 to 16 seconds in Latin America.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/journey-waiting-time-ds-2026-1.png) ## Why It Matters If you only look at the graph, you might believe performance has deteriorated. In truth it has improved. The repeated pauses have been removed. What was once six moments of waiting is now only one. It is like the difference between a stopping train and a direct service. In the old world, every switch between products was another halt along the way. The train still got you to your destination, but each stop added minutes to the journey. In the new world, the train leaves once and runs straight through. The departure time on the timetable looks the same, but the arrival is sooner because the intermediate delays are gone. That is the change consolidation brings: the journey feels faster because it is faster, even if the single number we track does not show it. ## Why It Is Hard to Measure Part of the difficulty is that the tooling has not caught up. LCP was designed for traditional page loads. It is good at measuring how long the app takes to load the first time, but it cannot yet see the faster transitions inside an app that stays alive as players move around. There is work under way in browsers to improve this. New standards will allow us to capture those “soft navigations” and report a fresh LCP-like number for each one. But those standards are not widely available yet. For now we rely on Core Web Vitals because they are the best we have. They give us a consistent way to compare ourselves with competitors, market by market, and to track our own progress over time. That consistency matters, even if it comes with limitations. Everything in life is a trade-off. Metrics are no different. They are imperfect, but they give us a common language until better tools arrive. ## The Broader Lesson Metrics are still valuable. They let us track, compare and hold ourselves to account. But they are always a proxy. They simplify reality into a single measure, and sometimes that simplification hides the progress that matters most. A proxy becomes dangerous when the system changes what the proxy is able to observe. Consolidating into a single domain does more than tidy the architecture. It changes the experience for players, and it changes what our tools are capable of measuring. Until standards evolve to capture these in-app movements, we must be careful not to confuse a rising number with a worsening experience. Because for players, the app feels faster. And that is what counts. ### Watching the Watchers URL: https://www.differentshelf.com/watching-the-watchers/ Last updated: 2026-08-24T16:18:06.000Z *Performance of consumer applications depends as much on what you permit around the app as what you engineer inside it. Cloud, caching and better algorithms can all help, yet a handful of observers can still undo the gains.* *Think of a chef working in a kitchen crowded by people in suits with cameras. They are not cooking, but they take up space, interrupt movement and record everything. Analytics, monitoring, replay, testing and pixels do the same. Each looks harmless alone. Together they leave the player waiting longer for the meal.* ## It isn’t the download that hurts, it’s what runs afterwards Observers do not just stand quietly in the background. They run. They allocate memory, spin event listeners, intercept DOM changes, capture clicks, serialise state. On constrained devices, where performance matters most, those cycles are precious. Consumer applications rely on finely tuned event cycles to stay responsive. The rhythm works when the application owns that cycle. Add code that listens, rewrites or interferes and the music falters. Each observer adds to the main thread’s bill. What looks like a simple metric call becomes an ongoing cost to the player experience. Third-party code becomes part of the product when it competes for the player's main thread. Native apps are not immune. Too much monitoring or replay will slow them down too. The difference is that consumer web apps make it easier, sometimes too easy, to add just one more observer. Drop in a tag, paste a snippet, and the change goes live. Business teams come to depend on that flexibility. A/B testing tools encourage it even further, promising instant experiments without a release cycle. The bill is not paid in infrastructure but in player experience. ## Why the real impact is hard to see, and harder to measure CPU load is slippery. You can see how big a bundle is, but not how hard it makes the device work once it runs. The way a tracker hammers the garbage collector or clogs the event loop rarely shows up in the tools we use to measure performance. That makes it easy to rationalise. It is only 20KB. It does not affect Time to First Byte. It loads after the main app. Players do not care about the loading strategy of an observer. They care that scrolling feels laggy, or that depositing money freezes for a second too long. ## Tools built to watch players can end up reshaping the game itself The irony is that many of these observers exist to measure player behaviour. Yet by their very presence they distort the very thing they claim to observe. The tools often undermine the thing they promise to improve. A session replay can stutter because of the replay recorder itself. An observer that changes the journey has become a participant. The burden falls hardest on the margins. Players on older Android phones, or in regions with weaker devices and networks. For them, what looks like a minor delay in profiling becomes a visible hitch in reality: a stutter, a pause, a sense that the app is not keeping up. ## Built for one world, dropped into another It is worth saying that many of these tools were designed with a different world in mind. Traditional e-commerce sites are multi-page applications. A tracker records a page view, logs a funnel step, measures conversion, then the page is torn down and a new one takes its place. We are not running that kind of site. We are running a complex consumer application, a single-page app that stays alive for hours at a time. It maintains state, updates dynamically, and depends on smooth cycles of interaction. Most observers do not make this distinction. They treat a long-lived SPA the same as a short-lived MPA. The result is noise, duplication and overhead in places where the margin for performance is already slim. ## The hidden costs of long life These differences play out in ways that anyone can recognise. A long-lived single-page app, weighed down with observers, often develops problems that a traditional multi-page site does not. Memory leaks build up until the tab becomes unstable. Crashes appear after hours of use. Bringing the app back to the foreground takes seconds instead of being instant. None of these issues are caused by one observer in isolation. They are the cumulative effect of watchers and trackers that were never designed to run side by side in a stateful, long-lived environment. In an MPA world, a refresh clears the slate. In an SPA, the burden just accumulates. ## For technical readers: how observers throw frameworks off rhythm Observers do not just consume cycles. They interfere with how frameworks detect change. In Angular, for example, Zone.js tracks asynchronous events so the framework knows when to run change detection. Many monitoring tools, from RUM agents to session replay libraries, hook into the same DOM mutation and event APIs. The result is a storm of signals. Angular ends up checking components far more often than is necessary, sometimes on every keystroke or mouse move. In practice that can double or triple the number of change detection passes during a simple interaction. A tap that should settle in around 250 milliseconds, quick enough to feel responsive, can stretch to 750 milliseconds or more once multiple monitoring tools are running. At that point the delay is obvious to the player. It gets worse when multiple tools overlap. A replay recorder and a RUM agent both listening to DOM mutations means the same work is duplicated, often two or three times. Each serialises the same event stream, while Angular burns cycles responding to the noise. What begins as background measurement becomes foreground friction. We have tried to mitigate. One approach is to use a library like [**Partytown**](https://partytown.qwik.dev/?ref=differentshelf.com), which moves observers off the main thread and into web workers. The effect was immediate. Responsiveness almost doubled (and in some markets tripled) compared to baseline. That is a good outcome, but also a warning. It shows just how much of the main thread was being consumed by monitoring and other injected code in the first place. By moving it elsewhere we made the player experience smoother, but the data became less reliable, arriving late or incomplete. The result is a stark reminder: these devices do not have infinite resources. If the main experience and monitoring both fight for the same cycles, the player will always feel it. Other techniques include running code outside Angular’s zone with `ngZone.runOutsideAngular`, or adopting OnPush change detection to limit the surface area. These help but they are not cures. The deeper issue is structural. Tools that were never written with Angular’s runtime in mind end up clashing with it, and with each other. The lesson is not “avoid Angular” or “avoid monitoring.” It is to treat observers as part of the runtime contract. If they are not considered at that level they will clash with the framework, with each other, and with the player. ## Making the trade-off explicit rather than accidental The easy story is to say “cut them all.” The harder truth is that observers exist for a reason. They provide evidence that helps products evolve. They give marketing teams the ability to optimise campaigns and drive new players to the product. They allow operations to prove what happened. The danger is that when they are layered on without restraint, the value they deliver is outweighed by the friction they introduce. Insight that comes at the cost of broken flows is not really insight at all. Optimisation that makes the core journey slower is not really optimisation. The answer is not elimination but precision. Do we need to run session replay on every player, or could we learn the same lessons from one percent? Do we need overlapping tools capturing the same events, or could we reduce duplication and still get the picture we need? Targeting smaller samples preserves the value while protecting the majority of players from the cost. Product teams still get data, marketing still drives campaigns, operations still get visibility, and players keep the fast, responsive experience they came for. Sampling is product design when observation itself has a cost. ## Moving what we can to the server, and leaving the rest alone Not every observer needs to run in the player’s browser. Many of the things we collect, clickstream data, navigation flows, even A/B test assignments, can be handled server-side. That shifts the cost away from the main thread and onto infrastructure that is built to cope with it. This is not a blanket solution. Some tools, like real-time replay, require client access to the DOM. But wherever the same outcome can be achieved server-side, it should be. Counting page views, logging transactions, deciding which experiment a player belongs in, these are all things the server can manage without slowing down the client. It comes back to the same trade-off. Every line of code that runs on the player’s device competes with the experience they are trying to have. If the work can be shifted to the server or handled elsewhere, it should be. ## Conclusion Performance of consumer applications is not only about what we build. It is about what we allow in. Observers usually come with good intentions, but each one is still a guest in the player’s journey. And like any guest, it can either be considerate or disruptive. Our job is to protect that journey. Not by counting bytes, but by recognising impact. Not by saying yes to every insight, but by weighing its cost. In the end, what matters is not how much code there is, but how much weight it puts on the player. *Postscript: This essay showed how every watcher takes something from the player. A sequel explores the mechanics in detail, from frame budgets to long-lived listeners to why claims of “no impact” rarely hold.* [*Read Part 2 here*](https://www.differentshelf.com/watching-the-watchers-part-2-how-scripts-break-your-frame-budget/)*.* ### Keep the Heart Beating URL: https://www.differentshelf.com/keep-the-heart-beating/ Last updated: 2026-08-22T22:55:15.000Z *Running services without a supervisor is like fitting a pacemaker that only records when the heart misses a beat. The data would be accurate, but the patient would not survive long enough for it to matter. A real pacemaker intervenes first. It keeps the heart beating, and then leaves a record of what went wrong. Services deserve the same treatment.* **Failure is not optional** Services fail. That is a constant. What varies is how organisations respond. Some notice quickly, some only after colleagues complain, and some rely on someone in operations to restart things by hand. Engineers sometimes resist automatic restarts. The concern is that if a service comes back by itself, the signal of failure will be hidden. The instinct is understandable: it comes from a desire to know what went wrong. But leaving a service down so that failure is obvious is the wrong trade-off. It makes everyone who relies on the system pay the price so that engineers can notice. Continuity and observability are not in conflict. A process supervisor can restart a service automatically and at the same time record the fact that it failed, how often, and why. Colleagues are not blocked, and engineers still see the signals they need to act on. Recovery first, evidence beside it. A reliable system should provide both before anyone is paged. **Too many cooks, too much config** Init scripts were brittle and often hand-written. Supervisord came next, then systemd, each adding more capability and more complexity. Kubernetes goes further still with liveness and readiness probes. These systems have their place, but they also add overhead. Init scripts are fragile. Systemd can be sprawling and opaque. Kubernetes solves orchestration, not day-to-day process reliability in a VM estate. For organisations running large fleets of services on traditional infrastructure, the result can be fragility disguised as sophistication. **Tech debt in disguise** Stacks often come with their own supervision features. The JVM has wrappers. Python servers like Gunicorn can respawn workers. Node has forever and nodemon. Each works locally, but across an organisation they introduce inconsistency. Operations staff must learn a different model for each stack. Support needs different runbooks. Reliability becomes uneven, depending on which team bolted on which layer. Language-specific supervisors solve one team’s problem while creating debt for everyone else. They embed process management into application code instead of treating it as a shared capability. The result is operational drag: more surface area, less consistency, and reduced trust. **When the supervisor falls asleep** Even purpose-built supervisors stumble. A systemd unit may sit in a “failed” state without being restarted. A language wrapper may quietly give up. The effect is the same: the process is down, but the supervisor believes it is up. A supervisor should have fewer ambiguous states than the incident it is preventing. Daemontools has avoided this trap. Its design is small and strict, which keeps it aligned with reality. If a process dies, it is restarted. If it is marked down, it stays down. If it is running, it is running. In production use across many environments, it has proven consistently reliable. **Boring, on purpose** Daemontools is mature, stable, and simple. Each service lives in a directory with a run script. That script makes intent explicit. Should the process restart automatically? Should it come up on boot? Should it stay down until called for? These decisions are clear and visible. **It speaks every language** Daemontools does not care what the process is. Java, Clojure, Python, Go, or anything else: if it can be started with a command, it can be supervised. This neutrality allows a single approach across the stack. Starting and stopping services follows the same pattern across environments. In a heterogeneous estate, that consistency matters. It means SREs and support staff can intervene with confidence, without needing tribal knowledge or waking a developer at three in the morning. **Rollbacks out of the box** The model also makes deployments straightforward. Multiple versions can sit side by side: *myapp-v1.2.3*, *myapp-v1.2.4*. The one that runs is whichever the symlink in */service* points at. Promoting a release is repointing the symlink. Rolling back is the same. It is atomic, transparent, and reversible. There is no separate deploy system, no hidden metadata. The filesystem itself is the source of truth. Anyone can see which version is live, which versions are available, and how to switch. The filesystem is not glamorous, but it is inspectable at three in the morning. Observability is part of the design. Logs show whether a process is restarting repeatedly or sporadically, always in the same format. The behaviour is predictable. The same commands apply across services. **Respect the people, not just the processes** The outcome is human, not abstract. Administrators make fewer mistakes. Developers spend less time chasing failures. People who rely on the systems experience fewer outages and less disruption. And this is the point that matters most. Product engineering depends on these processes being up. When they are not, features stall, releases slip, and colleagues lose confidence. As an organisation we owe it to each other to remove that risk. A common process supervisor is not just a technical convenience. It is a way of showing respect for time, trust, and focus. Choosing not to use one is not neutral. It adds toil and risk. It undermines stability. Daemontools shows that a simple tool, applied consistently, can provide stability that is otherwise hard to reach. Reliable processes are not only good engineering; they are a responsibility we hold to the people around us. ### Timeouts Aren’t Safety Nets URL: https://www.differentshelf.com/times-up/ Last updated: 2026-08-22T22:55:15.000Z *When an application slows down, the reflex is often to reach for the timeout setting. Ten seconds, twenty, thirty. More rope. What you usually end up with is not more reliability, only more waiting.* Human-computer interaction research has circled this problem for decades. Robert Miller’s 1968 work suggested the thresholds: a tenth of a second feels instant, about a second is just about tolerable, and after that attention starts to fracture. Jakob Nielsen later popularised the same 0.1 / 1 / 10 second rule. Put simply, a two-second pause already feels sluggish. By five seconds most players assume something is broken. At ten seconds, they are gone. The meaningful threshold is not when the system gives up, but when the player does. So if your timeout is ten seconds, the player is lost before it even fires. Even two seconds is already testing their patience. A practical ceiling is nearer to two-thirds of a second. If you keep hitting that limit, the system is telling you to optimise or scale. Increasing the timeout only mutes the warning. This is not just an engineering concern. Every extra second a player waits is a second they could be finishing a sign-up, completing a purchase, or simply choosing to do something else. Longer timeouts mean fewer completions, more abandonments, and weaker engagement. Speed is not vanity. It is trust, and it is revenue. The harder design choice is not how long to wait but how to fail. Do you show cached results, offer a retry, or fall back to something simpler? A graceful “not right now” keeps a player’s trust in the product. An endless spinner does the opposite. Timeouts should be tight, deliberate, and rarely adjusted. They are best treated as canaries in the coal mine. If they stop singing, the answer is not to build a bigger cage. ### Why Native Still Matters URL: https://www.differentshelf.com/why-native-still-matters/ Last updated: 2026-08-24T16:18:05.000Z *In the companion piece,* [*Beyond the WebView*](https://www.differentshelf.com/beyond-the-webview-why-angular-apps-belong-in-a-pwa-twa-world/)*, we looked at why teams should embrace PWAs and TWAs on Android: faster, smoother, and easier to maintain, while keeping the door open to native SDKs like AppsFlyer and GeoComply. The message was clear: the web has caught up, and for many experiences it is now the best vehicle.* *But the journey does not end at the cabin door. The web will get you to the destination. Native boards you in first class. Both take off, both land, both arrive in the same city. But in first class the journey has another layer: the touches that follow you beyond the seat itself. That is where native now shines. Not in duplicating what the web already does but in the spaces only it can reach. The lock screen. The widgets. The ecosystem SDKs. The moments woven into daily life.* Native matters most where the product leaves its own screen. ## From Parity to Differentiation For a long time, the case for building native apps rested on performance. If you needed speed, smoothness, or access to hardware, you built native. That argument no longer holds. Modern browsers, WebAssembly, and progressive web app standards have brought the web so close to near-native performance that for most use cases players will not notice the difference. The web already delivers offline support, push notifications, payments, background sync, and installability. It is not the stripped-back environment it once was. Which means native developers are no longer winning by duplicating those same features. So where does native shine? Parity work spends native expertise recreating what the web already supplies. ## Living Beyond the App Native matters because it does not just run when a player opens it. It lives in the operating system itself. That is where the web still cannot follow. Widgets keep your product on the home screen, where players glance dozens of times a day. A widget showing a live bet slip, a favourite team’s next match, or a quick balance check creates value without the player even opening the app. Live Activities on iOS and Ongoing Notifications on Android put real-time updates on the lock screen. Scores, countdowns, or transaction statuses appear at the exact moment they matter. Lock screen integrations make your app part of the daily rhythm. Media controls, fitness stats, or alerts sit in the most visible space on the device. App shortcuts let players jump straight to what they care about most, reducing friction and making everyday tasks effortless. These are not “nice extras.” They are the features that make an app feel embedded in a player’s life, rather than just another icon they occasionally tap. The strongest native feature may be one the player uses without opening the app. ## Beyond Features: Ecosystem Levers Native development also brings advantages that extend beyond product features. App Store and Play Store discovery still shape acquisition, with promotion, featuring, and recommendations built into the platforms. Ecosystem SDKs such as AppsFlyer or GeoComply are often still native-first, giving you better attribution, fraud prevention, and compliance options than the web currently allows. These may not be technical differentiators, but they are practical ones. They influence growth and trust in meaningful ways. ## Focus Where You Are Strong For native developers, the future is not about chasing parity with the web. That ground has been lost, and rightly so, because parity is wasted effort. What you bring to the table is different, and arguably more important. You can meet players where they already are, in the flow of their device. Every time someone checks their lock screen, glances at their home screen, or swipes down a notification, there is a chance to deliver value instantly, without asking for effort. That is the space the web still cannot touch. It is exactly where native developers should double down. ## Conclusion The rise of PWAs and TWAs changes the balance of power, but it does not end the story of native development. It clarifies it. The web is now the right tool for cross-platform logic, speed, and parity. Native is the right tool for presence, ecosystem leverage, and moments of value that happen outside the app itself. Together they form a complementary whole. Teams can stop wasting cycles on duplication, while native developers can focus on what only they can do: making the product feel alive in the operating system, woven into the daily habits of players. That is not a loss of importance. It is a sharpening of purpose, and a new reason why native still matters. ### What is a Monorepo, and Why Does it Matter? URL: https://www.differentshelf.com/what-is-a-monorepo-and-why-does-it-matter/ Last updated: 2026-08-22T22:55:15.000Z *A monorepo is less a vault for code and more a city where it all lives. In the old world, projects sat in scattered villages, each with its own fences and dialects. Bring them together and suddenly you have streets that connect, buildings that share foundations, and neighbours who can see what each other are building. Standards don’t arrive by decree. They appear the way traffic lights do, because the city needs them to flow.* A monorepo changes the unit of visibility before it changes the unit of delivery. *Most people hear “monorepo” and think only of code. In reality, a monorepo is as much about people as it is about software. It changes how we see each other, how we collaborate, and how we imagine the future of building things together. “Monolithic repository” is a misleading label. It doesn’t mean a giant, immovable block of code. It means one place where all of a company’s projects live side by side. And in that place, people begin to meet.* ## Breaking Down Walls When you move to a monorepo, the first thing you notice is how silos start to crumble. Engineers who might never have seen each other’s code suddenly inhabit the same space. Standards begin to emerge, not because of mandates, but because visibility creates accountability. Visibility is useful because it makes local choices legible to the whole organisation. A bug fixed in one corner can ripple across the company. A better way of writing tests, or enforcing security checks, spreads almost instantly. It becomes less about *my repo* and more about *our codebase*. The repository itself becomes a town square: a shared place of contribution, conversation, and responsibility. ## Standards as Shared Language Consistency is rarely glamorous, but it is powerful. A monorepo makes it easier to apply the same rules everywhere: style guides, dependency policies, security scans, even the way we name our branches. These things may seem small in isolation, but together they form a language — a way of working that feels coherent across teams. And when standards live in one place, they evolve faster. You change a rule once, and the effect is felt everywhere. Instead of wrestling with drift, you get momentum. ## Preparing for the Future There’s another reason monorepos matter, and it has less to do with humans and more to do with the systems we are beginning to work alongside. AI thrives on context. The more of the codebase it can see, the better it can suggest changes, detect patterns, and make improvements that stretch across the product, not just within a single service. Imagine migrating an API, or introducing a new accessibility guideline. In a world of separate repositories, this becomes a patchwork project: chasing down each codebase, negotiating local quirks, and hoping nothing slips through the cracks. In a monorepo, it’s one change, one sweep, one coherent transformation — with AI agents soon able to automate much of the work. Holistic visibility enables holistic improvement. ## Myths and Misunderstandings Whenever the idea of a monorepo is raised, familiar worries surface. Some hear the word and immediately picture a giant monolith — one fragile block of code where everything is tied together. But a monorepo is about *source*, not *runtime*. The code may live together, but services, apps, and products still deploy independently, on their own schedules, with their own pipelines. Others fear that builds will grind to a halt. Yet build speed is not dictated by folder structure in Git; it is shaped by the tools we choose and the way our CI/CD is orchestrated. With caching, incremental builds, and path-based triggers, feedback loops can remain just as fast — and in many cases, faster. Release cadence often comes up too. Web teams want to ship daily, while backend or platform teams might move more deliberately. A monorepo doesn’t flatten those differences; it simply makes them visible. A change to one folder needn’t ripple into another. Teams can, and should, continue to release at the pace that suits their product. Permissions are another common worry: the fear that a monorepo is a free-for-all, where anyone can change anything. In practice, the opposite is true. With code owners and protected branches, review boundaries become sharper, not blurrier. Sports approves Sports, Payments approves Payments. Visibility increases, but authority remains local. Global visibility and local authority are complements, not opposites. And then there’s the size of it all. Won’t the repository balloon until it becomes unmanageable? In truth, modern Git offers mirrors, partial clones, and shallow checkouts to keep day-to-day developer experience light. Heavy assets belong in registries, not wedged into source control. Good hygiene goes further than fragmentation ever could. What these myths share is the belief that a monorepo changes *how* you build and release. It doesn’t. It changes *where* the code lives. And in doing so, it creates the possibility of working as one organisation, not a collection of silos. ## Proven at Scale This isn’t a fringe idea. Some of the most accomplished engineering organisations in the world — Google, Meta, Microsoft, Uber — run on monorepos. They’ve shown that it’s possible not only to manage complexity at this scale, but to turn it into an advantage. If they can orchestrate billions of lines of code this way, we can draw confidence that the model is robust, not an experiment. ## One Codebase, One Company A monorepo is not just a strategy for source control. It’s a philosophy of collaboration. One codebase, one company. Shared responsibility for shared assets. A foundation that lets us move faster, not by running alone, but by improving together. When we work in a monorepo, we stop optimising for the smallest unit — the individual repository — and start building for the whole. And in that shift, something powerful happens: we begin to see ourselves not as separate teams, but as one organisation, with one product, moving in one direction. ### A Level Start URL: https://www.differentshelf.com/a-level-start/ Last updated: 2026-08-24T16:18:04.000Z *When you turn on a football match, the picture does not build itself one blade of grass at a time. The signal arrives whole. A television still has work to do, but the game is there from the first moment.* The server does not make devices equal. It makes their first responsibility smaller. That is the difference between leaving every device to construct the picture and giving everyone a clear view straight away. The experience begins together, whether the viewer is on a high-end set or something more modest. In player-facing apps, that idea is called **server side rendering**, often shortened to **SSR**. Instead of asking every phone or laptop to build the first view from scratch, the server prepares it in advance. The device still has work to do, but the opening is faster and consistent. ## Complexity at scale Single-page applications, or SPAs, deliver everything through one continuous web page rather than loading new pages as you navigate. This is the standard model for serious digital products. Facebook, Gmail, Netflix, Spotify, Airbnb: all follow this pattern. It allows app-like experiences in the browser, but it also shifts more responsibility onto the device. SPAs rarely stay small. Over time they absorb more responsibilities. What begins as a simple interface evolves into a platform that supports multiple features, audiences, and flows. On powerful devices, that weight can be absorbed without much thought. On mid-range hardware that stays in circulation for years, the same application can feel heavy. The unevenness is not just about polish. When speed varies by device, the product as a whole feels inconsistent. This is not failure. It is the natural path that every ambitious product takes as it grows. The challenge is not to avoid it, but to manage it. As with monorepos, the point is not technical purity but consistency at scale. ## Removing variability Devices will never be equal. Network conditions will never be uniform. The starting point can be. SSR provides a way to create a consistent beginning. The server delivers the first frame already assembled. Every device starts from the same place. A common first frame removes one source of inequality without pretending the devices are the same. The benefit is not novelty. It is the removal of variability. This is how the biggest players make highly dynamic applications feel usable across the widest possible audience. ## Hydration is the constant SSR does not eliminate the real work. The server can hand over a finished picture, but the application still needs to come alive on the device. Every button must wire up, every transition attach its logic, every rule connect to the right state. That process is known as hydration. Hydration is unavoidable. A highly dynamic application, with state that changes every second, cannot behave like a near-static ecommerce site that can be pre-rendered in full. The complexity is intrinsic. Even with careful dependency management, the weight remains. SSR does not erase it. What it buys is time. People see something recognisable and ready while the device works in the background. The cost is paid, just not in public. Hydration pays the same bill after moving it out of the opening scene. ## Beyond the stopwatch Core Web Vitals give us a shared framework for performance. Loading, interactivity, stability and now responsiveness all matter. SSR improves some of these immediately. **Largest Contentful Paint (LCP)** gets faster because the first view arrives assembled rather than pieced together on the device. **Cumulative Layout Shift (CLS)** often improves for the same reason: the page is stable from the start. Those benefits are real, but they are also bounded. They matter most in the opening moment. First impressions improve, yet the time people actually spend is inside the app. Once hydrated, the application behaves like any other SPA, with the same challenges of responsiveness and smooth interaction. SSR should be seen as a foundation, not a finish line. Metrics are signals, not absolutes. People do not experience a score. They experience whether a button responds, whether a transition feels smooth, and whether the system keeps its promises. For developers, the value of SSR or Trusted Web Activity is not only in a stronger opening but in reducing the firefighting that comes with slow starts and inconsistent delivery. That time can be spent improving the parts of the product where people actually live. ## Ingredients for premium All of these approaches are ingredients. SSR sets a consistent starting point. [Trusted Web Activity](https://www.differentshelf.com/beyond-the-webview-why-angular-apps-belong-in-a-pwa-twa-world/) reduces the friction of delivery, bringing web apps closer to native behaviour on Android. Rich transitions and gestures create a sense of continuity and control. Haptics provide reassurance that actions have been taken. Continuous optimisation keeps the experience improving as devices and expectations change. None of these in isolation makes a product premium. Together, they are enough. And this is not an unusual path. It is the same mix of engineering and design choices used by the most formidable digital companies in the world. There is always the view that the answer is to go fully native. Sometimes that is right. But more often the real question is not whether the code is native or web, it is whether the experience is consistent, fast and reliable. SSR and TWA are not compromises. They are ways of making web applications feel premium without discarding years of investment. And both native and web have their fragilities, which I explored in [The Fragility of Both](https://www.differentshelf.com/the-fragility-of-both/). The role of leadership is not to argue stack purity but to choose the levers that keep experiences consistent. The business impact is direct. A consistent start builds trust. Smooth interactions increase engagement. Responsiveness drives retention and conversion. Predictability gives leadership confidence that scale does not erode experience. These are not abstract engineering wins. They are choices about the kind of experience that will keep people coming back. The same theme runs through this series: the tools matter, but what defines the outcome is how they are used. SSR and TWA belong in that category. The match should begin the same for everyone. The difference comes from how it is played, not whether the screen ever finished loading. ### Signs Worth Reading URL: https://www.differentshelf.com/signs-worth-reading/ Last updated: 2026-08-24T16:18:04.000Z *We’ve all seen them: tickets in the backlog that just say “New onboarding flow.” Commits in source control that read “another fix.” Merge requests with the description box left empty. The scaffolding is there, but it doesn’t tell anyone what’s being built. It’s like driving past a road sign that only says “road.”* Whether the backlog lives in Jira, Linear or Notion, or the documentation sits in Confluence, and whether source control is GitHub, GitLab or Bitbucket, the pattern is the same. These artefacts aren’t bureaucracy. They are the map and the markers that make product engineering visible. They connect the work of one squad to the intent of another. They tell the story of how a product came to be, not just the code that made it work. A system of record without intent is only a filing cabinet. And now, with AI in the picture, there is little reason for the map to be blank. An epic like *“New onboarding flow”* can be enriched with goals, edge cases and constraints without someone spending half a day writing them down. A commit that once said *“another fix”* can then be summarised in context. The link between backlog and source control creates the map. A map only works if the signs are clear enough to follow. ## **Maps without routes** Empty tickets and vague commits aren’t just a nuisance. They blur the outcome. Was the work meant to shorten the time from registration to first deposit? To reduce drop-off in certain states? To test a lighter design for mobile? Without that context, few will know. And product managers rarely read the code. They live in the backlog. If the spec is vague, then even when the feature passes testing, it is hard to be sure it matches the intent. The signs exist, but they don’t point to the right place. The same applies when conversations spill elsewhere. As I wrote in [*Shouting in the Digital Office*](https://www.differentshelf.com/shouting-in-the-digital-office/), decisions shouted across a desk, dropped into a chat thread or hidden in a slide deck are lost to the mist of time. They leave no trace in the system of record. The real story begins in the backlog, and the output is in source control. When it lives elsewhere, the work may still get done, but the why is no longer obvious. The map is incomplete. And there are reasons this happens. People move fast. Meetings run long. Context switches pile up. Writing things down feels like a delay, so teams default to the quickest channel. In the moment it works, but later the story is broken. That isn’t saving time. It is losing not only the why, but the how. ## **Sat-nav for squads** When backlog and source control are stitched together properly, they behave like GPS. The *“New onboarding flow”* epic doesn’t just sit on a roadmap slide. It links directly to the commits that shaped it. Each commit carries its own roadside marker: a bridge rebuilt, a turn taken. Merge requests act like indicators, telling the convoy around you when a lane change is coming. AI makes this far easier. Summaries can be drawn from diffs. Acceptance criteria can be inferred from logs, screenshots and reports. Even the vaguest *“onboarding flow”* commit can be expanded into a clearer description of what changed and why. The route map can update itself. ## **When the lanes converge** Clearer artefacts don’t just help engineers understand each other. They change the relationship between product and engineering. Product managers who capture outcomes, constraints and success criteria with enough precision can go further than they might expect. With AI, those words can be translated into test scaffolds or early proofs of concept. A well-written epic can seed working code, allowing an idea to be explored before engineering commits to building the full road. In that sense, product begins to edge into engineering. And the reverse holds too. Engineers who learn to express intent and player outcomes in writing start to edge into product. If you can frame a change in terms of a player journey, a conversion goal or a hypothesis to be tested, AI can carry it forward into the kind of artefact a product manager would create. Written clarity becomes a bridge. Written clarity is an interface between product and engineering. This isn’t role erosion. It is the start of a deeper meld. Product and engineering remain distinct, but the flow between them becomes easier, faster and more experimental. ## **Arriving together** The story of a product should not be scattered across chats, slides, meetings or incidents. It should run straight from backlog into source control, with the signs along the way clear enough for anyone to follow. The common thread through all of this is the written word. Expressing intent, outcomes and decisions in language that carries. Writing them down, in the system of record, is what turns an idea into a product. And it is understandable why words leak into other places. Nobody skips documentation out of malice. They do it because they are under pressure, because the sprint is already moving, because it feels quicker to move on. But that short-term gain comes at the expense of long-term clarity. Tools will evolve, and AI will continue to reduce the toil, but the enabler is still the same. Clear words, kept in the right place, are what turn work into product engineering. The rest is just clutter on the roadside. AI can fill in prose. It cannot decide what the work was for. ### The Trunk is the Team URL: https://www.differentshelf.com/the-trunk-is-the-team/ Last updated: 2026-08-24T17:32:59.000Z *An orchestra can rehearse in separate rooms. Each section sounds fine to itself. Strings keep time, brass hit their notes. But the first time they come together the rhythms do not match. What was music alone becomes noise together. The only way to avoid it is to rehearse in the same hall, again and again.* Every day integration is delayed, more uncertainty accumulates. That is how many large repositories behave. Project teams work on their own develop branches, merging later into a release branch where all products finally meet. The release is then merged back into main. In theory it works. In practice the first real rehearsal is late in the cycle and that is when problems appear. Trunk-based development removes the intermediate steps. The release is cut from main. Everyone plays in the same hall. Integration does not wait until the end. It happens daily, in the open, where adjustments are easier and the rhythm is shared. ## Daylight and discipline Code in trunk is visible immediately, not hidden for weeks. Junior engineers do not wait to discover their approach will not hold. They learn in days, while the lessons are still manageable. Product teams see progress as it unfolds rather than as a reveal. And players feel value sooner, as a steady rhythm of improvement. A [monorepo](https://www.differentshelf.com/what-is-a-monorepo-and-why-does-it-matter/) helps with visibility and consistency, but it is not enough on its own. You can still have long-lived branches and late merges inside a single repository. Trunk-based development is the practice that makes the monorepo effective, turning shared code into shared progress. Think of it as phase two: once code is together, the challenge is keeping people together. A monorepo creates visibility. Trunk turns visibility into a daily operating habit. Merging main into a feature branch helps, but only partly. It shows whether your code works against the past. It cannot show how it behaves against the hundred other branches in flight, each carrying its own version of the future. The trunk collapses those futures into the present. A feature branch is one possible future. Main is the future everyone must survive. This only works when the trunk is treated as sacred. Break it and you fix it immediately. That expectation changes behaviour. Developers test more carefully before pushing. Teams lean on flags instead of leaving half-finished work exposed. It takes a cultural shift, and it is not perfect at first. Over time people adapt to the rhythm. The approach comes with costs. Smaller and more frequent commits take practice. Feature flags accumulate debt and need discipline to clear. Tests must be fast and trusted or progress slows. These drawbacks are visible and manageable. The alternative is drift hidden in dozens of branches, which is harder to correct and more costly in the long run. With five hundred engineers, visibility compounds. Not because everyone becomes a tester but because seeing each other’s work builds judgment. Junior developers learn faster. Seniors see what their choices unlock or constrain. Thousands of small decisions each week are made in the open, and that openness is what scales the wisdom of the crowd. ## Changing the key In an orchestra, changing key is not just one player shifting a note. The whole section must move together. Strings adjust as one, then brass, then percussion. The score is the same, but the pitch is new. Large refactors in software feel the same. A design system refresh, an Angular upgrade, a shift in a shared pattern. Hundreds of files move. Every product is affected. It is not enough for one team to play in tune. The section must change together, and the performance cannot stop. Trunk makes this possible. The new key is introduced in steps. Nx generators apply consistent changes across the repo, keeping signatures and imports aligned. Incremental builds reduce the cost of touching many files at once. One product moves first, while others continue as before. When that product is stable, the path is clear for the rest. Not every change can hide behind a feature flag. Some differences will surface in the open. That is acceptable. The point is not to insulate the organisation from change but to make it visible early, when it is still possible to adjust. The performance continues. Developers hear each other adapt and adjust their own parts. By the time the last product moves, the new key already feels natural. The piece carries on, coherent and whole. ## Why it matters Trunk-based development is not only an engineering concern. It shapes how quickly products reach the market, how reliably they perform across brands, and how visible progress is while they are being built. For leadership, the benefit is clarity and faster learning. Work flows into the hands of players sooner, with fewer surprises at launch. Product direction can be adjusted earlier because progress is visible every day rather than hidden in long branches. Teams learn more quickly what works and what does not, which reduces the cost of mistakes and shortens the path to value. For players, the benefit is continuity. Value arrives in smaller, steadier increments. The product does not lurch forward and stall, it evolves smoothly. Reliability matters as much as speed, and trunk supports both. Like an orchestra, trunk-based development is cultural. It takes practice. The first rehearsals are awkward. Mistakes are public. But people adjust to each other’s timing. They listen as well as play. In time the noise settles into rhythm, the rhythm becomes music, and the organisation can move as one. ### The Fragility of Both URL: https://www.differentshelf.com/the-fragility-of-both/ Last updated: 2026-08-24T16:18:03.000Z *Imagine plumbing built over years with pipes of different sizes. Each section works on its own, but once joined together the flow is unpredictable. Sometimes it runs smoothly, other times it slows to a trickle, and nobody is quite sure why. From the outside it looks like one system. Inside it is a set of mismatched parts, connected but never fully aligned.* That is how many mobile apps are built today. A web application lives inside a native wrapper, linked to Android, iOS, a bridge for each, the web layer, and finally the back-end platform. Each layer is solid in isolation, but the joins are fragile. A flow only works when everything lines up, and when it does not, the weak points tend to show through to players. In a fully native app, drawing on OS features builds on a consistent foundation. The platform and tooling align, and performance is often easier to achieve, though not always a given. That coherence is the strength of native. But when similar features are layered through multiple stacks, the story can become more complicated, as the examples that follow show. ## When compromise bites in practice Authentication is one of the clearest examples, though not the only one. WebAuthn already provides passkeys, biometrics, and second factors consistently across browsers. It delivers the kind of experience players expect: secure, seamless, device-native sign-in without extra steps. Yet some implementations still bridge biometrics through wrappers. That leaves authentication spread across Android and iOS, their bridges, the web layer, and the back end. No single tier is fully responsible, so when something behaves unexpectedly, it can take longer to pin down the cause than it should. Every bridge creates a place where ownership can evaporate. Players feel this as unevenness rather than outright failure. Biometrics sometimes succeed, sometimes do not. Sessions may disappear after a background event. These are not dramatic outages, but small inconsistencies that wear away trust over time. For engineers, what might take days to diagnose can take far longer, not because the problem is deeply complex, but because it depends on every layer of the stack aligning at the same time. Geolocation shows a similar pattern. The web already provides APIs that are accurate and consistent enough for most needs. But once location is split between Android and iOS implementations, translated by two bridges, passed into the web layer, and reconciled with back-end logic, the experience depends on all of those parts staying in step. Even minor drift makes it harder to understand and harder to fix. Neither case breaks the product outright. Both highlight the same truth: complexity grows fastest where responsibilities overlap, and the experience suffers quietly at the edges. ## Where native still earns its keep That does not mean native has no role. Far from it. There are times when native gives a product a sense of belonging on the device itself. Think of a balance or score pinned to the home screen, a match that stays live on the lock screen, or a shortcut that takes you straight into the heart of the app. These are the moments players can actually see and feel. They create trust because they look like part of the operating system, not just something running inside it. Crucially, all of this remains possible even when the core experience is delivered from a wrapper. These features can talk to APIs directly, without being tangled in the bridge, the web layer, or the platform underneath. That separation makes them less fragile, while still giving players the confidence that the app is integrated into their device. Native earns its keep where the player can see the operating system doing something useful. This is the same argument I made in [*Why Native Still Matters*](https://www.differentshelf.com/why-native-still-matters/). Native earns its place when it is visible to players, embedded in the OS, and part of their daily rhythm. A coherent web core, complemented by these deliberate native touches, is a path that can feel premium without multiplying complexity. ## Chasing improvements players seldom see It is tempting to believe that native detours create visible improvements. That gestures will be smoother, transitions faster, or sign-in more premium. The reality is usually more modest. Polish is harder for players to see than it is for teams to build. They rarely notice whether a swipe is processed natively or in the DOM. Modern browsers deliver animations that are already fluid. What they do notice is reliability. They notice when a login behaves inconsistently, or when location permissions feel unpredictable. So the polish may land occasionally, but the trade-offs are easier to feel. What stands out are delays, inconsistencies, and extra work for teams. ## A native detour, not a native progression Another assumption is that adding more native logic inside wrappers moves a product closer to being fully native. It is unlikely. If a team ever chose to build native end-to-end, the code written inside wrappers would be unlikely to carry across. It is not a foundation to build on. It is a separate path designed for a different architecture. A detour can be well engineered and still lead nowhere. In practice this means that when native is used where a strong web standard already exists, the maintenance burden grows today without lowering the cost of change tomorrow. The work may be well built, but it does not usually accumulate toward a broader native strategy. The real choice is between coherence and fragmentation. A web-first approach keeps the stack coherent. Native has its own coherence when used deliberately for OS-level touchpoints. Blending the two rarely brings convergence. It tends instead to create compromise. ## Why TWA changes the picture It is worth acknowledging why the pull toward native is so strong in the first place. On Android especially, WebView has long been a weak link. It is slower, less reliable, and often lags behind the performance players expect from a first-class app. Faced with those gaps, it is natural for teams to reach for native workarounds, and to believe this is the only way to deliver the quality players demand. The story is shifting. With Trusted Web Activities (TWA), it is now possible to run a Progressive Web App inside a lightweight native shell that uses Chrome rather than WebView. The difference is material. In side-by-side testing, apps delivered through TWA showed a 20 to 25 percent improvement at the 75th percentile compared to WebView. Support for modern standards is consistent, performance is stronger, and the wrapper remains minimal. The result is a web app that feels closer to native without the weight of a full native build. This matters because it reduces one of the biggest arguments for duplicating native logic. If the pressure to go native was largely driven by WebView’s shortcomings, then TWA softens much of that case. It allows teams to back web standards with more confidence, knowing they will perform well on Android and behave more consistently across devices. ## Building with intent The first step is clarity. If a product is a web app in a wrapper, it should be treated as such. That does not mean lowering the bar. Standards like WebAuthn, geolocation, or CSS transitions are capable of delivering excellent experiences when implemented with care. Clean flows, consistent state, and polished interactions are a product and engineering challenge, not a gap in technology. For organisations that have already invested heavily in web, starting over natively is always possible, but it is not the only route to premium. With intent, the existing investment can deliver experiences that are polished and consistent without discarding what has been built. For leaders, the distinction is not purity of stack but quality of outcome. Players value speed, reliability, and consistency more than the label on the technology that delivers it. A wrapper, built with care, can meet that expectation today while still leaving the door open to native if the conditions are right. ### Shouting in the Digital Office URL: https://www.differentshelf.com/shouting-in-the-digital-office/ Last updated: 2026-08-24T16:18:03.000Z *Picture an open-plan office where someone suddenly shouts loud enough for the whole floor to hear. Not because the building is on fire, but because they want a hand with something small. Thirty people lose their train of thought. Flow is broken. Work gets harder.* *That is what most chat platforms have become. Every ping is a tap on the shoulder. Every @channel or @everyone is a shout across the room. The intention is usually good. The outcome is not.* ## Notes and Shoulder Taps Async is leaving a note for someone to pick up when they are ready. Sync is asking them to stop now. The difference is not small. It is the difference between protecting deep work and breaking it. Meetings are the ultimate sync. Everyone has to down tools, whether or not they are needed in the room. Many of those meetings exist because someone did not want to write an email. If they had, the point would have been clearer and fewer people would have had to stop. Chat feels lighter but is often just sync in disguise. Notifications and the pressure to be “seen” as responsive mean people drop what they are doing. Group threads are mini meetings without the discipline of an agenda. Email is the purest form of async. It forces the sender to think, structure their thoughts, and commit them to writing. That discipline matters. If you cannot explain something in writing, it often means you do not understand it well enough yet. And because email carries no red dot on the taskbar, it lets the recipient reply when they have space, not when they are interrupted. Sometimes, though, the right answer is neither chat nor email but a phone call. Ten minutes of direct conversation can save a week of emails bouncing back and forth. The point is not to banish sync. It is to use it deliberately, knowing that every interruption is stealing minutes from the deep work developers crave. Urgency is a property of the work, not the sender's impatience. A simple spectrum emerges. If it demands immediate action, chat or a quick call is right. If a decision truly needs collective discussion, call a meeting. If it can wait, write it down. ## Pulling the Fire Alarm Nothing breaks flow faster than an @everyone. It is the digital equivalent of pulling the fire alarm. One shout that forces the whole office to stop, whether they needed to hear it or not. There are moments when it is right to use it: a live incident, or a blocker holding up an entire squad. Outside those cases, it is costly. The numbers make the point. Thirty engineers. One minute to read the message. Three minutes to get back into flow. Four minutes each, two hours lost in total. And that is the best case. An interruption is cheap only when you count the sender's time. Now scale it. In a pillar of around 250 engineers, if each one is pulled into just two irrelevant @everyones a day, the cost is more than 30 engineer-hours lost daily. Over a week that is close to an engineer-month. Over a year it is dozens of engineer-years gone to noise. Across a wider organisation of 2,000 engineers, the numbers multiply again. Even if each one loses only 15 minutes a day to irrelevant @everyones, that is 500 hours gone daily. The equivalent of more than ten full squads doing nothing but recovering from interruption. *But not every distraction is as loud as an @everyone. Some are quieter, tucked into private messages. They feel harmless, even polite, but can drain deep work just as effectively.* ## Behind Closed Doors Private messages feel polite. They feel efficient. In reality they shrink the room. The same questions get asked again and again, usually to the same people, who quietly become bottlenecks. Those bottlenecks are often the very engineers whose deep work is already in shortest supply. Take a new joiner who asks in private, “How do I get this service running locally?” A senior stops, answers, and loses their flow. The next day another new joiner asks the same thing. The cost repeats. If the question had been asked in public, the answer would have been there for all to see. Public channels change the economics. Questions become shared knowledge. Answers become a record. Others can help. The load spreads. The organisation learns in the open. And crucially, the few people capable of answering the hardest questions get more time back for deep work. ## Death by a Thousand Pings Flow is fragile. It takes time to build and seconds to lose. Developers talk about “deep work” for a reason. At the company I work at, when we ask developers what gets in the way of their best work, lack of deep work always comes back as a top complaint. Too many meetings. Too many pings. Too many @everyones. Not a refusal to collaborate. A plea to be given the chance to do the work they were hired to do. That is why a quick message is rarely quick. The sender saves five minutes. The receiver loses thirty. Multiply that across a team and the costs pile up silently. Each interruption might feel small, but every one steals a slice of flow. The organisation pays, invisibly, every single day. ## The Empty Hello One of the simplest examples is the empty greeting. A message that says only “Hello” or “How are you?” with no follow-up. It forces an unnecessary loop: the other person replies “Hey, what’s up?” or “I’m fine, can I help you?” before the real question arrives. Nobody does this with bad intent. It comes from politeness, or habit, or a desire not to be abrupt. But in a working context it slows the exchange for no benefit. It delays the answer and breaks flow twice instead of once. The fix is simple. Do not ask if you can ask. Just ask. Start with “Hi” if you want, but include the question in the same message. Efficiency is not rudeness. It is respect for the other person’s time and their ability to stay in deep work. *The same applies when asking for technical help. Most requests come with good intent, but without enough detail they create more interruption than progress. A little extra care up front saves a lot of time later.* ## Bring the Clues with You Another costly habit is asking someone to debug with no information. A colleague calls in a “tech person” but provides nothing to go on: no error logs, no screenshots, no steps to reproduce. The expert is forced to drag the detail out one question at a time. Both people lose focus. Again, the intent is not bad. People are often in a hurry or unsure what matters. But arriving empty-handed puts the burden of discovery onto someone else. It breaks their flow and steals more time from the deep work everyone wants to protect. A better pattern is to bring as much evidence as you can. Show the steps you took, the message you saw, the change that triggered it. Even if you are not sure what is relevant, include it. It is faster to ignore unneeded detail than to guess at missing pieces. Good bug reports save time. They show respect for the expertise you are asking for. And they keep more of that scarce deep work time where it belongs. *None of this is about adding friction. It is about making intent clearer, so people can respond without guessing. Even small signals help protect deep work.* ## Say What You Mean One practical step is to mark intent directly. A simple tag at the start of a message changes how it lands. - \[Async\] Sharing early thoughts on the new API contract. Please take a look this week and comment when you have time. - \[Info\] Rollout complete for the new payments flow. No action needed. - \[Urgent\] Production issue affecting logins. Need help now. These small signals give others permission. Permission not to break their flow. Permission to wait until they are ready. Permission to focus. Every signal that protects deep work is a productivity win. ## Better Than Mindful Being thoughtful in communication is important. But even better is reducing the need to communicate in the first place. That sounds counter-intuitive, almost inhuman, but the reality is the more context people have, the less they need to ask. A [monorepo](https://www.differentshelf.com/what-is-a-monorepo-and-why-does-it-matter/) helps here. Engineers can see what others are doing, how they are doing it, and build up context without having to interrupt. Observability does the same. When systems expose their own state clearly, engineers can help themselves. Add consistent, predictable tooling across the organisation and the effect compounds. You no longer have pockets of custom scripts or hidden knowledge. You have one way of doing things, discoverable by anyone. The best communication system answers routine questions before anyone has to ask. Documentation, shared records, and clear processes sit alongside these. Together they mean fewer interruptions, because the answers are already visible. The goal is not silence. It is to make communication the exception rather than the constant, so when we do reach out it matters. Protecting deep work sometimes means fewer pings. Sometimes it means none at all. ## The Cost of Good Intent Nobody sends an @everyone, drops a “Hello”, or pings a senior engineer to debug without details in order to waste time. The intent is almost always good. But intent does not cancel out effect. At scale, habits compound. A few lost minutes here and there add up to days. Days add up to weeks. Ask developers what gets in the way of doing their best work and deep work is always near the top. Meetings, noise, pings, interruptions. None of these are trivial. They are the biggest productivity cost we face. Habits are choices. As leaders and as colleagues, we decide whether to protect deep work or to chip away at it. If we care about productivity, the way we communicate is one of the biggest levers we have. ### Why Players Should Get Apps Tailored for Their Device, Not One Size Fits All URL: https://www.differentshelf.com/moving-beyond-the-lowest-common-denominator-differential-builds-for-near-native-web-performance/ Last updated: 2026-08-24T16:18:02.000Z *One-size-fits-all clothing has its appeal. Everyone is covered. Nobody is left out. But it is never a perfect fit. On some it hangs loose, on others it pulls tight. For years, our JavaScript bundle has been much the same: serviceable, universal, but far from tailored.* ## Serving Everyone, Penalising Many Our guiding principle was simple: serve everyone. We aimed to make sure that players on old browsers, outdated WebViews, and underpowered devices could still load our apps without error. To achieve that, we delivered a single JavaScript bundle cut to the ES2015 standard. For those not steeped in the naming, ES2015, ES2020, and ES2022 are editions of the language. Each new cut trims away old stitching, unlocking cleaner syntax and lighter code. ES2015 is the baseline: sturdy, compatible, but heavy with extra fabric. It worked. Nobody was excluded. But the cost was hidden in plain sight. Modern players were forced to squeeze into hand-me-down code stitched with polyfills and padded out with legacy helpers. Compatibility became a tax paid most heavily by the devices least in need of it. On the very devices most capable of speed, our apps felt slower than they should. Parsing dragged. Time to interactive stretched. And as expectations rose for fast, fluid, near-native experiences, the compromise grew harder to justify. ## WebViews and Their More Capable Cousin The gap is most visible on Android. Standard WebViews are usually kept current through Play Services, so they are not fossils. They render modern pages and support the essentials. But they often lack the full breadth of optimisations, the latest JavaScript engine tweaks, or the deep integration that Chrome enjoys. Trusted Web Activities, by contrast, are the more capable cousin: running inside the full Chrome browser, with every optimisation, JIT enhancement, and API at their disposal. In a TWA, modern code can stretch its legs, run close to native speed, and show what the web is really capable of. I wrote about this shift in [*Beyond the WebView: Why Apps Belong in a PWA + TWA World*](https://www.differentshelf.com/beyond-the-webview-why-angular-apps-belong-in-a-pwa-twa-world/), where TWAs are not just a technical option but a statement of intent. They mark a move away from compromised, containerised experiences towards the web itself as the primary runtime. And yet, until now, we have hobbled that potential by serving even TWAs the same legacy bundle as everything else. It is like buying a racing bike and fitting it with stabilisers. ## A More Tailored Fit The answer is differential loading. Instead of forcing everyone into one shape, we cut three fits. Think of them as patterns cut from the same cloth at different levels of detail. One codebase need not force one payload on every device. - **Modern (ES2022)** is the most recent cut. Slim, lightweight, and efficient, it uses the latest features of the language to strip away extra fabric. - **Intermediate (ES2020)** is a step back. Still neat, still modern, but missing some of the finer tailoring. - **Fallback (ES2015)** is the safe, sturdy option. A little heavier, stitched for compatibility above all else. At runtime, each device gets the bundle it deserves. Modern engines are no longer shackled by yesterday’s syntax. Legacy devices are still served, but without dictating the experience for everyone else. ## Near-Native Speed The difference is immediate. In TWAs, ES2022 bundles compile with full JIT, benefit from hidden classes and inline caching, and run with almost native responsiveness. Animations are smooth, interactions snappy, and heavy frameworks like Angular no longer feel like a burden. The same app that once felt bloated suddenly feels alive. For players, it means time to interactive drops, the UI feels fluid, and the distance between web and native narrows to a hairline. ## Beyond the Lowest Common Denominator Compatibility once meant sameness. Today, it means respect: giving each device the best it can handle. Differential loading is not just an optimisation trick, it is a shift in philosophy. We stop penalising the many for the sake of the few. We start trusting modern environments to show their strength, while still carrying those who need more help. The web does not have to be one size fits all. It can be tailored, measured, and cut to fit, ensuring players feel at home whether they arrive on the oldest phone or the more capable cousin of a WebView. **Further Reading** If you want to dive deeper into why TWAs matter, see [*Beyond the WebView: Why Apps Belong in a PWA + TWA World*.](https://www.differentshelf.com/beyond-the-webview-why-angular-apps-belong-in-a-pwa-twa-world/) ### Streamlining Collaboration: How Mirroring Frontend and Backend Teams Can Solve Coordination Challenges URL: https://www.differentshelf.com/streamlining-collaboration-how-mirroring-frontend-and-backend-teams-can-solve-coordination-challenges/ Last updated: 2026-08-24T16:18:01.000Z *Pairing frontend and back-end teams can make cross-team work more predictable, but it does not remove the dependency. One product change can still cross several teams, and every hand-off adds delay, misalignment and frustration. The question is how to reduce that coordination without pretending every customer or service belongs in one cross-functional team.* A dependency is not removed when two teams are paired. It is made more predictable. One solution we’re exploring is **mirroring frontend and backend teams**. This approach aligns backend teams closely with frontend teams, creating a one-to-one or one-to-few relationship between them. It’s not a full merge, but it’s a step toward reducing coordination overhead while maintaining the flexibility to serve multiple customers. Here’s how it works, why it might be the right fit for us, and how we can implement it effectively. ## **What Does Mirroring Look Like?** Mirroring involves structuring backend teams to align closely with frontend teams. Here’s what it looks like in practice: - Each frontend team has a dedicated backend team (or a small number of backend teams) that it primarily works with. - The backend team(s) focus on delivering the APIs, services, and data that the frontend team needs to build its features. - The backend team(s) may still own specific services or domains, but their primary responsibility is to support the frontend team(s) they are aligned with. For example, if we have a frontend team working on the checkout flow for Customer A, we’d have a dedicated backend team that owns the checkout service and payment APIs for that customer. Meanwhile, another backend team might support the user profile features for Customer B. ## **Why Mirroring Works for Us** 1. **Reduced Coordination Overhead**: - Frontend teams only need to coordinate with their dedicated backend team(s), rather than multiple backend teams. - This simplifies communication and decision-making, especially in a multi-customer environment. 2. **Stronger Alignment**: - Backend teams are incentivized to prioritize the needs of their aligned frontend team(s). - This creates a shared sense of ownership and accountability for delivering features. 3. **Faster Iteration**: - Frontend teams can move faster because they have a dedicated backend team focused on their needs. - This reduces delays caused by competing priorities across multiple backend teams. 4. **Clearer Boundaries**: - Each backend team has a well-defined scope of responsibility, reducing overlap and duplication of effort. 5. **Easier to Implement**: - Mirroring is less disruptive than merging teams or reorganizing around domains, as it preserves existing team structures to a large extent. 6. **Platform Modernization**: - By dedicating backend teams to frontend needs, the **platform team can focus on modernizing the platform** without being pulled into frontend-specific priorities. - This reduces conflicts between frontend requirements and platform-wide improvements, allowing the platform team to innovate and evolve the underlying infrastructure. 7. **Empowered Product Managers**: - **Product managers can work end-to-end** within their aligned frontend and backend teams, ensuring a seamless flow from customer requirements to delivered features. - At the same time, **platform product managers can continue solving for the Platform-as-a-Service (PaaS) layer**, ensuring the platform remains robust, scalable, and future-proof. ## **Challenges to Watch Out For** While mirroring offers many benefits, it’s not without its challenges: 1. **Risk of Silos**: Backend teams may become too focused on their aligned frontend team(s), potentially losing sight of the broader system. 2. **Backend Team Overload**: Supporting multiple frontend teams can create bottlenecks if priorities aren’t balanced effectively. 3. **Technical Debt**: Short-term solutions to meet frontend needs may lead to long-term technical debt. 4. **Dependency Management**: Shared services may still require coordination across teams, creating dependencies. ## **How We Can Implement Mirroring Effectively** To make mirroring work for our organization, we’ll need to: 1. **Define Clear Ownership**: - Assign each backend team to a specific frontend team or set of frontend teams. - Clearly define the scope of responsibility for each backend team (e.g., specific services, APIs, or domains). 2. **Establish Strong Communication Channels**: - **Act as One Team**: Encourage frontend and backend teams to operate as a single unit in terms of communication. For example: - Attend the same **daily standups** to share progress, blockers, and priorities. - Hold **joint sprint planning sessions** to align on goals and deliverables. - Conduct **shared retrospectives** to reflect on what’s working and what needs improvement. - Use collaboration tools (e.g., Slack channels, shared documentation) to facilitate ongoing communication. 3. **Adopt a "Contract-First" Approach**: - Define API contracts upfront to ensure frontend and backend teams can work independently once the contract is agreed upon. - Use tools like OpenAPI/Swagger to document and version APIs. 4. **Balance Autonomy and Collaboration**: - Give backend teams autonomy to make technical decisions, but ensure they collaborate with other backend teams to maintain consistency across the system. - Establish guilds or communities of practice for backend developers to share knowledge and align on best practices. 5. **Monitor and Adjust**: - Regularly assess the effectiveness of the mirrored structure. - Be prepared to adjust team assignments or responsibilities as needed. 6. **Invest in Platform Teams**: - Introduce a platform team to handle shared backend concerns (e.g., infrastructure, authentication, logging). - This reduces the burden on backend teams and ensures consistency across the system. ## **A Real-World Example** Imagine we have three frontend teams: - **Team A**: Works on the checkout flow for Customer A. - **Team B**: Works on user profiles for Customer B. - **Team C**: Works on search and discovery for Customer C. We could structure our backend teams as follows: - **Backend Team 1**: Dedicated to Team A, owns the checkout service and payment APIs. - **Backend Team 2**: Dedicated to Team B, owns the user service and profile APIs. - **Backend Team 3**: Dedicated to Team C, owns the search service and recommendation APIs. If there are shared services (e.g., authentication), a platform team could own those. ## **When Mirroring Makes Sense** Mirroring is particularly well-suited for organizations like ours, where: - **Multiple Customers Exist**: Each customer has distinct needs, making full cross-functional teams impractical. - **Specialization is Required**: Frontend teams require tailored backend support, but backend services need to remain generalized for the platform. - **Coordination Overhead is High**: Delivering features requires frequent communication across multiple teams, leading to delays and inefficiencies. - **Platform Modernization is a Priority**: The platform team needs the freedom to innovate without being bogged down by frontend-specific demands. - **Product Managers Need End-to-End Ownership**: Product managers can focus on delivering customer value while platform product managers ensure the PaaS layer evolves to meet future needs. ## **Conclusion: Walking the Balance** Mirroring frontend and backend teams isn’t a silver bullet, but it’s a pragmatic approach to solving the coordination challenges we face. By aligning backend teams closely with frontend teams, we can streamline communication, accelerate delivery, and maintain clear ownership—all while preserving the flexibility to serve multiple customers. The unit of alignment should follow the work, not the existing organisation chart. This approach also allows the **platform team to focus on modernizing the platform** without being pulled into frontend-specific priorities, ensuring the platform remains robust and future-proof. At the same time, **product managers can work end-to-end** within their aligned teams, delivering customer value efficiently, while **platform product managers continue solving for the PaaS layer**, ensuring the platform evolves to meet broader business goals. In a world where specialization and collaboration often feel at odds, mirroring offers a way to walk the balance. It’s not about merging teams or sacrificing flexibility—it’s about creating the right structure to deliver value efficiently and effectively. ### Our Journey to Streamline a Complex Consumer Frontend URL: https://www.differentshelf.com/our-journey-to-streamline-a-complex-consumer-frontend/ Last updated: 2026-08-24T16:18:01.000Z *Six consumer products once meant six distinct frontends, each with its own repository, release process and team. As the portfolio grew, so did the cost of coordinating releases, maintaining a consistent player experience and changing more than one product at a time.* The separation gave teams autonomy, but cross-product changes required repeated integrations and releases. Coordination took days. Each boundary added cost and another place for the player experience to drift. The transformation removed a sequence of boundaries, not one repository problem. We set three goals: - **Move Faster:** Reduce the time and effort required to release changes, enabling us to respond quickly to market demands and player feedback. - **Improve Product Consistency:** Ensure a seamless and cohesive player experience across all products, leveraging shared components and design systems. - **Balance Risk:** Minimize the risk of introducing bugs or breaking changes while maintaining the agility to innovate. This journey has taken us through several architectural evolutions—from a fragmented, product-specific approach to a unified frontend app housed in a monorepo, and eventually to a decoupled backend/frontend architecture. Along the way, we’ve explored various strategies to improve both player and developer experiences, including the adoption of a robust design system. Each boundary removed a different multiplier. Treating them as one change hides where the cost fell. ![Four aligned stages show integration counts falling from six to one with a monorepo, frontend release time falling from days to minutes after backend decoupling, and release paths and teams falling from six to one at a single domain.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/frontend-boundaries-ds-2026.png) Now, we stand on the brink of our next major step: moving to a **single domain**. For years, our products have been hosted across six different web sub-domains, which introduced inherent problems. Product switching was slow and cumbersome, and tracking player journeys across sub-domains was inconsistent, leading to fragmented data and a disjointed player experience. These challenges made it difficult to deliver a holistic, joined-up, and consistent experience for our players. By consolidating our products under a single domain, we aim to eliminate these pain points. This transition, which will take place over the coming weeks and months, represents a significant milestone in our journey. A single domain will streamline releases, enhance the player experience, and reduce coordination overhead. It will also provide a foundation for future innovations as we continue to evolve our architecture to meet the needs of our players and business. ## **Single Domain: A Foundation for Improvement** The move to a single domain is the cornerstone of our efforts to streamline the frontend architecture. By consolidating our six sub-domains into one, we’ve addressed long-standing issues that impacted both players and developers. This change not only simplifies the technical landscape but also unlocks significant benefits across player experience, SEO, and developer efficiency. ## **Player Experience Improvements** The move to a single domain has been transformative for our players. Previously, navigating across multiple sub-domains was slow and disjointed, creating friction in the player journey. Switching between products often resulted in delays, broken tracking, and a lack of continuity. Now, with all products hosted on a single domain, players enjoy a seamless, fast, and consistent experience. Tracking is more accurate, enabling us to better understand player behavior and deliver personalized experiences. The holistic, joined-up approach ensures that players can move effortlessly between products, fostering greater engagement and satisfaction. ## **SEO and Domain Authority Benefits** Moving to a single domain also brings significant **SEO (Search Engine Optimization)** advantages. Previously, having six separate sub-domains diluted our domain authority, as search engines treated each sub-domain as a distinct entity. This fragmentation made it harder to rank competitively for key terms and reduced the overall visibility of our products. By consolidating under a single domain, we’re able to consolidate our domain authority, making it stronger and more impactful. All inbound links, social shares, and engagement metrics now contribute to a single domain, boosting our search engine rankings and improving discoverability. Additionally, a unified domain simplifies our SEO strategy, allowing us to focus on optimizing one primary domain rather than spreading efforts across multiple sub-domains. This change not only enhances our organic search performance but also provides a better experience for players searching for our products online. ## **Design Systems: Consistency for Players and Developers** A key enabler of our journey has been the adoption of a unified **design system**. This system provides a shared library of components, patterns, and guidelines that ensure consistency across all products. For players, this means a cohesive experience—whether they’re using Product A or Product F, the look, feel, and behavior remain familiar and intuitive. For developers, the design system has been a game-changer. Instead of reinventing the wheel for each product, teams can now leverage pre-built, reusable components that are thoroughly tested and documented. This not only speeds up development but also reduces the risk of inconsistencies and bugs. The design system fosters collaboration across teams, as everyone works from the same foundational elements, and it empowers developers to focus on building features that enhance the player experience rather than wrestling with UI inconsistencies. Reuse matters when it releases teams to improve the journey, not merely when it reduces component count. ## **Developer Experience Improvements** The transition to a unified architecture and single domain has also significantly improved the developer experience. Previously, developers worked in silos, focusing on individual products without visibility into the broader ecosystem. This often led to duplicated efforts, inconsistent implementations, and a lack of shared knowledge. Now, with a unified architecture, shared design system, and single domain, developers can work against the holistic experience. Collaboration across teams has become easier, redundancy has been reduced, and consistency across products is now the norm. Developers can focus on building features that enhance the entire player journey, rather than being confined to the constraints of a single product silo. In this document, we outline the key steps in our journey, the lessons we've learned, and the trade-offs we've made. We also explore how different architectural approaches—monorepos, backend/frontend splits, and design systems—have impacted our ability to deliver value faster, maintain consistency, and manage risk. By sharing our experiences, we hope to provide insights for other organizations facing similar challenges and to inspire continued innovation in frontend architecture. ## Cost Factors associated with a Unified Frontend App with 6 Products | Scenario | Number of Times a Change Has to Be Integrated | Number of Times a Change Has to Be Released | Number of Teams Involved | Time Taken to Release | Overall Cost Factor | | ---------------------------------------------------- | ----------------------------------------------- | --------------------------------------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------- | | **Original** | 6x (multiple frontend repositories or products) | 6x (per product; a combined frontend and backend release) | 6 teams (one per product) | Days (due to coordination and multiple releases) | 216 (6 integrations × 6 releases × 6 days) | | **Monorepo** | Once (single repository for the unified app) | 6x (per product; a combined frontend and backend release) | 6 teams (one per product) | Days (release procedure itself didn't change) | 36 (1 integration × 6 releases × 6 days) | | **Monorepo & Backend/Frontend Split** | Once (single repository for the unified app) | 6x (per product; a combined frontend and backend release) | 6 teams (one per product) | Minutes (frontend can be released independently; no need for regulatory approval in most cases) | 0.6 (1 integration × 6 releases × 0.1 days) | | **Monorepo, Backend/Frontend Split & Single Domain** | Once (single repository for the unified app) | Once (single release for the unified app) | Single team | Minutes (frontend can be released independently; no need for regulatory approval in most cases) | 0.1 (1 integration × 1 release × 0.1 days) | ## Explanation for Unified Frontend App with 6 Products ## 1\. Original: - Each of the 6 products within the frontend app has its own repository or project, requiring changes to be integrated 6x. - Each product requires a combined frontend and backend release (6x), leading to significant coordination overhead. - 6 teams (one per product) are involved, leading to complex coordination challenges. - **Time Taken to Release**: Days, due to the need for coordination and multiple releases. - **Overall Cost Factor**: 216 (6 integrations × 6 releases × 6 days). ## 2\. Monorepo: - All 6 products are housed in a single repository for the unified frontend app, reducing integration complexity to once. - However, each product still requires a combined frontend and backend release (6x), as the release procedure itself hasn't changed. - 6 teams (one per product) are involved, but integration is simplified due to the monorepo. - **Time Taken to Release**: Days, as the release procedure itself didn't change. - **Overall Cost Factor**: 36 (1 integration × 6 releases × 6 days). ## 3\. Monorepo & Backend/Frontend Split: - The frontend and backend are decoupled, allowing the frontend to be released independently of the backend. - Each product still requires a combined frontend and backend release (6x), but the frontend can be released independently in minutes. - 6 teams (one per product) are involved, but the decoupling reduces coordination overhead for frontend releases. - **Time Taken to Release**: Minutes for the frontend, as it can be released independently; no need for regulatory approval in most cases. - **Overall Cost Factor**: 0.6 (1 integration × 6 releases × 0.1 days). ## 4\. Monorepo, Backend/Frontend Split & Single Domain: - The entire unified frontend app is in a single repository, and the frontend and backend are decoupled. - The product now only needs to be released once, as the app is treated as a single domain. - A single team handles the changes, minimizing coordination efforts. - **Time Taken to Release**: Minutes for the frontend, as it can be released independently; no need for regulatory approval in most cases. - **Overall Cost Factor**: 0.1(1 integration × 1 release × 0.1 days). ## Microfrontends: The Next Step in Evolution? - **What Are Microfrontends?**: Microfrontends extend the concept of microservices to the frontend, allowing different parts of a web application to be developed, deployed, and maintained independently by separate teams. - **Benefits**: - **Independent Releases**: Teams can release their microfrontend independently, enabling faster iteration and reducing coordination overhead. - **Team Autonomy**: Each team can choose its own framework, tools, and development practices, fostering innovation and specialization. - **Scalability**: Microfrontends are well-suited for large, complex applications with multiple teams working in parallel. - **Drawbacks**: - **Framework Compatibility**: Different microfrontends may use different frameworks (e.g., React, Angular, Vue), leading to potential compatibility issues and increased complexity in integration. - **Performance Overhead**: Loading multiple frameworks or libraries can increase page load times and reduce performance. - **Consistency Challenges**: Maintaining a consistent player experience and design system across microfrontends can be difficult without strict governance. - **Evolutionary Step**: - Microfrontends represent the next step in the evolution of frontend architecture, building on the benefits of monorepos and backend/frontend splits. - They enable even greater independence and scalability but require careful planning and tooling to address compatibility and performance challenges. - Organizations should consider microfrontends when they have multiple teams working on distinct areas of the application and need to scale development efforts without sacrificing release agility. ## Process and People Changes Required - **Technical Capabilities Are Not Enough**: While the scenarios above highlight the technical advantages of a monorepo, backend/frontend split, and single-domain approach, these benefits cannot be fully realized without corresponding changes in processes and team dynamics. - **Process Changes**: - **Release Processes**: Moving to a unified frontend app requires redefining release processes to accommodate independent frontend releases, automated testing, and continuous integration/continuous deployment (CI/CD) pipelines. - **Coordination**: Even with a monorepo, teams must establish clear communication and coordination mechanisms to avoid conflicts and ensure smooth integration of changes. - **Regulatory Compliance**: In industries like gaming, regulatory approvals may still be required for backend changes, necessitating a clear separation of frontend and backend release processes. - **People Changes**: - **Team Structure**: Transitioning to a single team or reducing silos between product teams requires a cultural shift, encouraging collaboration and shared ownership of the unified app. - **Skill Sets**: Teams may need to upskill or adapt to new tools and workflows, such as monorepo management, CI/CD practices, and independent frontend deployment. - **Mindset Shift**: Moving from product-specific ownership to a unified app mindset requires buy-in from all stakeholders, including developers, product managers, and leadership. - **Change Management**: Successfully adopting these technical capabilities requires a structured change management approach, including training, documentation, and iterative improvements to processes and workflows. - **Iterative Transition**: Organizations may need to transition gradually, starting with a monorepo, then decoupling the backend and frontend, and finally moving to a single-domain approach, ensuring that processes and teams adapt at each step. ## Key Considerations for Unified Frontend App - **Integration**: In a unified app, changes to shared components or libraries benefit from being in a monorepo, as they only need to be integrated once. - **Releases**: The frontend app can be released independently in minutes with a backend/frontend split, but backend releases might still be required depending on the nature of the change (e.g., API updates, new features, or bug fixes). - **Teams**: Moving to a monorepo or single-domain approach encourages collaboration and reduces silos between product teams. - **Time Taken to Release**: With a backend/frontend split, frontend releases can be completed in minutes, significantly reducing the time compared to the original and monorepo scenarios (which could take days). - **Overall Cost Factor**: The cost factor decreases significantly as integration, releases, and time taken to release are streamlined, with the lowest cost in the **Monorepo, Backend/Frontend Split & Single Domain** scenario. ### Lazy loading isn't just lazy, it's late: the web deserves faster. URL: https://www.differentshelf.com/lazy-loading-isnt-just-lazy-its-late-the-web-deserves-faster/ Last updated: 2026-08-24T16:17:57.000Z *For years, web developers were taught to minimise bandwidth. More recently, the rule became: load visible content early and defer what the user cannot see. We call that lazy loading.* *But it is just that: lazy. We fetch more only when the user scrolls it into view or moves to the next page. By then, lazy loading is often late, and the experience suffers.* It's fair to suggest lazy-loading is primarily designed to ensure your initial site load is fast and not a morsel of bandwidth or compute is wasted on anything that could put your [Core Web Vitals](https://web.dev/articles/vitals?ref=differentshelf.com#core-web-vitals) into the amber or - heaven forbid - the red. Which is perfectly reasonable and eminently sensible. But, what next? What happens when a user moves to the next route? Core Web Vitals can't measure a soft-navigation ([for now](https://developer.chrome.com/docs/web-platform/soft-navigations-experiment?ref=differentshelf.com)) and ergo we don't talk about what comes next nearly enough. But let's come to back to Core Web Vitals later. How did we get here? --- ## A short history on bandwidth consumption ![The box for a USRobotics Sportster 14,400 fax modem.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2024/09/image-6-1.png) My first modem We've come a long way since 14.4k baud modems (my personal first) when BBS's and ASCII art ruled. And if you think about it all our media consumption is driven, to some extent, by the delivery medium of the day. BBS's were necessarily text based. Downloading music (not streaming I hasten to add) only became practical when the [MP3 format](https://www.thoughtco.com/history-of-mp4-1992132?ref=differentshelf.com) came into being. Netflix started life as a [postal service](https://about.netflix.com/en/news/netflix-dvd-the-final-season?ref=differentshelf.com). And then, here we are today, where downloading games like Flight Simulator 2020 - [a whopping 100Gb+](https://www.flightsim.com/forums/topic/53255-msfs-2020-download-size/?ref=differentshelf.com) \- or streaming 4k movies is considered normal. > Juxtapose streaming a 4K movie against modern user-centric web guidance, web apps are so parsimonious in comparison. Did you know you can download the entirety of Wikipedia in around 19Gb but your typical 90 minute 4K movie runs to around 21Gb? Fact is, you could download many entire websites in a few minutes. Maybe less. ### Boiled Frog Syndrome You've doubtless heard of Boiled Frog Syndrome? It's a metaphor for the idea that if change is gradual enough you'll fail to react until it's too late. I occasionally bump into web developers who think there's still a [6 concurrent connection limit per domain](https://medium.com/@hnasr/chromes-6-tcp-connections-limit-c199fe550af6?ref=differentshelf.com). Last week when talking to content editors about low-resolution image it turned out they didn't know about the [Device Pixel Ratio](https://web.dev/learn/images/descriptive?ref=differentshelf.com#describing%5Fdensity%5Fwith%5Fx). Not that surprising. But how many of us realise how much crisper images can look on a modern mobile device? Maybe your experience is different but my hypothesis is that many in the web ecosystem are still optimising like it's 2015 and haven't noticed the explosion in available bandwidth. Little wonder native applications are considered a premium experience and [progressive web apps](https://web.dev/explore/progressive-web-apps?ref=differentshelf.com) (PWAs) remain somewhat in the doldrums. --- ## The problem with Core Web Vitals and SPAs Whilst it's right and proper that users' initial experience is a good one, because when it comes to a single-page application ([SPA](https://developer.mozilla.org/en-US/docs/Glossary/SPA?ref=differentshelf.com)) there is no good universal way to measure what happens next, that experience has been sorely neglected. Yes, there's now [INP](https://web.dev/articles/inp?ref=differentshelf.com), but that's about ensuring the web app responds in someway to the users tap or click, if you visually respond with something - anything - you pass. If it takes another 10 seconds to complete the soft-navigation that's simply not captured as of today. ![A threshold line shows good INP up to 200 milliseconds, needs improvement from 200 to 500 milliseconds and poor beyond 500 milliseconds. A second timeline shows that next paint can arrive quickly while the navigation continues to route completion.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/inp-response-versus-journey-ds-2026.png) Having gone through a long journey optimising the [Angular](https://angular.dev/?ref=differentshelf.com) SPA I work on, we've followed almost every last piece of best practise including but not limited to pre-loading; lazy-loading; async loading; code splitting; bundle rollups; getting rid of extraneous change detection cycles; edge caching; other types of caching; stale-while-revalidate; algorithm optimisation; image optimisation; compression; minification; moving tracking and 3rd parties to a worker-thread via [PartyTown](https://partytown.builder.io/?ref=differentshelf.com); and countless other micro-optimisations. Yet those soft-navigations still weren't fast enough. When you tapped there was a perceptible delay before you were at your destination. Why? Why, because in our app - and many others I suspect - they still needed to collect the data required to show the next page and that meant a round-trip to the content delivery network (CDN) or even the Origin. And with lazy loading of routes, the first time you show a new route you probably need to download, parse, and compile some JavaScript. Only then would you know what data you needed, then you'd have to fetch it, and only then could you render it. ### Buffering Everyone knows what buffering is these days. Why? Because when it doesn't work and you're streaming the latest episode of Rick & Morty you get an intensely frustrating spinning wheel. Worse still, no real way of knowing when it'll disappear so you can continue your experience. ![Two sequences compare reactive and anticipatory loading. In the first, route code, data and rendering begin after the player taps, leaving an uncertain wait. In the second, likely route code and data load before the tap so less work remains afterwards.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2026/08/spinner-uncertainty-ds-2026.png) But that's the default experience of the web. There is no buffering per se. Spinning wheels and [skeleton loading screens](https://www.nngroup.com/articles/skeleton-screens/?ref=differentshelf.com) run amok. For many, what is needed next is only retrieved *after* you tap. That's simply too late. > Put it all together and the chance of that navigation happening end-to-end such that it feels instant (<200ms), is unlikely. Think about it, strip everything else away, and the typical out-of-the-box framework would have the following occur: 1. User taps on a new route 2. A JavaScript module needs to be downloaded, parsed, and compiled. 3. The data for the route needs to be fetched 4. ... Doesn't that already sound wrong? Surely we could've already found an idle moment to do this that wouldn't have hurt the users' experience. On the contrary it could have been proactive. After all, that's what [requestIdleCallback](https://caniuse.com/requestidlecallback?ref=differentshelf.com) is for! Remember we haven't even got the content yet let alone rendered it. Lazy, late, whichever way you cut it we're doing and thinking about this in one-dimension, that of initial loading time. Could it be we've gone too far with lazy loading and we need to compensate for what we removed from the initial load by proactively loading or buffering what comes next? ### Prior Art Solutions exists in the form of [ngx-quicklink](https://www.npmjs.com/package/ngx-quicklink?ref=differentshelf.com) (for Angular): it automatically downloads the lazy-loaded modules associated with all the visible links on the screen. But it's fiddlesome relying on routes being specified in a certain way. Not to mention ignores the fact that there tends to be data that also needs to be fetched. There are libraries like [Guess.js](https://github.com/guess-js?ref=differentshelf.com) that figure out where your users are likely to click next based on analytics and machine-learning to tell you what to prefetch. But then, how do you do that? The [Speculation Rules API](https://developer.mozilla.org/en-US/docs/Web/API/Speculation%5FRules%5FAPI?ref=differentshelf.com) while experimental is not for SPAs, only intended for multi-page applications (MPAs). The problem is obviously understood but solutions are hard to come by. We need to fetch that data proactively. And to the best of my knowledge there is no way out-of-the-box to do that that's widely discussed. Yet, even one round-trip even on a good connection, is likely to risk that soft-navigation not feeling particularly instant. Maybe I've missed something, but my gut is that it's way too hard to give users that instant navigation experience, to the detriment of much of the web. Prediction I predict when Core Web Vitals can measure soft navigations accurately, it won't be long before SPA frameworks build in developer-friendly mechanisms to proactively buffer content a tap away, before it's needed. --- ## A possible solution for Angular and other SPAs To proactively fetch that data what needs to be true: 1. We need to know the URLs for the data needed to render those routes. 2. There needs to be some caching mechanism available such that the data is readily available when it's time to render it. ### Proactive soft navigations Angular has the concept of [route resolvers](https://angular.dev/api/router/Resolve?ref=differentshelf.com). Primarily it's designed to ensure all the data required to render the page is available prior to you beginning to render it, so you can avoid things like [CLS](https://web.dev/articles/cls?ref=differentshelf.com). So close. I mean, really close. Effectively this route resolver does exactly what we need: it figures out what data is needed and fetches it prior to the commencement of rendering. This is where in our app we introduced proactive soft navigations: we figured out how to run the route resolvers for links in the viewport using an[ interaction observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection%5FObserver%5FAPI?ref=differentshelf.com). No rendering or anything that would consume CPU or really even block the main thread. We had a ready-made caching mechanism in the form of a regular browser cache; the web is really good at that. Using a CDN meant the round-trip time was minimised and our Origin servers weren't hurt by the inevitable uptick in requests as we fetched data that, frankly, may never be used. Throw in [stale-while-revalidate](https://web.dev/articles/stale-while-revalidate?ref=differentshelf.com) and there's even a way to make sure the cache stays fresh but in an async manner. Look at that timeline now: 1. A JavaScript module is downloaded, parsed, and compiled during an idle moment when the route that uses it is in the viewport. 2. We figure out what data the route needs and proactively fetch it (effectively buffering it for later). 3. *The user taps on a new route.* 4. We render it. Doesn't that feel better? When the user taps we now have everything required, up front, and typically that rendering can be done such that it feels instant. Even on a middle of the road Android device on an average connection, it works. ## Result: our navigations are now \~75% faster Do you want to see what it looks like? Depending on your jurisdiction and assuming you're over 21 years of age visit the Sportsbooks at BetMGM, or bwin, or Sportingbet. Or just enjoy these flame graphs instead. ### Interaction Observer triggering the Angular Route Resolver ![A bwin page beside a browser network panel showing fixture requests triggered before navigation.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2024/09/interaction-observer-proactive-buffering-bwin.gif) You can see each request takes up to \~500ms, that's 500ms saved on the next navigation ### Time to Navigate: Before ![A bwin product switch beside a browser performance trace. The network request takes 394 milliseconds and the complete navigation about 500 milliseconds.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2024/09/time-to-navigate-without-proactive-buffering.png) Without proactively fetching the data the navigation takes \~500ms on a 4G connection ### Time to Navigate: After ![A bwin product switch beside a browser performance trace after proactive loading. Cached data arrives in 16.4 milliseconds and the complete navigation takes about 120 milliseconds.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2024/09/time-to-navigate-after-proactive-buffering.png) The total time to navigate is now \~120ms What are we left to do in our implementation at the time of writing? Handle page visibility: how do we handle refreshing content after regaining focus when the user returns to the tab or foregrounds the application? Edit: of course one needs to be mindful of users who have [Save Data](https://web.dev/articles/optimizing-content-efficiency-save-data?ref=differentshelf.com) enabled. The above isn't right for everyone, especially those who explicitly want to save data, not everyone has unlimited bandwidth. --- ## Epilogue I might be wrong, or maybe I've missed something, but either way I think this topic is so rarely discussed it drove me to write about it. Maybe other component libraries or frameworks have a ready-made out-the-box way to do this, answers on a postcard if so. As I wrote earlier, I predict once Core Web Vitals, browsers, and frameworks agree a universal way to measure soft-navigations there will be a renaissance in how we handle them. Hopefully this time around the answer might not be to only minify, reduce, remove, split, and otherwise be lazy. Maybe the answer will also involve being more proactive to give a better user experience, not to mention an easier developer experience. Huge thanks to [Push Based](https://push-based.io/?ref=differentshelf.com) and [Christopher Holder](https://www.linkedin.com/in/christopher-p-holder/?ref=differentshelf.com) in particular who worked on the implementation with me. If you ever need Angular expertise don't hesitate to call them. ### Local Without Losing Shape URL: https://www.differentshelf.com/local-without-losing-shape/ Last updated: 2026-08-22T22:55:13.000Z *A player in São Paulo should not feel like a guest in a London lobby. Same skeleton, different clothes. The craft is to let the outfit change without replacing the bones every quarter.* Most teams treat localisation as a tidy table of strings, currencies, and date formats. Necessary, yes. Sufficient, no. What players notice is tone, ritual, and timing. Which payment method appears first. How identity checks unfold. Which promotions surface before a match. How typography and colour carry a brand’s voice. That is where multi-brand platforms either scale with grace or sink into small, persistent messes. Local difference belongs at named seams. Everywhere else, consistency should win. This essay sets the frame for a series on building multi-brand frontends that feel native in every market without turning into a zoo. It introduces five planes that divide the work cleanly and shows what “difference made safe” looks like in practice. ## The problem we are really solving Tuesday morning, two launches collide. Spain needs a new identity step before lunch. Brazil wants a payment tweak for the evening rush. Both touch the same code path. A small change in Madrid ripples into São Paulo’s build, someone rolls back the whole thing, and three teams spend the afternoon in a call naming their branches like they are ships. Nobody ships what they planned. Everyone is now behind. Product teams want speed without collisions. Move fast in parallel, do not wait on another region for local taste or policy. Shared code should not mean shared bottleneck. When something goes wrong, the blast radius should be one profile, one capability, one slot. Recovery should be precise, not theatrical. Shared code becomes a bottleneck when local difference has nowhere explicit to live. To get there, we need concurrency without chaos. The clean way is to break the job into separate tensions, each with its own remedy. MECE, so nothing overlaps and nothing is missed. ## Six tensions that define the job 1. **Policy churn vs code stability** Rules change daily. Code should not. We make rules data, not branches. This is the **Configuration plane**. *Follow-up article 2: schemas, versioning, cache keys, and change control you can audit.* 2. **Brand individuality vs platform consistency** Markets need their own voice. The platform needs one grammar. Tokens are the currency, patterns the grammar, brand packs the artefacts. This is the **Design system plane**. *Follow-up article 3: tokens, patterns, and packs that avoid drift.* 3. **Feature velocity vs shared surface area** Teams must compose features differently without forking the bones. Components are APIs. Flows are recipes. DI swaps policies and adapters. This is the **Angular plane**. *Follow-up article 4: capabilities not pages, route recipes, build profiles, SDK hygiene, and monorepo discipline.* 4. **Local partners vs global capability contracts** Every market brings a new provider. The capability must stay the same. Localism sits behind the API, not in the web tier. This is the **Platform API plane**. *Follow-up article 5: versioned capabilities, market adapters behind the API, parity maps, and sunset rules.* 5. **Campaign tempo vs release tempo** Editors move faster than sprints. Content should appear, compete, and retire without a new build. This is the **Content management plane**. *Follow-up article 6: events, placements, targeting by dimension, arbitration, preview, and pacing that does not flicker.* There is a final piece: a pocket guide to how this fails in the wild and how to catch it early. *Follow-up article 8: a field guide to conditional creep, SDK sprawl, config-as-code, and their opposites.* *Follow-up article 7 threads through all of this: incentives, rituals, and governance that works in a lunch break.* ## Five planes at a glance - **1) Configuration** — Rules as data. **Include:** feature flags, limits, KYC requirements, allowed payments, caps, blackouts, experiments. **Exclude:** layout, copy, flow logic. **Health:** versioned, schema-checked, auditable snapshots. **Failure:** config mutates into a scripting language. - **2) Design system** — Shared grammar. **Include:** tokens, patterns, brand packs. **Exclude:** business rules, SDK hooks, market policy. **Health:** designers argue about patterns; brand packs swap without code changes. **Failure:** per-brand component sets, drift, accessibility debt. - **3) Angular** — Composition without forks. **Include:** capability libraries, route recipes, provider graphs, lazy variance, build profiles. **Exclude:** vendor calls in UI, colours in JS, taste conditionals. **Health:** a market adds an adapter and updates a recipe, no component edits. **Failure:** conditional creep, SDK imports in components. **DI in a sentence:** the switchboard supplies the right implementation by contract. - **4) Platform APIs** — Facts and transactions. **Include:** versioned capabilities for wallet, identity, offers, odds; market adapters behind the API. **Exclude:** presentation, editorial priority. **Health:** one contract returns local methods; parity is visible; old versions sunset on schedule. **Failure:** one-off endpoints, mystery flags, business rules pushed to the frontend. - **5) Content management** — Voice and pace. **Include:** events, placements, targeting by brand or market or device or identity or context or consent, editorial priority, caps, lifecycle, preview. **Exclude:** flow logic, raw HTML with behaviour. **Health:** persona and time-travel previews, explainable arbitration, viewable impressions, holdouts for lift. **Failure:** content acts like code, rule sprawl, SSR and hydration flicker. ## A short story about DI Picture a busy switchboard. A team picks up the phone and asks for payments, not a specific gateway. The operator checks the caller’s profile and connects them to the right line. In Angular, the component is the caller and the payments contract is the number. Providers map that number to the correct implementation for each brand and market. Spain gets card, Brazil gets Pix, tests get a fake. The component never changes its request, and the switchboard keeps the wiring tidy. ## Extension without forks - **Slots where variation is inevitable** Consent banners, payment surfaces, identity steps, promotions. Expose a slot rather than hiding a conditional. - **Policies as first-class code** Express rules for timeouts, formats, limits, and consent as injectable services. Features ask policy for the truth. No geography sprinkled through templates. - **Adapters at the seams** Wrap every partner SDK behind a small interface. Markets plug in their choice. The capability contract stays steady. The UI stays boring, which is a compliment. - **Profiles that wire, not duplicate** One app shell, many build profiles. A profile chooses brand pack, policy set, and adapter implementations. Brazil feels independent without being a separate codebase. ## Performance and observability with context - **Lazy where variance lives** Market-only SDKs and steps load only when needed. If a market does not have a method, it never downloads it. - **Budgets for motion and weight** Flourish is welcome until it stutters. Set a simple rule for transitions on a baseline device and spend the budget where it matters. - **Measure apples with apples** Tag real-user metrics with brand, market, device class, and policy version. Alert locally before globally. Averages without context lie. ## Governance that does not feel like governance - **One-page playbooks** How to add a slot, publish a brand pack, register an adapter, change a policy, curate an event. No folklore. - **A registry, not a treasure hunt** Index every capability, adapter, brand pack, and placement. Each entry has an owner, a status, and a last verified date. Items without status are untrusted. - **Release trains** Core ships on a predictable cadence. Brand packs and wrappers ride a train behind unless they opt in. Markets patch in a small window, then rebase to head. - **Sunset by default** Local overrides and transitional API versions expire unless renewed with evidence. Variance remains honest. ## Costs of getting it wrong Drift. Duplicated effort. Inconsistent performance. Fragile compliance. Leadership fatigue. A platform that says “it depends” more than it ships. ## What good looks like A small core with stable contracts. Markets moving at their own pace without leaving the rails. Designers arguing about patterns, not pixels. Engineers implementing policy, not politics. Players who feel at home. ## The modest claim This approach does not promise perfection. It promises clarity. You trade improvisation for explicit extension. You trade folklore for contracts and audits. You trade a fast fork today for a platform that is still shippable next year. ## What comes next in the series - **The Configuration Plane** - **The Design System Plane** - **The Angular Plane** - **The Platform API Plane** - **The Content Management Plane** - **The Human Loop** - **Field Guide: Failure Modes and Success Patterns** ### Algorithmic Plotter Art URL: https://www.differentshelf.com/algorithmic-plotter-art/ Last updated: 2026-08-22T22:55:13.000Z ![A rectangular plotter artwork made from thousands of brightly coloured triangular paint strokes.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2025/08/newer-isnt-always-better-1-1.webp) ## Newer Isn't Always Better The pattern is derived algorithmically from a delaunay transformation of a photograph of a couple; her arm around him. The technique to apply the paint robotically in a smooth and consistent fashion was developed over many months and represents 1000s of lines of code and countless iterations. There are 2030 triangles with 291.18 metres of paint strokes. The algorithm draws the image. Paint, drying time and registration decide whether it becomes an object. --- ![A plotter artwork of narrow vertical colour bands progressing across a white board.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2024/09/image-2.png) ## Towards The Light, Then Back Again --- ## Technical Discussion ### Equipment Like many, in the recent resurgence of interest in plotting, the tool of choice is the [AxiDraw](https://axidraw.com/?ref=differentshelf.com) and in particular the [SE/A3](https://shop.evilmadscientist.com/908?ref=differentshelf.com). The API is very good, as is the CLI it ships with, and enables full programmatic control. Furthermore on the occasion I've had a question the response from [Evil Mad Scientist](https://www.evilmadscientist.com/?ref=differentshelf.com) has always been helpful. Having had something of a crash course in artists' materials plotting directly onto primed board is my preference. As opposed to paper or canvas, something like [gessobord](https://ampersandart.com/gessobord.php?ref=differentshelf.com) gives a superior finish when using acrylic paint. It's also more forgiving if the surface the plotter is on isn't perfectly flat. In terms of paint look no further than the [Molotow Acrylic Paint Markers](https://shop.molotow.com/en/marker-refills/one4all-227hs-display-set-complete.html?ref=differentshelf.com). Real paint that doesn't require an occasional change of clothes coupled with predictable flow. Initially prototyping was done with [Winsor & Newton Pigment Markers](https://www.winsornewton.com/na/pigment-marker?ref=differentshelf.com) which are excellent in their own right but didn't produce the finished effect I so lusted after. ### Language Choice Many other programmers producing algorithmic art I've come across use [Processing](https://processing.org/?ref=differentshelf.com) either directly or via a wrapper library. Having had some experience with it when developing the [Newton Virus](http://www.everita.com/how-the-newton-virus-was-made?ref=differentshelf.com) it didn't seem the right fit or perhaps more truthfully: it isn't something I wanted to invest time in learning. Snobbery, perhaps. Given that we're dealing with — ultimately — lines, or vectors to be more specific, something that can translate a series of vectors into SVG parlance is what is required to get the output of the algorithm onto a board in acrylic paint. To that end Lisp, Clojure, or something functional seemed to be the order of the day. However for now, Python suffices with [matplotlib](https://matplotlib.org/?ref=differentshelf.com) used as a shim-layer to get from vertices to SVG. It was and continues to be the path of least resistance if not quite as intellectually satisfying as I might have hoped. In fact, now I've produced a couple of pieces, it's fairly obvious I could replace matplotlib with a relatively simple output layer given everything, so far at least, has been expressed as as a series of lines which is about as basic as SVG gets. Even if you were to use a circle primitive you'd have to describe that fill as a series of lines. As an aside the title of my first work is *Newer Isn't Always Better*. The reason being, believe it or not, I originally intended to implement all of this in [Logo and Turtle Graphics](https://en.wikipedia.org/wiki/Turtle%5Fgraphics?ref=differentshelf.com) furthermore on a [BBC Micro](https://en.wikipedia.org/wiki/BBC%5FMicro?ref=differentshelf.com) thus proving it could have all been done in 19821. Perhaps when I tire of producing somewhat naïve paintings I'll move into installation art instead. ### Programming Workflow The environment and workflow is fairly typical. It's described as a Dockerfile, coupled with some Emacs chicanery, and finally an auto-refreshing browser window to display the SVG output: it's in the spirit of a REPL environment if not the embodiment. In fact to an extent, a piece can be produced end-to-end without ever leaving Emacs, I know there are fetishists out there who will not so secretly be pleased by that. The only minor annoyance, and it is indeed minor, is that the Docker runtime on Mac (or more specifically the hypervisor) is oblivious to USB devices and hence the AxiDraw itself can't be controlled from within it. Well, I could if I switched to using *docker-machine*. This means one has to go to the bother of hand-cranking the drivers onto, in my case, the Mac itself. It could be worse I suppose. ### Hatching (or flood-filling polygons) When explaining the process behind the paintings it's how much effort goes into filling the shapes with paint that surprises innocent bystanders. Most people are so used to a flood-fill in Photoshop or its ilk they've never stopped to think about how it might be implemented, algorithmically speaking. And while AxiDraw does provide some helper functions for this, adding a `fill="purple"` attribute to a SVG shape isn't going to cut it. Tangentially I think that's borne out by a lot of the plotter art that is produced of late; it has a tendency to be line art and so much so it's implicit the work has been produced by a plotter and not by hand2. The AxiDraw supplied extension to Inkscape is more than two thousand lines of [fiendishly clever code](https://github.com/evil-mad/EggBot/blob/master/inkscape%5Fdriver/eggbot%5Fhatch.py?ref=differentshelf.com) that turns elements like circles, rectangles, and polygons into a series of vertices and projects lines through the bounding box. It then works out where these lines intersect with the original shape and clips them accordingly. Something of a simplification but you get the idea. Initially having tried it a few times the extra step to hatch the SVG output in InkScape stuck in the craw sufficiently that I implemented a very crude version of the aforementioned hatching technique myself. It was also many lines of code and while on screen it looked great in acrylic paint it looked like, well, it looked like it had been hatched by someone who had no consideration for ink bleed or brush size nor falling perceptibly outside the lines. Finally and most worryingly they didn't care if the paint faded or ran out mid-stroke: most disappointing. And — again — withstanding all that, it looked too obviously plotted. ### How I Learned to Stop Worrying and Love Clipper Someone doubtless has already figured this out but when I was looking for a better solution my DuckDuckGo-fu wasn't good enough. Conceptually it struck me a much easier way to fill a polygon is to draw the same polygon again, but slightly smaller, and repeat at intervals slightly smaller than the width of the brush to ensure no gaps. In steps [Clipper](http://www.angusj.com/delphi/clipper.php?ref=differentshelf.com) which again is another devilishly clever library that amongst other things can perform [polygon offsetting](http://www.angusj.com/delphi/clipper/documentation/Docs/Units/ClipperLib/Classes/ClipperOffset/%5FBody.htm?ref=differentshelf.com). That is to say take a set of vertices and return another set of slightly smaller vertices. Pop it in a loop and hey presto: a filled polygon with some distinct advantages over one that has been linearly hatched. The machine can execute every line perfectly and still betray the picture. First and foremost: there's little risk of the ink bleeding over the edge of the outer polygon or conversley a gap between the hatch line and the outer polygon. The polygons being plotted are concentric and ever decreasing in area. Secondly the plotter doesn't have to change direction as much and ostensibly this produces flatter and more even fills particularly when using acrylic paint. Finally it's trivially easy to connect the polygons which means the entire fill can be performed with no *pen up* or *pen down* movement which reduces unsightly blotches that can occur when the pen first comes in contact with the surface. Pigment can vary in viscosity and opacity and the effect can be more pronounced depending on the colour. Technically to achieve no *pen up* or *pen down* you do inevitably retrace small areas albeit in a different direction. Despite this, offsetting has proved a far superior technique and algorithmically simpler to implement. ### How To Paint, Not Print It wasn't obvious to me at the outset the direction in which this would go. Initially, when the AxiDraw was originally purchased, I thought the production of intricate lines would be the primary focus. Instead it's filled polygons and how to transfer them from screen to physical media that is seemingly lesser explored and therefore of more interest. On screen or giclee printed one doesn't have to worry about painting over the same area and accidentally picking up the paint that was just put down. Or how the various different pigments vary in consistency. Or minimising *pen-up* and *pen-down* to reduce visible sploges. There's a suprising amount of scope for innovation if you like your fine art to have both paint *and* brush strokes. The best optimisation was not faster plotting. It was learning when the paint needed time. To solve the problem of picking up paint that had only gone down moments before and was still wet I briefly considered using masking. Any portion of a polygon that was obscured would simply not be plotted; again an obvious technique on screen or in print but noticable when done in paint: the brush strokes, if I can call them that, confessed all. Then I was quite convinced that unioning the polygons that intersected on the same layer was the way to go: and it wasn't until I wrote the above that I realised that the brush strokes mattered more, to me at least. Two or more triangles for example would become an irregular polygon. These look noticeably different when hatched. Instead it transpires that patience is the answer. Each colour is plotted as a single layer in a single run. Rather, for colours where there are overlaps, the polygons are separated into additional layers. This is an easy way to create a delay — giving the paint time to dry — without sacrificing the brush strokes and the overall aesthetic. ### Board Positioning Techniques One of the more frustrating aspects, the algorithms themselves withstanding, is positioning the board in relation to the plotter. A millimetre out in any direction can be noticeable, especially when dealing with regular geometric shapes. The piece *Towards The Light, Then Back Again* was particularly unforgiving since any variance was very visible against the white margin. It's fairly obvious making a jig to hold the board parallel to the plotter at a fixed distance is a workable solution. Alas, it's still on the to-do list. At the outset my intention was to produce much larger works and simply move the board around plotting an A3 sized section each time. Perhaps predictably I gave up in disgust: getting the registration perfect was infuriatingly difficult. If a polygon saddled two sections the transition was obvious even with the smallest margin of error. It transpires after enquiring that Evil Mad Scientist will manufacture larger plotters to order: it's a problem I'm happy not to solve. 1It could have of course been done, and was done as far back as the sixties 2Patently that is no slur: there are numerous astonishingly beautiful examples of line art to aspire to produce 3Apologies for the florid language, Paul *Last updated: 20th February 2020 — as the work develops this treatise will be updated* --- ![A plotter artwork of multicoloured lines radiating towards a small white centre.](https://storage.ghost.io/c/0f/68/0f687d79-8b68-43bd-a87f-cc95b18e521f/content/images/2024/09/image-3.png)