rapid agenticdevelopment
← Sales Scribe

CASE STUDY / SALES SCRIBE

Working in six weeks.
Trusted with the books
in five months of work.

Sales Scribe is an invoicing and double-entry accounting application for UK small businesses. I built it on my own, part-time, with AI doing most of the typing and me doing all of the deciding. This is the honest account: the constraints, the calls I made, the things the AI got wrong, and what I’d do differently next time.

What this means for your business.

Sales Scribe handles multi-currency transactions with automatic exchange-gain and loss accounting, and tracks Stripe payments through fees, payouts, refunds and chargebacks. Accountancy practices can manage access for each member of staff and each client, with mandatory two-step verification and an audit trail of their actions. A working version took six weeks; further review and testing brought it to public staging and structured user testing. It remains pre-launch, with a rehearsed backup restore required before paying customers use it.

For your project, the same approach starts with one agreed workflow and a fixed price. I review the code, test real business scenarios and database behaviour, and require evidence that fixes work. Security checks and a proven recovery path are part of release readiness, with source code and documentation handed over so another developer can maintain what you own.

6weeks to a working versionfirst commit to a posting ledger
22weeks of actual workpart-time, about 25 hours a week
2,100+automated testsrun against a real database
2adversarial code reviews1 Critical, 12+ High findings — all fixed

01 / THE BRIEF

A real product, not a demo.

The aim was never a prototype. It was software a UK business could actually run on: issue invoices, take payments, keep books that an accountant would sign off, and stay on the right side of VAT and the Companies Act. That meant a proper double-entry general ledger underneath everything, not an invoicing tool with a “reports” tab bolted on.

I chose it as the first product because it is unforgiving. Money has to balance to the penny, historical documents must never silently change, and one customer must never see another’s data. If the way I work with AI holds up here, it holds up for most business software.

Three numbers, so nobody feels spun

Six weeks from the first commit on 17 November 2025 to a working system: invoices, credit notes, purchase documents, payments, PDFs, email and a posting double-entry ledger, all committed by 26 December. The dates are in the timeline below and in the repository. That is the “rapid” part, and it is what a client’s first version looks like—except a client’s first version is one workflow, not an accounting suite.

Twenty-two weeks of actual work, part-time—about five months. Everything after week six was about making the software trustworthy enough to hold other people’s financial records: review, hardening, testing, compliance. That is the part most quick builds skip, and the part I won’t.

Ten months on the calendar, because for nearly five of them—mid February to early July—Sales Scribe waited while I built two other products. One person, 25 hours a week: things queue. I’d rather say so than pretend otherwise—and be clear about what it means for a client: I run one client build at a time, and client work takes priority over my own products. That is why those queued, and yours won’t.

The constraints

02 / THE TIMELINE

From empty repository to online payments.

  • First commit.

    Clean-architecture solution: Domain, Application, Infrastructure, Web. Organisations, customers, suppliers, products.

  • Weeks three to six: a working system.

    Invoice create, edit and list by 3 December. Payments with general-ledger posting and an audit pipeline by 7 December. VAT posted at issue on the 8th. PDF invoices and statements on the 13th. Invoice emails with the PDF attached and a payment link on the 20th. Credit notes, delivery notes and purchase orders with their own PDFs and emails on the 21st and 22nd; goods-received notes on the 26th. Working, and a long way from trustworthy.

  • Two decisions that shaped everything after.

    Issuing an invoice became a single atomic transaction, and the parallel “account entries” sub-ledger was deleted so the double-entry ledger is the only source of truth.

  • Payments, FX, billing, reports.

    Payment allocation rebuilt, exchange-rate handling on documents, edit-locking rules between linked documents, billing accounts with Stripe subscriptions, purchase credit notes, the aged-receivables report. 112 commits, then the last feature commit on 12 February.

  • Sales Scribe waits.

    Four maintenance commits in twenty weeks—vulnerable-package upgrades, reCAPTCHA. The hours went to two other products, XBRL2JSON and UK Company Intelligence. That is the honest cost of one person and a 25-hour week: things queue.

  • First full adversarial review.

    A read-only audit across security, accounting correctness, FX, robustness, performance and testing. Verdict: strong accounting core, but one Critical and a run of High findings. Details below.

  • Remediation: 124 commits in eleven days.

    Global, fail-closed tenant isolation with a write-side guard; currency minor units end to end; the money-integrity fixes; 700+ new tests.

  • Second review, re-verifying the first.

    Tasked explicitly not to trust the completion reports. It found several “complete” controls that didn’t exist as code. The standing lesson from this: trust tests, not “COMPLETE” labels.

  • Hardening.

    Role-based permissions on every action, strict Content Security Policy with no inline scripts (243 views migrated), personal data kept out of logs, structured monitoring.

  • .NET 10, accountancy practices, the data lifecycle.

    Upgraded to .NET 10 LTS with 2,144 tests green. Deployed to a public testing server. Built the accountant-delegation model, then GDPR export, closure, erasure and six-year retention. Published a Security & Trust Centre where every claim is tied to evidence in the code.

  • Strategy reset.

    Competitive gap analysis against the incumbents. Decision: win a wedge — UK small VAT-registered businesses, the complete money-in-to-VAT-filed loop — rather than chase feature parity.

  • “Pay now.”

    Online card payment on invoices through Stripe Connect, landing on the ledger exactly once, with fees booked, payouts reconciled, refunds and disputes handled. Eleven increments in four days.

  • Accounts Preperation.

    The goal: an accountant can prepare annual accounts from the data, without rebuilding the numbers elsewhere.

03 / THE DECISIONS

Five calls that mattered.

01

The ledger is the only source of truth.

Early on there were two records of what a customer owed: the general ledger, and a separate table of “account entries” for statements and balances. Two records will drift. In December 2025 I deleted the second one and derived statements and balances from ledger lines instead, adding the customer and supplier references the ledger needed to make that work.

Why it matters: when a customer asks why their statement doesn’t match their invoices, there is exactly one place to look, and it is the same place the accountant looks.

02

Issuing is atomic; issued documents are immutable.

Found during testing: an invoice could be marked “Issued” even when its ledger posting failed, leaving an invoice with no accounting behind it. The fix was to wrap issuing in one database transaction — either the status changes and the postings exist, or nothing changes. From then on, issued documents are frozen; corrections happen by reversal, and the PDF as issued is archived at that moment.

Why it matters: this is the property an auditor cares about most, and the review in July confirmed it held: no normal path can silently alter a historical document or posted ledger.

03

Tenant isolation moved from “remember to check” to “cannot forget”.

For the first eight months, most queries carried a hand-written “where organisation = this one” clause. The July review found three services where that had been missed — a cross-tenant hole. Rather than patch the three, I rebuilt isolation as a global query filter that fails closed (no organisation context means zero rows, not all rows) plus an interceptor that validates every write before it reaches the database. Done in small waves, each with its own tests against a real MariaDB.

Why it matters: a security property that depends on a developer remembering something will eventually be forgotten. The second review, looking hard for one, found no exploitable cross-tenant hole.

04

Ship the safe version first, prove the better one later.

Un-allocating a supplier payment that had already posted to the ledger left the books drifting. Two options: block it (small, safe, symmetric with the customer side) or build a full reversing ledger event (better, but only provable against a real database, which that session couldn’t run). I shipped the block the same day and the full reversal two days later, once seven integration tests on a real MariaDB proved it nets to zero.

Why it matters: the order was chosen by what could be proven at each point, not by what was most impressive.

05

Backups before customers. A wedge before breadth.

The August competitive analysis was clear: the incumbents have a decade of accreted features and I cannot out-list them. So the plan targets one country, one customer type, and one complete loop — sign-up to issued invoice to online payment to bank reconciliation to a VAT return filed with HMRC — done better than they do it. And the first item on that plan is not a feature: it is nightly encrypted backups with a scheduled restore rehearsal that asserts a known invoice’s PDF byte-matches. A backup never restored is a hope, not a backup.

Why it matters: you cannot hold someone’s financial records without a proven recovery path. It goes first and is never skipped.

04 / WHERE THE AI WAS WRONG

What the reviews caught, and who caught it.

This is the section a cautious buyer should read twice. AI writes fast and confidently; that is the point, and also the risk. The value I add is the review. Here is what it found.

The Critical finding.

The invoice edit path trusted a client-posted “amount paid” field with no server-side validation. A draft could be issued showing money received that had never been received, breaking agreement between the ledger and the customer’s account. Found by the July review; fixed by making the settlement engine the sole owner of paid and balance figures and recomputing at issue.

Secrets and access.

Live API keys had been written into a configuration file and committed. A diagnostics endpoint, protected only by “must be logged in”, would let any user of any organisation read the mail configuration and overwrite a process environment variable. Both were the kind of thing an AI produces when told to “make it work” and nobody asks the security question. Rotated, removed, and a source-scan guard added so it can’t recur.

“Complete” that wasn’t.

The second review was told not to trust the wave reports. It found that a promised set of architecture guard tests had never been written; that a tax-calculation test suite exercised a re-implemented private copy of the logic, not the production code; that the database schema had drifted from the model so two unique indexes existed only in memory; and that the new two-factor authentication feature was silently non-functional because a provider was never registered. Each was documented as done. None was.

The remediation was applied surface-by-surface, not invariant-by-invariant.

— Second code review, 16 July 2026. The single most useful sentence anyone wrote about this project.

The small ones that would have shipped.

  • False-green builds. The front-end bundler had failed with three syntax errors; the AI’s output filter matched nothing and it reported “bundles built” several times while the app served stale JavaScript. Caught only because I checked the actual page in a browser.
  • A wrong first diagnosis. A payments bug was “fixed” for individual customers, but my test customer was a company. Correcting that one fact led to the actual cause — a third code path nobody had mentioned.
  • Four attempts at one bug. Optimistic concurrency on customers and suppliers silently let one user overwrite another. Three plausible fixes failed. The root cause was a single missing configuration line; the fix that worked was the simplest of the four. The documents are titled, in order, “fix”, “v2 final”, “v3 final solution” and “v4 actual final”. I keep them as a reminder.
  • An assumption not checked against live data. In September a billing feature was built on the assumption that each account had exactly one payment profile. It didn’t. The lesson in my own notes: “should have checked the live row before shipping.”

And one that went the other way: a profit-and-loss report that “didn’t reconcile”. I queried the ledger directly. It reconciled exactly — draft documents don’t post, and goods for resale go to the inventory asset, not expenses. Not a bug. Knowing when not to change code is part of the job.

05 / WHAT I’D DO DIFFERENTLY

Four things, honestly.

06 / WHERE IT STANDS

Development and Testing.

Sales Scribe is deployed on a private staging site so we can demonstrate it for those interested. Structured user testing continues: scripted end-to-end scenarios covering sales, purchases, banking, multi-currency, practice delegation, statutory compliance and online payments. It has no paying customers yet, by design — the backup and rehearsed-restore gate comes first.

Development continues on new features and in the infrastructure side we're arranging a home for production hosting and backups.

The test suites stand at over 2,100 and counting, automated tests across domain, application, infrastructure and web layers, the infrastructure suite running against a real MariaDB. Every public security claim on the product’s trust page is tied to the code that entitles it. The full record — 1,069 commits, 567 design and implementation notes and a running session log — is the kind of documentation I hand over on client work.

What it does today

  • Invoices, credit notes, quotes, purchase orders, delivery notes, goods-received notes and supplier invoices, with branded PDFs archived at issue.
  • Customer and supplier payments, allocation across documents, multi-currency with ECB rates and functional-currency posting.
  • A double-entry general ledger with profit and loss, trial balance, aged receivables and payables, VAT summary, customer statements.
  • Online card payment on invoices through Stripe Connect, with fees, payouts, refunds and disputes reconciled to the ledger.
  • Accountancy-practice delegation: consent-based, scoped, revocable, attributed, with mandatory two-factor authentication for practice staff.
  • Role-based permissions on every action, field-level encryption of personal data, append-only audit trails, GDPR export, erasure and six-year retention.

Work currently in progress

  • A trial balance report with opening, movement and closing balances.
  • Control-account reconciliation, checking the ledger against the invoices and bills behind it.
  • Prior-year comparatives on the profit and loss, balance sheet and trial balance.
  • Opening-balance conversion for businesses moving from another system, including outstanding invoices and bills.
  • Import of an opening trial balance and outstanding documents from a spreadsheet.
  • Foreign-currency opening balances.
  • Amendments to a posted opening position, with a full audit record.
  • Automatic locking of the period before conversion.
  • Year end confirmed from the Companies House register.
  • Accounting periods of up to 18 months when changing a year end.
  • Credit notes applied to invoices recorded in the ledger, with realised exchange gains and losses.
  • Stripe refunds and disputes that fall in a closed period held for review rather than posted.

The stack

ASP.NET Core MVC on .NET 10 LTS · Entity Framework Core 10 · MariaDB · ASP.NET Identity with TOTP two-factor · Stripe (Checkout, Connect, webhooks) · QuestPDF · MJML email templates · Serilog and Seq · xUnit with Testcontainers · Vitest · webpack · ClamAV upload scanning · Dependabot.

Visit Sales Scribe

THE SAME PROCESS, FOR YOUR SOFTWARE

This is how I’d
build yours.

Working first version fast. Real review before anyone relies on it. Decisions written down so you and any future developer can see why. And a clear line between what the AI drafts and what I stand behind.

If you have a problem worth solving, start with what you know. I’ll help with the rest.

Tell me your idea

Watch us on YouTube

Follow us on LinkedIn

Rapid Agentic Development Ltd · Registered in England and Wales · Company number 17458231

Registered office: 181 Coniscliffe Road, Darlington, England, DL3 8DE. · Privacy notice

ICO registration reference: ZC248420