Backend & AI infrastructure Lagos · UTC+1 Open to roles

The demo
always works.Production is the part nobody demos.

I build the layer that keeps LLM features alive once real traffic reaches them. Deterministic tests around non-deterministic output. Repair loops with a hard cycle bound. Error classification that knows which failures time can fix. Retrieval scoped inside the query rather than above it. Eight years of backend work sits underneath all of it, most of that in Postgres.

Repair loop, boundedStructure, not telemetry
CYCLE 1fail
CYCLE 2fail
CYCLE 3halt
Six steps per cycle. The loop stops at three whether or not the build is green, because a model that has misread the error twice will not read it correctly on the fourth pass. The bound is the feature.
8
Years, production

Backend since March 2018. Node and TypeScript, Python, Laravel, and the SQL under all three.

4
Years, regulated data

On an ABTA/ATOL-bonded UK travel platform. GDPR refactoring, column-level PII masking, automated retention and purge.

44
Test files, from zero

Written on an AI app-builder platform, roughly a third of its coverage. Nobody asked me to.

Span 01 · Mechanisms

Three decisions, and the case against each one.

Backend work is invisible by design, which is what makes it easy to describe and impossible to show. So here it is running instead. All three shipped on an AI app-builder platform where I was the only backend engineer. Every one names the option I rejected and the point at which that option would have been the right call.

Not every failure deserves a retry.

A deployment loop that retries everything is a loop that sends the same broken statement at a database thirty times and then reports a timeout. The useful work happens before the retry decision, in splitting failures that time can fix from failures that time cannot.

Network and readiness errors go back through the loop, because the platform is often still provisioning. A SQL syntax error breaks out on the first response. So does an object that already exists. Neither resolves by waiting, and continuing to ask only buries the real cause under twenty-nine identical failures.

The case against

Blanket exponential backoff is right when the failure modes are genuinely uniform, which describes most read paths against a single upstream. It stops being right the moment the loop can receive an error the retry itself cannot change. If your dependency only ever fails on availability, classification is ceremony.

Classifiermax 30 attempts
Pick a failure and watch where it goes.

An AI that fixes builds needs a stop condition more than it needs intelligence.

When a mobile build fails, the loop pulls the build log over the provider's API, decompresses the stream, and extracts the one actionable error instead of handing a model forty thousand lines of noise. The project file tree goes in alongside it as context. The model writes a patch back to project source and the build re-triggers.

Retry state persists on the build record, so a process restart does not reset the count. The loop stops at three cycles whether or not it has succeeded. Three is not a tuned number. It is the point past which a model that has misread the error twice is unlikely to read it correctly on the fourth pass, and every further cycle is spend.

The case against

Unbounded self-repair is defensible when a human reviews every patch before it lands, which is how most assisted-fix tooling works. Once patches write directly to project source with no review gate, the bound is the only thing standing between a confident wrong diagnosis and a bill.

Repair loopMAX_RETRY_CYCLES = 3
Idle

The model picks the template. It does not write the code.

Free-form generation of edge functions produces code that reads correctly and fails at deploy, on import syntax, runtime APIs and permission scopes. So the model's job got smaller. It classifies intent, maps the request to one of six audited function templates, and returns a confidence score. Below the threshold it asks a clarifying question instead of guessing.

A normalization pass at deploy time rewrites five classes of deprecated import to pinned specifiers, because the templates were written against a runtime that kept moving. Reliability here came from shrinking what the model was permitted to decide, not from a better prompt.

The case against

Free-form generation wins where the surface is genuinely open-ended and a human runs the output before it touches anything real. For a deploy path with six recurring shapes, the template library covers the distribution and the security audit happens once rather than on every request. Add a seventh shape a month and the maintenance cost overtakes the reliability gain.

Intent routerthreshold 0.70
Pick what a user might ask for.
Span 02 · Position

Retrieval is a database problem wearing an AI costume.

The standard move is to bolt a vector store onto an application that already runs Postgres, then rebuild tenant isolation in application code. Which means the boundary between one customer's documents and another's is now a filter somebody has to remember to write, in every path, forever. Miss it once and a support agent's question gets answered with another company's contract.

Postgres already does this. The embeddings live next to the rows they describe, an index makes the search fast enough, and row-level security scopes the result set inside the query plan. Isolation becomes a database guarantee rather than a code-review promise, and retrieval joins against the relational data instead of trying to reproduce it in metadata.

This is the part worth arguing about, because it changes what eight years of schema design and migration work is for. It stops being a previous chapter and becomes the reason the layer above it holds.

-- one query. no application-side filter to forget.
SELECT d.id, d.title,
       d.embedding <=> $1 AS distance
FROM   documents d
WHERE  d.tenant_id = auth.tenant()   -- RLS attaches this
AND    d.status = 'indexed'
ORDER BY distance
LIMIT  8;

The tenant predicate is not written by the caller. The policy attaches it to every read of the table, so a forgotten filter in new application code cannot leak across the boundary.

Span 03 · Work

What I've built, and what I can show of it.

Client and product work is described rather than linked. The code is either proprietary or the product has not launched. The open-source work is linked, readable, and scoped honestly below.

Product links go up as each one launches on its own domain. I would rather show you nothing than send you to a half-branded staging URL.

Span 04 · Track

Where the eight years went.

Span 05 · Stack

Split down the middle, on purpose.

Most stack lists flatten "I have run this in production for years" and "I built a weekend project with it" into one row of logos. That difference is the whole conversation in an interview, so it is drawn here instead of left for you to find out.

Open to backend and AI-infrastructure roles.

Based in Lagos, working with teams anywhere. Full overlap with the European day, mornings with US East. I am strongest where the interview looks like a take-home, a system design conversation, or a code review, and I am happy to walk through any mechanism on this page in as much detail as you want.

B.Sc. Geology, Ekiti State University, Nigeria (2011–2016)