Personal account

Keeping a rent-rewards platform safe to change

I am the founding platform architect of Brick Rewards, a UK app that rewards renters for paying rent. This account covers how rent is verified from bank data, how the pipeline stays correct and cheap, and how a change reaches production.

Evidence boundaryThis is my personal account of work at Brick Rewards, where I am the founding platform architect in a small team. The figures are ones we measured in production in August and September 2026. Customer data, financial figures, internal names, and security configuration are omitted. Public links show the product exists; they do not independently verify my contribution.

Problem

Brick Rewards is a UK app that rewards renters for paying rent on time. Points come from bank transactions read through open banking, and members spend them on gift cards and local offers. That makes the platform money-adjacent from the first line of code. Bank data arrives late and out of order. One slow endpoint can be cut off when the platform scales down. A bad production change reaches real balances. As founding platform architect, I work with the team on how this system changes without breaking any of that.

Constraints

  • A wrong overdue-rent message harms a member, so a rent check must wait for bank data rather than guess.
  • Open-banking data arrives hours or days late and out of order, and a provider outage can leave it stale for days.
  • Cloud Run gives a fixed ten seconds after it signals shutdown. A version 2 service cannot raise it, so slow requests at scale-down are lost.
  • Points and gift cards are money. A write needs a preview, an operator, a reason, before-and-after values, idempotency, an audit record, and a reversal path.
  • Production infrastructure changes only through Terraform in reviewed CI, from staging to test to production. Cloud command-line tools stay read-only.
  • Customer data, financial figures, internal service names, and security configuration stay private.

Architecture

Rent verification from open-banking data

  1. Moneyhub sync jobAn hourly Cloud Run job under a heartbeat lock pulls member transactions.
  2. Parallel
    Bounded BigQuery mergeMerge on transaction id, bounded on the partition date so only recent partitions are scanned.
    Replay topicMerged rows go to Pub/Sub for the points earn worker.
  3. Readiness gateIs bank data synced through the date this check needs? If not, defer and record why.
  4. Candidate window and graceSearch seven days before to fourteen days after the expected date; grace depends on payment frequency.
  5. Verified rent or deferred checkPoints are awarded on the real transaction. A missed or overdue notice goes out only from ready data.
Simplified public reconstruction from this personal account. Internal names and security-sensitive details are omitted.

How a change reaches production

  1. Push to mainEvery change lands on main through a reviewed branch.
  2. Parallel
    Staging deployCloud Build builds the image and applies staging.
    Test deployThe same image is promoted to the test environment.
  3. Journey smoke testsA hard gate runs real user journeys against the deployed test service. A failure creates no production build.
  4. Approval-gated production buildPlans, proves the Terraform diff touches only the image and version, then promotes on approval.
  5. Infrastructure slicesAnything beyond the image applies only in a named, target-scoped mode with a confirmation phrase and an invariant test.
  6. Reviewed production changeNothing reaches production unreviewed, and a green staging or test build is never mistaken for a release.
Simplified public reconstruction from this personal account. Internal names and security-sensitive details are omitted.
  • 94xless BigQuery data scanned per bank-data merge, after bounding it on the partition date
  • 0false overdue-rent alerts during a two-day bank-data outage, with 1,634 checks deferred instead of the usual 302
  • 24 in 1,000requests on one endpoint were failing at scale-down; traced to tail latency past the drain budget, then cut by halving instance churn and serving reads from a snapshot

Decisions

Defer a rent check; never falsify it

ChoiceGate every missed or overdue notice on whether bank data is synced through the date the check needs. If not, skip with a recorded reason and re-evaluate on a later pass.

Trade-offDuring a provider outage, genuine issues are evaluated late. We accepted that, because a member wrongly told their rent is overdue loses trust that a correction cannot restore.

Bound the merge on the partition column

ChoiceAdd a transaction-date predicate to the merge so BigQuery prunes partitions. Measure the cost from audit records rather than dry runs, because a dry run is a read metric and a merge is a write.

Trade-offCost now scales with rows, so batch size stopped mattering and we stopped tuning it. The remaining run time is genuine backlog, which drains as the sync catches up.

Renew the job lock while the run is alive

ChoiceRenew the lock at a third of its lifetime, only if this execution still holds it, and stop in the cleanup path. Keep the lifetime short so a crashed run frees it within the hour.

Trade-offSuccessful renewals log nothing, so the lock document is the only place to see them. The return of a stale-lock takeover message is the regression signal, and we watch for it.

Treat endpoint latency as the shutdown budget

ChoiceCloud Run allows a fixed ten seconds after the shutdown signal, and nothing can raise it. So any request slower than about eight seconds at scale-down is lost. We serve slow reads from a shared stale-while-revalidate cache, size concurrency to reduce churn, and treat latency past eight seconds as failure.

Trade-offA shared cache serves slightly stale values, and the first request after an empty key still pays the full read. The mobile client retries, so members see a slow load rather than an error.

Make a production change a reviewed slice

ChoiceJourney smoke tests gate every production build. The approval-gated build proves the infrastructure diff touches only the image and version. Anything else applies only through a named, target-scoped mode with a confirmation phrase and an invariant test.

Trade-offEach new infrastructure change needs its own mode and test, which is slower. In return, nothing reaches production unreviewed, and a green staging or test build is never mistaken for a release.

Tolerate a bad row on read; never hide a wallet

ChoiceWhen a stored gift-card purchase fails a validator that tightened after it was written, sanitise or tolerate it on read. A member must always see a card they paid points for.

Trade-offA tolerated row stays readable but not writable, so its state transitions still need the write-path fix. Expiry is itself a write that releases the member's points, so a write defect blocks both.

Public context

Review the public service context separately from this personal account. It describes the service behavior and does not independently verify my personal contribution.

Limitations

  • This is a personal account of a team's work. It does not attribute every decision to me alone.
  • The figures are point-in-time production measurements from August and September 2026, not guarantees of current behaviour.
  • Customer numbers, financial figures, internal service names, and security configuration are not disclosed.
  • The scale-down failure was reduced on the affected endpoints. The platform limit remains, so the latency rule is a standing operating constraint, not a closed defect.