Skip to content
matthewcm.dev

    ↑ ↓ move · ↵ open · tab scopeopen graph →

    $ radar --rings adopt,trial,assess,hold

    Radar

    A personal tech radar in the Thoughtworks style: what I'd use today, what I'm trialling, what I'm watching, and what I'd hold off on.

    Tech radar with 24 items across four rings: adopt, trial, assess and hold.ADOPTTRIALASSESSHOLDTechniquesToolsPlatformsLanguages & frameworks123456789101112131415161718192021222324
    1. Techniques

      1. 1Context engineering for coding agentsadopt
      2. 2Decouple the render loop from the data feedadopt
      3. 3Golden pathsadopt
      4. 4Platform as a productadopt
      5. 5Evals-driven development for LLM featurestrial
      6. 6Feedback sensors for coding agentstrial
      7. 7Durable execution for agent workflowsassess
      8. 8Mutation testingassess
      9. 9Coding throughput as a productivity measurehold
      10. 10MCP by defaulthold
    2. Platforms

      1. 11Server-Sent Events for one-way streamsadopt
      2. 12OpenTelemetry for agent tracestrial
      3. 13Agent UI protocols (AG-UI, MCP Apps)assess
      4. 14Developer portals (Backstage, Port)assess
      5. 15WebTransportassess
    3. Tools

      1. 16Claude Codeadopt
      2. 17MSW (Mock Service Worker)adopt
      3. 18Playwrightadopt
      4. 19Vitest (with Browser Mode)adopt
      5. 20Playwright test agentsassess
      6. 21Cypress for new projectshold
    4. Languages & frameworks

      1. 22Astroadopt
      2. 23Zustandadopt
      3. 24Rust + WebAssembly for UI hot pathsassess

    Techniques

    Context engineering for coding agents

    adopt

    Curate the smallest set of high-signal instructions, docs and examples an agent sees, and version them with the code.

    Prompting has turned into curating context. I run my whole second brain through a single constitution file and a few slash-command skills, and the quality of the agent’s work tracks how well that context is written, not how clever the prompt is.

    Thoughtworks now puts context engineering and curated shared instructions for teams in Adopt. The flip side, which they flag as a caution, is instruction bloat: a 2,000-line rules file is the agent equivalent of a training plan with fifteen exercises. Fewer, sharper cues win.

    Golden paths

    adopt

    An opinionated, supported default that takes a team from nothing to a deployed, observable service. Optional but attractive.

    A golden path is the calisthenics Recommended Routine of engineering. It’s a sensible default that beginners can follow without making fifty decisions, and experts can leave once they know why. I’ve built one for designers on a low-code workflow platform, and I’m building a “golden path for one” for my side projects.

    The rules I keep coming back to: keep them thin and versioned, reference shared modules rather than copying code into templates, and measure whether teams actually find them faster. A golden path should never become a cage.

    Platform as a product

    adopt

    Treat internal developers as customers: research, roadmap, adoption metrics, no mandates.

    Every platform I’ve seen fail was built for developers rather than with them. The ones that work do user research, publish a roadmap and earn adoption by being the easiest option. If people have to raise a ticket to get something done, it’s a service desk, not a platform.

    Recent industry surveys point the same way: platform maturity seems to be part of what separates teams getting real value from AI from teams just adding noise, yet plenty of platform teams still don’t measure their success at all. Adoption, lead time and developer satisfaction are the minimum I’d track.

    Decouple the render loop from the data feed

    adopt

    Let high-frequency streams update a store outside React and paint on animation frames, not on every message.

    When a feed pushes hundreds of updates a second, re-rendering on every message is how you drop frames. The pattern I rely on for real-time trading UIs: write ticks into a store transiently, coalesce them, and let components read the latest state once per animation frame.

    It’s backpressure on the client. The network can be as bursty as it likes, but the screen only needs 60 good frames a second. Measure it with the Performance panel and long-task tracking, not by eye.

    Evals-driven development for LLM features

    trial

    Build an eval set from real traces before tuning prompts. Pass rate is a product decision, not a unit test.

    Most LLM features I’ve seen stall at “vibe checks”. The way out is boring: collect traces, do error analysis, write targeted evals, then change prompts or models against that baseline. Evals are to AI features what a test pyramid is to a UI.

    For a platform, this is a golden-path capability. Every assistant or agent a team ships should come with an eval harness and a release/rollback path by default, not bolted on later.

    Feedback sensors for coding agents

    trial

    Give agents deterministic checks such as type checker, linter, unit and e2e tests, so they self-correct before a human reviews.

    This is “harness engineering”: guides (feedforward) shape what an agent does, and sensors (feedback) tell it when it went wrong. In a frontend repo the sensors already exist: tsc, ESLint, Vitest, Playwright. The work is wiring them in so the agent runs them itself and reads the output.

    It feels exactly like handstand balance. You don’t hold still, you make small corrections from fast feedback. I’m trialling it on personal repos and want to see how much review steering it saves.

    Durable execution for agent workflows

    assess

    Long-running agents need to persist progress and resume after a failed model or tool call.

    An agent that works in a demo is a happy-path workflow. In production, model calls time out, tools fail and runs span hours. That’s a distributed-systems problem we’ve solved before with workflow engines and sagas.

    Thoughtworks lists ignoring durability as a caution and points to durable execution in frameworks and platforms like Temporal. Having worked on a BPMN workflow platform, I’m curious how far classic orchestration patterns carry over to agents.

    Mutation testing

    assess

    Mutate the code and check the tests notice. A better signal of test quality than coverage, and a strong sensor for AI-written code.

    Coverage tells you a line ran, not that anything checked it. Mutation testing flips operators and deletes branches, then asks whether a test failed. It’s an old idea that’s back because agents can write lots of tests that assert very little.

    I want to try it (e.g. Stryker for TypeScript) on a well-tested component library and see whether the cost is worth it in CI, or whether it belongs as a nightly job.

    Coding throughput as a productivity measure

    hold

    Lines, PRs or AI-accepted suggestions measure activity, not outcomes. Stick to DORA-style flow and reliability metrics.

    With coding agents, throughput numbers go up whatever happens to quality. Measuring it rewards the wrong thing and builds what Thoughtworks calls codebase cognitive debt: code nobody on the team really understands.

    As a lead I’d rather watch lead time, change failure rate and recovery time, plus a qualitative check on whether people still understand the system they own.

    MCP by default

    hold

    Reach for a protocol layer only when you need interoperability. A good CLI with JSON output often serves agents better.

    I like MCP and use it, but wrapping every internal API in an MCP server by reflex adds an “abstraction tax”: less fidelity, more moving parts, more permissions to govern. Thoughtworks puts “MCP by default” in Caution for the same reason.

    My rule: if a well-documented CLI with structured output and clear errors does the job, start there. Use MCP when you need auth, multi-tenant governance or cross-tool interoperability.

    Platforms

    Server-Sent Events for one-way streams

    adopt

    For server-to-client updates such as prices, notifications and LLM token streams, SSE is simpler than WebSockets and works over plain HTTP.

    I reach for WebSockets when the client genuinely talks back at high frequency. For everything else, SSE gives automatic reconnection, plain HTTP semantics and easy proxying. It’s also how most LLM APIs stream tokens, and protocols like AG-UI build on it.

    Picking the lightest transport that fits is the same instinct as choosing the easiest progression that still trains the skill.

    OpenTelemetry for agent traces

    trial

    Trace agent runs, model calls and tool calls with the same standard as the rest of the stack. GenAI conventions are still in development.

    OpenTelemetry is already the default for services. The interesting part is extending it to agents: a root span per run, child spans for model calls and tool executions (including MCP calls). Then an agent’s behaviour shows up in the same traces as the APIs it calls.

    The GenAI semantic conventions were still marked Development when I last checked, so expect attribute names to churn. I’d trial it with that caveat rather than wait, because retrofitting observability later is always more expensive.

    Agent UI protocols (AG-UI, MCP Apps)

    assess

    Standard ways for agents to stream events and render interactive UI, rather than every team inventing its own.

    This is where my two pillars meet. AG-UI streams agent events to rich frontends over SSE or WebSockets, while MCP Apps let a server ship UI alongside its tools. Both are basically real-time UI problems: streaming state, partial updates and optimistic rendering.

    It’s too early to pick a winner. I’m watching which one gets traction and building small experiments rather than committing.

    Developer portals (Backstage, Port)

    assess

    A front door to the platform: service catalogue, templates, docs and scorecards. Valuable only once the platform underneath is real.

    Portals are often the first thing organisations buy and the last thing that should be built. A catalogue over a messy estate is a prettier mess. I’d start with golden-path templates and self-service APIs, then add a portal as the UI.

    Thoughtworks has Port in Trial. I’m assessing the space with one question: does it reduce cognitive load for stream-aligned teams, or add another tool to learn?

    WebTransport

    assess

    HTTP/3-based streams and datagrams in the browser, now Baseline across major engines.

    With Safari support arriving, WebTransport is now reported as Baseline across the major engines. Unreliable datagrams and multiple streams without head-of-line blocking are interesting for real-time UIs where the latest value matters more than every value.

    It’s “newly available”, not “widely available”, and server and proxy support is still patchy. I’d assess it behind a WebSocket fallback before betting a product on it.

    Tools

    Claude Code

    adopt

    A terminal coding agent I use daily, for code and to run my second brain through skills and slash commands.

    I use Claude Code for more than code. It’s the librarian of my Obsidian vault: it distils daily logs, files notes, links ideas and keeps this site fed. Thoughtworks features it on its radar too, alongside other coding agents.

    What makes it work is the harness around it: a clear constitution file, small reusable skills, and git as the safety net after every run. Without those it’s just a very confident intern.

    MSW (Mock Service Worker)

    adopt

    Mock at the network boundary, not by stubbing fetch. The same handlers serve tests, Storybook and local dev.

    “Stop mocking fetch” is one of the most useful testing lessons I’ve learnt. MSW intercepts requests at the network level, so components use their real data layer and tests read like user journeys.

    The win compounds: one set of handlers powers unit tests, Storybook stories and offline local development. It’s also how I’d let a coding agent work on a UI without needing a live backend.

    updated

    Playwright

    adopt

    My default for end-to-end browser tests: auto-waiting, parallel runs, traces, three engines.

    I championed Playwright at Sky as part of a shift-left testing strategy, and it’s still my first choice. Auto-waits remove most flakiness, traces make CI failures debuggable, and parallel runs keep feedback fast. Thoughtworks moved it to Adopt back in Radar 29 and it’s only got stronger since.

    It’s also becoming the browser layer for agents. The Playwright MCP server and test agents let a coding agent drive the app it just changed.

    updated

    Vitest (with Browser Mode)

    adopt

    Fast unit and component tests that share the Vite pipeline. Browser Mode runs components in a real browser.

    I trialled Vitest when Thoughtworks first flagged it in Radar 28. One pipeline instead of separate Jest and Vite configs was reason enough to move. Since Vitest 4, Browser Mode is stable, so component tests can run in a real browser via Playwright instead of a simulated DOM.

    That matches the broader “browser-based component testing” signal. Fewer mocks of the platform, more confidence that the thing actually renders.

    updated

    Playwright test agents

    assess

    Planner, generator and healer agents that explore an app, write Playwright tests and repair broken ones.

    Playwright now ships agent definitions that plan tests from exploring the app, generate specs and heal failing ones. It’s promising, but self-healing tests worry me: a test that quietly adapts to a broken UI is worse than a red build.

    I want to assess it on a side project, keeping a human in the loop on every heal, and see whether the planner finds journeys I’d missed.

    Cypress for new projects

    hold

    Still fine where it's established, but I wouldn't start a new e2e suite on it today.

    Cypress taught a generation of frontend developers to enjoy e2e testing, me included. But for new work Playwright wins on multi-browser support, parallelism and cost in CI, and it’s where the agent tooling is heading.

    Hold doesn’t mean rip it out. If a suite is healthy, migrate incrementally (progressive overload, not a max-out attempt) and only when there’s a real reason.

    updated

    Languages & frameworks

    Astro

    adopt

    Content-first, static by default, islands only where interactivity earns its place. This garden runs on it.

    The Qwik → Astro migration of this site is done: the old blog posts still live at their original URLs, and everything new is a typed content collection — notes, radar, ideas, skill trees. For a garden that’s mostly reading, shipping zero JavaScript by default is the right starting point, and islands let the graph, search and skill tree hydrate on their own.

    Qwik’s resumability is clever, but here the content layer mattered more than the runtime. Astro’s upgrades have been smooth so far, which is exactly what I want from the boring layer of a side project.

    Zustand

    adopt

    Small, unopinionated React state with selectors and transient updates. Ideal for high-frequency UIs.

    Zustand is the state library I recommend most. It’s tiny, hooks-first, and its subscribe API lets components react to fast-changing data without re-rendering on every update. That’s the key to keeping live dashboards smooth.

    Pair it with a server-state library for request caching and keep the store for genuinely client-owned, real-time state.

    Rust + WebAssembly for UI hot paths

    assess

    Move heavy, CPU-bound work such as aggregation, layout and parsing into Wasm, but only after profiling proves JS is the bottleneck.

    I’ve been learning Rust on and off for years, and Wasm is the obvious bridge to my frontend work: number-crunching order books, graph layouts or binary feeds off the main thread.

    The ecosystem is shifting. The old rustwasm org was sunset in 2025 and wasm-bindgen moved to new maintainers. And the JS-to-Wasm boundary has a cost of its own. Assess with a benchmark, not with enthusiasm.