Andrew Kirwin

Stewardship of systems

Capsule

Staff Software Engineer

— present

I re-built Capsule CRM as a Single Page Application, contributing to the open source community along the way.

This happened in parallel with development on the existing product, and so, for a time there were two versions of Capsule running. The technical challenge was to ensure the customer couldn't tell. And to reduce the boot time of the SPA as much as possible as the user may transition between the two.

Read More
  1. The year of AI

    Contributed to the frontend implementation of multiple new features leveraging LLMs: AI Summaries, AI Email assist, AI Meeting prep and AI pipeline generator.

    Lower costs

    Replaced an expensive backend service with client-side SVG generation. With a pseudo-random colour palette, giving colourful, stable avatars instantly with zero network cost.

    Pins

    Built a feature letting users surface key information by pinning activities across multiple entity types, with carefully wired bidirectional relationships to keep the data graph consistent & giving instant reactive UI updates

    Image of the Pins user interface
  2. Calendar features

    Refactored the calendar to support multi-day events, working out the layout logic to keep a spanning event in a consistent row across all its days, sized correctly across grid cells, and split cleanly where it runs into the next week.

    Ember moves to Vite

    Converted 13 internal Ember addons to the new v2 (Vite) format

  3. Discussions

    I built a new Discussion UX, with comments, @mentions, notification center and a new floating formatting toolbar utilising my past experience with ProseMirror. I also added bi-directional support to the infinite scroll library.

    Image of the Discussions user interface
  4. Plans & Payments

    Led the implementation of the upgrade/downgrade flow and new upsell features. Accounting for legacy plan states, user quota violations, and ensuring all features, UI components, and state is correct after a plan change.

    Embroider

    Coordinated a large migration off Broccoli builds to Embroider (Webpack), overcoming challenges around dynamic assets and component lookups by implementing statically analyzable alternatives.

  5. Internationalisation

    Started work on internationalising the codebase (~8k strings). I came up with a solution for translating strings with components mid-sentence. Put error handling, tests, linting and logging in place to improve visibility of missing dynamic strings.

    Workflow Automations

    Delivered the Workflow Automations feature: trigger-based rules for sales pipelines, with nested triggers and actions. It supported reordering with a custom drag/drop solution using the native browser API. Introduced the app's first auto-save pattern with an optimistic UI.

    Image of the Workflow Automations user interface
  6. ProseMirror

    Adopted ProseMirror after evaluating alternatives, and led the creation of a rich text email editor. I designed the document schema, and implemented the state management, plugins, keymaps, input rules, etc. Then integrated it into the Ember ecosystem and shipped the subject, body, toolbar, hyperlink editor, attachments, templates, signatures and very thorough test suite.

    Image of an Email Editor with example content
    The Write with AI feature was added a few years later.
  7. Multiple Sales Pipelines

    I built the front end to allow users to migrate from a single Sales Pipeline to multiple Sales Pipelines. It guided them to fix pre-migration issues, showed a preview of the resulting configuration, and then ran the migration as an async job with polling and progress feedback.

    Mixins

    Migrated shared route logic from mixins to services, overcoming tight coupling and inheritance constraints to enable native-class compatibility in Ember Octane.

  8. Glimmerisation

    Migrated away from Ember.Object and observers to native classes with @tracked properties, making behaviour explicit, maintainable, and predictable.

    File downloads

    Implemented a feature to intercepts file-downloads - allowing trusted files through without friction, and routing higher-risk files through a confirmation step so users can make an informed choice before proceeding.

  9. Flexible Pagination

    Designed and implemented a reusable pagination abstraction, driven by Link headers, decoupling pagination logic from both the UI and Ember Data models, enabling use across arbitrary arrays.

    Tooltips

    Developed the @zestia/ember-async-tooltips Ember addon, implementing a technique to reduce perceived latency in lazy-loaded tooltips by accounting for data-fetch time within the show-animation delay. Focused on accessibility, keyboard/focus support, and sticky behavior for reliable contextual guidance.

  10. List filtering

    I led the rebuild of a legacy JSF list-filtering feature, delivering a modern UI that supports complex query composition with multiple conditions, grouped AND/OR logic, and operators across text, numeric, date, boolean, money, and custom-field data types. I also implemented filter serialisation/deserialisation for local storage and resilient restore logic to handle stale filters.

    Tech health

    Configured parallel test execution for faster runs, replaced PhantomJS with Headless Chrome, and migrated dependencies from Bower to npm.

  11. Faux select box

    I created a highly customisable select-box component with a deliberately small API surface. It follows WAI-ARIA practices, supports full keyboard and screen reader interaction, and was designed to be easily replaceable if a native browser solution emerged.

    It's backed by a comprehensive test suite - because select boxes are deceptively complex. Keyboard interaction, focus management, ARIA state, async rendering edge cases, and cross-browser quirks all make this a high-risk UI primitive.

    Image of multiple different types of select boxes
    The select box can be a basic listbox, or a combobox, either with a trigger, an input, or both.

    Styleguide

    I introduced a styleguide built as an Ember Engine - a self-contained, mountable mini-app that could be excluded from production builds. In 2026, similar isolation is achieved using modern Embroider techniques.

    It wasn't just documentation - it was a sandbox encouraging developers to build components outside the main app. This small mindset shift fostered reusable abstractions, reinforced good architectural practices, and reduced accidental coupling.

  12. Performance dashboard

    Started capturing and sending performance metrics about route loading - First Paint to Full Render, and capturing slow components. Sending the data to a NewRelic dashboard.

    Tech health

    Upgraded Ember.js to version 2. The way user interactions changed in Ember, which involved a sweeping refactor from strings to closures. I also phased out CoffeeScript.

    Integrations

    Continued migrating the product to a SPA. Focusing on integrations with third parties like Zendesk, Sage, FreeAgent, Mailchimp, Xero, and Quickbooks etc.

  13. Smooth rollout technique

    Ported the most popular page to an Ember.js app. Users would enter the SPA when clicking to view a contact only. But would otherwise remain on the traditional MPA. Refreshing the page would load the old MPA - giving users an escape hatch if there was a problem with SPA.

    Route gating

    Improved how the Ember app was being rolled out by updating our Link component, such that it would route you within in the SPA or turf you out to the MPA version, based on a feature flag. Wrapped with tests so that unfinished pages didn't accidentally go into production.

    Tackling performance issues

    Due to component instantiation being expensive in Ember (we're talking version 1.13.2!), partials were sometimes used instead (For example when rendering long lists or tables). This was harder to maintain, but we knew they could be migrated over time as Ember performance improved.

    For users who fell into the feature flag segment, the SPA app was prefetched on the MPA login screen. So upon signing in the app would already be loaded.

Abstraktion

Web & app developer

Prototyping websites, building hybrid mobile applications, and even art installations for an impressive client list including Spotify, Universal Music, David Lloyd, NUS, Domino Records and more.

Highlights include Guilty Pledgers — a Spotify app that let attendees pledge money in order to manipulate the playlist at a party or event. This was my first use of web sockets.

Photo of an iPhone running the Guilty Pledgers app at a Party

SpareRoom

Web developer

I worked on modernising SpareRoom's 10 year old Perl codebase, introduced version control, and a test suite.

For 4 years I was the sole developer responsible for writing and managing the CSS and JavaScript for the UK's #1 Flatshare website. I'm proud to say some of the code is still in production today.

Highlights included rendering Rooms For Rent on an interactive map and creating a backoffice tool for detecting spam adverts using a scoring system.

Setfire Media

Software Engineer

I worked on large scale e-commerce websites, including AJAXifying Flight and Hotel search results for the well known holiday booking site OnTheBeach.co.uk.

Ichameleon

Web developer / designer

In my first job after graduating from university I met with clients in the charity and not for profit sector, and designed, built and supported their website. Using an in-house CMS, and technology like jQuery which was revolutionary at the time!

MobySoft

Web developer / designer

As a freelancer whilst at university, I designed and developed a real-time web-app for use on stage, that displayed graphs of data collected from SMS messages sent from the audience.

I help organisations design, build, and maintain software systems. My focus is on the user-facing experience, together with the services and data that support it.

For 20 years I've nurtured large code bases, for long periods of time. I'm drawn to this kind of work because I enjoy seeing changes reach customers and then iterating thoughtfully on what comes next.

Earlier in my career, most of this work was a low-level, intricate craft. With the advent of AI, the emphasis has shifted: I still care about naming, conventions, structure, and the right abstractions etc, but I now spend more of my energy shaping problems, setting constraints, and reviewing AI-generated changes so the systems stay coherent over time.

At the moment, my work centres on the hugely popular Capsule CRM, built using modern Ember.js, with a Scala backend. I help build this remotely from Cornwall in the UK.

You can see some of my contributions on GitHub, or connect with me on LinkedIn.