Record

The record

32 internal bank systems between June 2018 and September 2026. 9 of them started from an empty folder. 5 were written near enough alone, 16 with him as the principal author, and 11 as a contributor. Figures are commit shares as of 2026-09-06; where a system's origin cannot be established from its history, it is marked unknown rather than claimed.

Timeline · 32 systems

  1. The payment solutions support platform June 2018 to February 2025 · principal author
  2. The securities-based lending platform for advisors August 2018 to April 2026 · contributor
  3. The organisation's Angular application template November 2018 to June 2026 · sole author
  4. The internal IT ticketing and asset register December 2018 to June 2026 · principal author
  5. The enterprise transactional email platform December 2018 to August 2026 · principal author
  6. The refund system for accounts left in credit June 2019 to September 2026 · contributor
  7. The submission and approval service behind new accounts March 2020 to August 2026 · principal author
  8. The automated integration that opens accounts on the core platform March 2020 to August 2026 · principal author
  9. The account fulfilment and compliance queue jobs March 2020 to June 2026 · contributor
  10. The customer money-movement service estate July 2020 to January 2023 · contributor
  11. The insurance-backed line of credit tracker July 2020 to June 2026 · principal author
  12. The system behind a card reported stolen or sent back undelivered January 2021 to June 2026 · contributor
  13. The overnight sweep, overdraft and settlement processor February 2021 to June 2026 · principal author
  14. The shared internal package estate February 2021 to June 2026 · contributor
  15. The check ordering and check-fraud detection integration February 2021 to June 2026 · principal author
  16. The wire-handling and transfer-communication jobs May 2021 to June 2026 · contributor
  17. The stored-document service May 2021 to June 2026 · contributor
  18. The fraud alert triage platform July 2021 to June 2026 · principal author
  19. The instant-payment rail and its settlement process May 2022 to July 2022 · contributor
  20. The admin tool over internal program settings and their audit history September 2023 to May 2026 · sole author
  21. The general-ledger upload system for deposit accounting January 2024 to July 2026 · sole author
  22. The bulk source-repository mirroring utility April 2024 to May 2026 · sole author
  23. The organisation's Blazor application template November 2024 to June 2026 · principal author
  24. The account services desk for bank operations staff December 2024 to June 2026 · principal author
  25. The typed core-banking bridge APIs May 2025 to September 2026 · contributor
  26. The internal real-time messaging bus July 2025 to June 2026 · sole author
  27. The partner funding exchange job September 2025 to June 2026 · principal author
  28. The log-reading tool, still a scaffold November 2025 to June 2026 · principal author
  29. The ACH and government payment processing suite November 2025 to September 2026 · principal author
  30. The tool that hands out the next available account number June 2026 to August 2026 · contributor
  31. The downstream balance and transaction file generator June 2026 to August 2026 · principal author
  32. The nightly batch that applies change requests to the core June 2026 to August 2026 · principal author

Recurring decisions · 7

Endpoints over controllers

Every HTTP endpoint is its own class carrying its own request and response types, rather than a controller holding a dozen loosely related actions.

In his words

Controllers bloat. Finding one action means scanning hundreds of lines or hunting for a method, on top of the decorators a standard controller carries. FastEndpoints does not merely permit one endpoint per file, it expects it, and it keeps that file minimal. The hard work belongs in the service.

Trade-off

More files. A five-operation feature is five endpoint classes plus its contracts, where a controller would have been one file.

Evidence

  • The general-ledger upload system for deposit accounting
  • The admin tool over internal program settings and their audit history
  • The account services desk for bank operations staff

Never throw out of a service

Every service returns a result envelope. Failure is a value the caller has to handle, and only the endpoint layer turns that value into an HTTP error response. The rule is written down inside the repositories that follow it.

In his words

Exception flow never feels like it carries enough substance. Did it really fail, or was the exception planned? Did it come from the direct parent or bubble up from further down? Attaching a message to it is not an option. A result envelope behaves the way he wants code to behave and leaves far more room to move.

Trade-off

Every call site has to unwrap. Where a failure genuinely is exceptional, a result envelope is more ceremony than a throw would have been.

Evidence

  • The account services desk for bank operations staff
  • The enterprise transactional email platform

The browser never touches the API

A server-side hop sits between the browser and the API. The browser talks to that hop, the hop attaches the caller's identity and forwards. In one system the browser cannot reach the API at all.

In his words

A security environment requirement, not a preference. Individual users are not to reach an API at all, and that needed a hard cut: only service accounts may call one. The hop in front is what guarantees no human ever gets there directly.

Trade-off

An extra deployable, an extra hop of latency, and every endpoint exists twice: once on the API and once as the pass-through in front of it.

Evidence

  • The admin tool over internal program settings and their audit history
  • The general-ledger upload system for deposit accounting
  • The account services desk for bank operations staff

Authorization decomposed to the action

Permissions are granted per action, not per record. Approving, back-dating, auto-reversing, voiding, changing a settlement date and editing the field that grants access are each their own permission, separate from ordinary edit.

In his words

The standard set is read, edit and admin. Anything past those came either from planning the application up front or from discovering over time that a finer permission was needed. Both routes produced entries in the list.

Trade-off

More roles to administer, and a permission model somebody has to keep a map of.

Evidence

  • The general-ledger upload system for deposit accounting
  • The overnight sweep, overdraft and settlement processor
  • The internal IT ticketing and asset register

Legacy marked, not deleted

A repository that is superseded but still standing gets a LEGACY prefix on its name. The prefix stops anyone making changes to it, and it stays until the repository is confirmed safe to remove from master. Renaming a project is easier to reason about than hunting for the commit to revert to, so the old thing is kept, labelled, as the rollback path.

In his words

The prefix exists to stop other people editing a repository that is superseded but still standing. It stays until that repository is confirmed safe to remove from master, because renaming a project is easier to reason about than hunting for the commit to revert to.

Trade-off

Dead repositories stay in the estate, visibly, and somebody has to decide when each one is finally safe to remove.

Evidence

  • The enterprise transactional email platform

Shared contracts, so legacy consumers move on their own schedule

A contract package targets both the modern runtime and the decade-old one, so callers on either share the same types. Where that is not possible the old and the new ship side by side as two packages rather than as one breaking upgrade.

In his words

It was a bear. Wrappers and converters carry the old request models alongside the new ones that gained functionality, and unit testing was what made it survivable, probably more than on any other project he has worked on.

Trade-off

Two runtimes to keep a package building against, and a lowest common denominator on anything the old target cannot express.

Evidence

  • The internal real-time messaging bus
  • The payment solutions support platform
  • The shared internal package estate

Record the correction; label an inference as an inference

A superseded decision is struck through and dated rather than overwritten, and where an implementer reasoned but nobody actually ruled, the entry is labelled an inference pending a decision, with the reasoning chain and the alternative reading both stated. In one estate the written record is checked mechanically against the code: retired terms surviving unmarked, citations of decisions that do not exist, verification claims whose covered files have moved.

Trade-off

The record gets longer and messier to read, and somebody has to keep the checks working.

Evidence

  • The ACH and government payment processing suite

Case studies · 4

The browser tab label became an alert channel.

Situation

Operations staff keep the general-ledger upload system pinned in a background tab and work in front of something else. A posting that errors needs attention now, and nobody was looking at the page.

The trap

The obvious answers are the ones that need the page to be on screen: a banner, a toast, a badge. All of them are invisible to a person whose attention is somewhere else, which is exactly the case that matters.

How it was found

An approver on that system needed to see a failed real-time post without the page in front of them. Posting time carries a stated guarantee, so a failure that sits unnoticed in a background tab is a broken promise rather than an inconvenience. (interview C1)

The fix

The page title itself flips to an error marker whenever errored real-time posts exist, so the tab label carries the alert into the strip along the top of the browser.

The cost

The non-production environments are permanently full of stale errored posts, so the indicator is almost always lit there. The developer documentation has to say outright that this is not an application failure.

The general-ledger upload system for deposit accounting

A configured zero is a real setting, and reading it as absent fails open.

Situation

The general-ledger upload system caps how far back a posting may be dated. The cap is configuration, and zero is a legitimate value for it: no back-dating allowed at all.

The trap

The natural way to check whether configuration has loaded is to ask whether anything came back. A cap of zero came back as nothing, so the client would have read a real, deliberately strict setting as not loaded yet, and let back-dated rows through.

The fix

Loaded-ness is tracked by whether the floor date is set, not by whether the count is above zero. A one-line property with a four-line comment explaining the trap.

The cost

The rule is enforced on the client and the API no longer re-checks, so the failure mode had to be deliberate rather than incidental. When the cap cannot be loaded at all, every back-dated row is blocked while today and future rows still pass.

The general-ledger upload system for deposit accounting

A red test run that is the bug list.

Situation

Fifteen-year-old validators in the account submission service hold thirty-one live defects. The rules for that work said the tests could not modify the production source.

The trap

A characterization test that asserts what the code currently does locks the bug in and calls it correct. The suite would go green and the defects would become the specification.

How it was found

Part of a push to get unit, integration and end-to-end testing onto every internal repository. Writing an accurate test meant inferring what the code was meant to do rather than recording what it does, so a failure states the gap. Fixing was explicitly not the goal; the audience is the next developer to open the repository, who has to clear the failures before doing anything else. (interview C3)

The fix

Each test asserts the intended behaviour, wrapped so it fails while the defect is alive and names the defect, the production file and line, and what the code should do instead. Fixing the source turns the test green by itself, at which point the wrapper is deleted. Each carries a trait so a build gate can exclude them on purpose rather than by accident, and a defect that escapes as an exception declares its exception type so an unrelated crash is rethrown untouched instead of being mislabelled.

The cost

A permanently red suite has to be explained to everyone who runs it, and the recorded invariant has to be the failure count rather than the pass count, because pass counts drift every time a test is added.

The submission and approval service behind new accounts

The cleanup job deleted live databases, and it surfaced as somebody else's login failure.

Situation

The end-to-end suite for the next-account-number tool provisions a throwaway database per run and sweeps up orphans left by runs that died. The sweep compares each database's creation timestamp against a cutoff.

The trap

The creation timestamp is recorded in the server's local time and the cutoff was computed in UTC, four hours ahead. Every scratch database therefore read as older than the grace period, including the ones belonging to runs still in progress. The damage did not look like a sweep bug: it destroyed a reviewer's probe database in the middle of a review and arrived as an unrelated login failure.

The fix

Compare local time to local time. The fix ships with the reasoning in a comment beside it.

The cost

The sweep had to be refactored to take its prefixes and grace period as parameters before it could be tested at all: the creation timestamp is not settable, so with a hard-coded one-hour grace no test database could ever be old enough to reach the code path. It was untestable by construction, which is why the defect lived there.

The tool that hands out the next available account number

The general-ledger upload system for deposit accounting

Purpose

Builds, checks and posts the general-ledger entries that deposit accounting depends on, from templates and batches through to real-time posts. Three legacy console applications feed it.
Originstarted from an empty folder
Commit share99% · sole author
SpanJanuary 2024 to July 2026

as of 2026-09-06

The internal IT ticketing and asset register

Purpose

The tool the IT department runs on: tickets, departments, locations, company property and employee records.
Originstarted from an empty folder
Commit share55% · principal author
SpanDecember 2018 to June 2026

as of 2026-09-06

The instant-payment rail and its settlement process

Purpose

Sends and reverses instant payments across the national instant-payment network, with settlement running as its own scheduled process.
Originstarted from an empty folder
Commit share36% · contributor
SpanMay 2022 to July 2022

as of 2026-09-06

The account services desk for bank operations staff

Purpose

What operations staff use to look up a customer or an account, change an address, handle returned mail, update a customer email, and trigger a user id or password reset. A nightly job clears the returned-mail queue behind it.
Originstarted from an empty folder
Commit share84% · principal author
SpanDecember 2024 to June 2026

as of 2026-09-06

The internal real-time messaging bus

Purpose

Infrastructure rather than an application. One system publishes a notification, another subscribes to it, and the two never have to know about each other.
Originstarted from an empty folder
Commit share93% · sole author
SpanJuly 2025 to June 2026

as of 2026-09-06

The admin tool over internal program settings and their audit history

Purpose

Read and edit the configuration values other internal systems run on, with a full audit history of every change, across three separate databases.
Originstarted from an empty folder
Commit share93% · sole author
SpanSeptember 2023 to May 2026

as of 2026-09-06

The bulk source-repository mirroring utility

Purpose

A small utility that clones the whole organisation's source repositories in one pass. Built to the same standard as the applications, with its own test project.
Originstarted from an empty folder
Commit share96% · sole author
SpanApril 2024 to May 2026

as of 2026-09-06

The wire-handling and transfer-communication jobs

Purpose

Console jobs handling outgoing wires and manual transfer communications for a sweep programme. Founded here, then carried mostly by other authors.
Originstarted from an empty folder
Commit share20% · contributor
SpanMay 2021 to June 2026

as of 2026-09-06

The log-reading tool, still a scaffold

Purpose

A repository created, set up, and documented as having no source in it yet. An honest placeholder rather than an abandoned half-application.
Originstarted from an empty folder
Commit share83% · principal author
SpanNovember 2025 to June 2026

as of 2026-09-06

The enterprise transactional email platform

Purpose

The queue every internal system hands its customer email to. Persists the message and its attachments, ships a typed client library and a shared models package, and a downstream worker sends. The largest single body of work in the estate by volume.
Originjoined an existing codebase
Commit share80% · principal author
SpanDecember 2018 to August 2026

as of 2026-09-06

The ACH and government payment processing suite

Purpose

Seven console applications, an API and a web application handling ACH returns, notifications of change, and the acknowledgement files the Federal Reserve sends back. Plus a new data model and loader migrating originator setup data off a workbook.
Originjoined an existing codebase
Commit share45% · principal author
SpanNovember 2025 to September 2026

as of 2026-09-06

The payment solutions support platform

Purpose

The support platform behind the payment solutions business: an API, a web front end and a console, with a typed client library shipped alongside the API.
Originjoined an existing codebase
Commit share59% · principal author
SpanJune 2018 to February 2025

as of 2026-09-06

The securities-based lending platform for advisors

Purpose

An advisor-facing lending platform integrating a third-party collateral valuation and credit policy engine. A large multi-team codebase.
Originjoined an existing codebase
Commit share9% · contributor
SpanAugust 2018 to April 2026

as of 2026-09-06

The overnight sweep, overdraft and settlement processor

Purpose

Moves money nightly between internal accounts and an external partner: monthly minimum and maximum sweeps, overdraft processing, position maintenance, settlement and recurring transfers, exchanged as flat files.
Originjoined an existing codebase
Commit share69% · principal author
SpanFebruary 2021 to June 2026

as of 2026-09-06

The fraud alert triage platform

Purpose

Fraud analysts upload alert files, work them through follow-up, review and quality-control queues, attach documentation, and push qualifying customers into the restricted customer database.
Originorigin unknown
Commit share65% · principal author
SpanJuly 2021 to June 2026

as of 2026-09-06

The customer money-movement service estate

Purpose

Twenty-six projects: four service-contract packages, their implementations and hosts, a processor console, a tier-limit console, an email processor and three websites.
Originjoined an existing codebase
Commit share19% · contributor
SpanJuly 2020 to January 2023

as of 2026-09-06

The insurance-backed line of credit tracker

Purpose

Tracks loans and the insurance policies pledged against them, reading supporting balance and commitment information from a reporting database.
Originorigin unknown
Commit share65% · principal author
SpanJuly 2020 to June 2026

as of 2026-09-06

The tool that hands out the next available account number

Purpose

Enter an affinity number and an account type, get the next free account number back. Delivered as a full three-tier system rather than a script.
Originorigin unknown
Commit share76% · contributor
SpanJune 2026 to August 2026

as of 2026-09-06

The organisation's Angular application template

Purpose

The project template most of the bank's Angular web applications were copied from, with authentication, environment routing and the wrapper-API pattern already wired.
Originorigin unknown
Commit share94% · sole author
SpanNovember 2018 to June 2026

as of 2026-09-06

The submission and approval service behind new accounts

Purpose

The back-end service covering submit, approve, decline, resubmit and escalate across roughly fourteen application types, along with applicants, beneficiaries, signers, identity results, risk detail, queues and audit history. Mostly 2011-era code.
Originorigin unknown
Commit share73% · principal author
SpanMarch 2020 to August 2026

as of 2026-09-06

The shared internal package estate

Purpose

Not one product. The bundle of every internal library and small console the bank publishes to its own package feed, spanning frameworks a decade apart in one solution.
Originorigin unknown
Commit share26% · contributor
SpanFebruary 2021 to June 2026

as of 2026-09-06

The refund system for accounts left in credit

Purpose

Console, data, models, API and website handling refunds where an account is left holding a credit balance.
Originorigin unknown
Commit share8% · contributor
SpanJune 2019 to September 2026

as of 2026-09-06

The automated integration that opens accounts on the core platform

Purpose

A service and console that open accounts automatically against the core banking platform.
Originorigin unknown
Commit share56% · principal author
SpanMarch 2020 to August 2026

as of 2026-09-06

The downstream balance and transaction file generator

Purpose

A console that writes account balance and transaction files, in both delimited and fixed-width layouts, for systems downstream. The layout is configuration, not code.
Originorigin unknown
Commit share53% · principal author
SpanJune 2026 to August 2026

as of 2026-09-06

The account fulfilment and compliance queue jobs

Purpose

Consoles covering account fulfilment, customer risk assessment, compliance-queue email and new-account funding.
Originorigin unknown
Commit share22% · contributor
SpanMarch 2020 to June 2026

as of 2026-09-06

The system behind a card reported stolen or sent back undelivered

Purpose

Contracts, module, API and website covering cards reported compromised and cards returned undelivered.
Originorigin unknown
Commit share14% · contributor
SpanJanuary 2021 to June 2026

as of 2026-09-06

The stored-document service

Purpose

The service other systems file documents through. Its internal types are sealed off so every caller has to go through the published interfaces.
Originorigin unknown
Commit share19% · contributor
SpanMay 2021 to June 2026

as of 2026-09-06

The partner funding exchange job

Purpose

A console job exchanging funding data with a partner.
Originorigin unknown
Commit share79% · principal author
SpanSeptember 2025 to June 2026

as of 2026-09-06

The nightly batch that applies change requests to the core

Purpose

A scheduled batch applying account, customer, card, address, email and keyword change requests to the core banking platform over two service endpoints, marking each row with the core's own response.
Originorigin unknown
Commit share54% · principal author
SpanJune 2026 to August 2026

as of 2026-09-06

The check ordering and check-fraud detection integration

Purpose

The integration behind ordering customer checks and the fraud detection that goes with it.
Originorigin unknown
Commit share57% · principal author
SpanFebruary 2021 to June 2026

as of 2026-09-06

The typed core-banking bridge APIs

Purpose

The typed APIs that sit between internal applications and the core banking platform, consumed by several of the systems above.
Originorigin unknown
Commit share4% · contributor
SpanMay 2025 to September 2026

as of 2026-09-06

The organisation's Blazor application template

Purpose

The standard project template the bank's newer Blazor web applications are copied from.
Originorigin unknown
Commit share52% · principal author
SpanNovember 2024 to June 2026

as of 2026-09-06