james l@ngridge

👋 Hi.

I'm James, a product-minded software engineer with an unorthodox, self-taught background and over 5 years of experience, working at Orbit Software Laboratories since August 2023.

I've worked for established, multinational enterprises, and small, agile startups, in fully product-driven roles as well as client-focused roles, including:

I also run a lot, juggle (knives, fire, whatever), and skateboard.

You can browse some notable projects I've worked on below, view or download my full CV, and see all my open source work on GitHub.

Currently I am focused on building a full-stack project from scratch for a global leader in the energy business, whose technology generates approximately 1/6th of global electricity.

gis

  • C#
  • .NET
  • Angular 17
  • TypeScript
  • Windows Presentation Foundation (WPF)
  • Azure DevOps
  • SonarQube
  • Figma
Sep 2024 – Present

I designed, built, and continue to maintain and develop new features for this product myself as a side project, since March 2023. The product idea came about because a personal trainer was using a variety of different apps to run her business, and wanted to consolidate them into one with only the features she wants. I continue to work closely with this user to understand her real problems and needs, and provide value by figuring out solutions and developing features that she wants and benefits from. As this is a side project, I have to be ruthless about working only on stuff that the user cares about.

To help me ship faster, I use GitHub Actions to automate CI code quality checks, backups, and production database migrations. When I merge a pull request into the main branch, it is automatically deployed on Vercel, where I have analytics and logging. And I use Sentry for error monitoring so I can fix things when they break.

ptp

Features

The core feature of the app are shared calendars between the personal trainer and each trainee. The trainer is able to view every trainee's calendar, and create, update, and delete appointments, workouts, and bootcamps. Trainees can only view their own calendar, including detailed information for workouts and appointments, check off workouts as completed, and check off bootcamps to confirm attendance if they have enough credits.

Other features include a mobile-friendly view for the trainees, tables where the trainer can view billing data and email invoices with a single click, and custom forms managed in Contentful CMS that get emailed to the trainer when a trainee fills the form out.

ptp

Iterations

With the first prototype, I experimented with Next.js 13 and the brand new App Router which was still quite unstable at the time. I decided to use RTK Query for data fetching and caching, and Redux Toolkit for state management. In hindsight, I over-engineered the state management by using Redux, as the app does not need complex client-side state. I recently did a big refactor to remove Redux, and experiment with moving everything to the server, removing the need for global client state, and relying on Next.js caching features.

However, I wasn't happy with the performance and user experience of dynamic route segments rendered at request time. And the scale and dynamic nature of the app means that it isn't feasible to prerender the routes at build time. So I refactored again to a mix of server and client-side rendering, but without global client-side state, and instead using React Query to manage server state.

Data is prefetched in Server Components and cached, so it is immediately available on the client. Data for adjacent calendar months is then prefetched on the client and cached, so they load immediately when the user navigates to them in a Client Component. React Query is also used to invalidate cache when users update server state, and to synchronize other users' clients with the updated server state. This makes the user experience very snappy.

Personal Trainer Planner
  • Next.js 15 (App Router)
  • React 19
  • React Query
  • Tailwind CSS
  • shadcn/ui
  • PostgreSQL
  • Prisma
  • Contentful CMS
  • GitHub Actions
  • Vercel
Mar 2023 – Present

I led the implementation of an interactive dashboard for a climate non-profit, which visualises historic and predicted COâ‚‚ emissions across six emissions categories, and the complex effect that different government policy measures have on the data.

Time series chart

Data

The data is maintained in CSV files. I implemented a page in the CMS where the data could be imported. The CSV files get parsed on the client and stored as JSON. It was important to validate the data while parsing to ensure buggy data did not get imported.

Data is typically structured for D3.js charts as an array of objects. However, complex logic to re-calculate the data is triggered whenever the user selects an option, and the code is simpler and more efficient when working on an object of arrays. The data does require extra transformation to work with D3.js, but this is less overhead than the calculation complexity. And while the calculation logic may get updated or expanded in the future, the logic to render the chart probably won't, so it's more maintainable.

MappingZero system architecture

State Management

The Next.js app fetches the initial data from the CMS in a React Server Component, and passes it to the dashboard client component, where it is stored in a Context provider. Context is not the typical choice for state management, since every component consuming the Context re-renders when the Context value changes. You might go for state management libraries like Redux, Zustand, or Jotai instead.

However, that is exactly what we wanted in this case. The Context holds the emission data, and whenever the user makes a selection which updates the data, we need to re-render all the charts on the page with the updated data. So Context was the simpler choice than adding another library, and also avoided prop drilling. Again, more maintainable.

Search sequence diagram

Performance

Users can search for government measures, and filter measures by industry and emissions category. The data is large and growing, so we only fetch ten policy measures on the client when the page first loads, and cache them. Measures are then fetched and cached when the user clicks load more or searches or filters the measures, ensuring a smoother experience for the user.

Search sequence diagram
MappingZero
  • Next.js 14 (App Router)
  • React 18 (Server Components)
  • TypeScript
  • Node.js
  • D3.js
  • Chakra UI
  • Orama search
  • Payload CMS
  • Figma
Mar – Sep 2024

This is a proprietary product I have worked on at Orbit Labs in a team of four developers. It's a storage, management, and exchange platform for digital asset and media data, targeted at corporations, agencies, and individual professionals. We plan the product development and roadmap together. This is still a work in progress but I am currently focused on another project.

XR

My notable individual contributions so far have included:

  • Reduced client-side asset loading times significantly through persistent server-side caching of uploaded assets in commonly-used dimensions with Rust, improving app performance and UX.
  • Implemented a full-stack feature to share assets with existing and new users.
  • Optimised the UI and UX for mobile devices.
  • Set up the authorisation service deployment on Digital Ocean with Docker and Terraform.
XR
  • Rust
  • TypeScript
  • Node.js
  • React 18
  • React Query
  • Tailwind CSS
  • Express.js
  • PostgreSQL
  • Docker
  • Digital Ocean
  • Terraform
Feb – Oct 2024

This is a proprietary product I have worked on at Orbit Labs in a team of three developers. It's a minimalist URL status monitoring service designed for small businesses and solo professionals.

We plan the product development and roadmap together, making early stage product and design decisions. My notable individual contributions so far have included:

  • Implemented the first automated tests and code quality pipelines.
  • Migrated the entire app from Next.js v13 Pages Router to v14 with App Router.
  • Implemented a feature for users to join organisations and share monitors.
  • Refactored the core Node.js cron job to a separate Docker container, using Terraform.

This is still a work in progress but I am currently focused on another project.

gz-1
Inga&Friends
  • TypeScript
  • Next.js 14 (App Router)
  • React 18
  • Node.js
  • React Query
  • Chakra UI
  • GitLab CI/CD
  • MongoDB
  • Firebase
  • Docker
  • Digital Ocean
  • Terraform
Aug 2023 – May 2024

I worked full stack in a cross-functional scrum team on the e-commerce site for this 1-billion-euro furniture retail business with over 130 shops in Germany. On the front end, I developed and maintained a scalable, reusable React component library. On the back end, I was on the core team tasked with migrating a legacy monolith Java back end to an event-driven microservices architecture.

gz-1
Porta
  • TypeScript
  • Next.js
  • Styled-JSX
  • Node.js
  • Contentful CMS
  • Commercetools
  • Google Cloud Platform
  • Microservices
  • Pub / Sub
  • Figma
  • Jira
Nov 2021 – Feb 2023

This was my first developer job. I developed a scalable white-label job and real estate portal, serving over 80,000 listings across 90 portals to over 3 million visitors per month.

gz-1
Classmarkets
  • PHP
  • Symfony
  • JavaScript
  • jQuery
  • Bootstrap
  • React
Sep 2019 – Sep 2021