SaaS

Reconciliation service for a consumer lending platform

Built by Rafael Bautista

Built the service that matches disbursements and repayments against partner settlement files, and flags what does not line up.

  • Builder email verified

    Verification

Type
Professional
Category
SaaS
Published
Jul 25, 2026
Updated
Jul 25, 2026
Built withGoPostgreSQLDockerTerraformAWS
TopicsFintechAnalytics

About this project

The problem

The lender moved money through four partners, and each sent a settlement file on its own schedule in its own layout. Finance reconciled these in spreadsheets, which took two people most of a week each month and still left a tail of unexplained differences that just carried forward. Beyond the labour, the real risk was that a genuine failure — money that left but never arrived — looked identical to a formatting difference until somebody manually chased it. They needed the matching automated and, more importantly, they needed the exceptions surfaced quickly instead of buried.

My role

Senior backend engineer, effectively the only engineer on this service for the first four months. I worked directly with the finance lead rather than through a product manager, which was faster and meant I had to learn more accounting than I expected.

What I owned

I owned the whole service end to end: file intake, the matching engine, the exception model, and the API the internal tool reads from. The matching rules were the substance — exact match first, then amount-and-date within tolerance, then a fuzzy pass on reference strings, with every match recording which rule caught it and why. I built the exception workbench endpoints so finance could accept, split, or annotate a difference without an engineer. I also wrote the daily digest that goes out at 7am with the previous day's unmatched total.

Technical & product decisions

I made the matching rules data rather than code — each rule is a row with its own tolerances and ordering, editable by finance through an internal screen. The alternative was a rules file we deploy, which is simpler to build and would have made me the bottleneck for every tolerance tweak. I also chose to store every match attempt, not just the winning one; it roughly tripled the table size and it is the only reason anyone can answer 'why did this match'. On storage I stayed with PostgreSQL and used partitioned tables by month rather than reaching for anything columnar, because the query pattern is almost always one month at a time. Go over Node here was mostly about the file parsing throughput and the fact that the platform team already ran Go services.

Constraints

Everything had to be auditable — the regulator's expectation was that any figure in a report could be traced back to a source line, so nothing could be recomputed on the fly and discarded. Partner files could not be requested on demand; we got what they sent, when they sent it, and one partner's file arrived twice on some days with overlapping rows. I also had no access to production data during development, so all my test fixtures came from anonymised samples the finance lead built by hand.

Result & impact

The monthly close went from about eight person-days to under one, and the unexplained tail — which had been growing quietly — was cleared and then held near zero. In the first quarter after launch the fuzzy pass surfaced two genuine failed disbursements within a day instead of at month end, which is the outcome finance cared about most. Match rate on the automatic rules settled around ninety-four percent, with the rest going to the workbench, and that number is on the digest so nobody has to ask.

Who else worked on it

The finance lead defined every rule and tolerance and reviewed the match explanations line by line for the first month. A frontend engineer built the exception workbench against my API in the last six weeks. Our security engineer reviewed the file intake path, which is where she found that I was logging full account references.