@notOluwayimika

Backend-leaning full-stack engineer

Oluwayimika builds systems that hold up under audit.

I build systems that hold up under audit — multi-tenant platforms, financial ledgers, and the boring, load-bearing infrastructure underneath them.

Oluwayimika
based
Lagos, NG (UTC+1)
focus
Laravel / React / Postgres
building
AI engineering (RAG, agents)
education
B.Sc, Computer Engineering — University of Ghana, 2024

01 — About

Who I am, what I do

Oluwayimika

I'm a full-stack engineer working mainly in Laravel and React, with production experience across school portals, ERP, and EMR systems. Most of my work sits at the boundary between business rules and data integrity: tenancy, money, permissions, audit trails — the parts of a system that are expensive to get wrong.

I take on client engagements through platforms like Toptal and Andela, and outside client work I'm building Proctora, a multi-tenant CBT platform, from the architecture up. I'm also deep in AI engineering right now — RAG, embeddings, structured LLM outputs — with the goal of bringing that into production systems, not just demos.

based:
Lagos, NG (UTC+1)
focus:
Laravel / React / Postgres
building:
AI engineering (RAG, agents)
education:
B.Sc, Computer Engineering — University of Ghana, 2024
certification:
Google AI Essentials, 2025
github:
@notOluwayimika

02 — Experience

Where I've worked

Product engineering across ERP, EMR, and education platforms — mostly the backend, mostly the parts that carry money, records, or access.

Cavidel Ltd

Dec 2024 — present

Software Engineer

  • Build and maintain features across ERP and EMR (electronic medical records) software used by 9 clinics
  • Led a team of 3 engineers delivering the Business Health Checker, a multi-tenant SME loan-readiness platform
  • Refactored legacy modules using modern frameworks, improving performance on key workflows and bringing the codebase up to current standards

Mattobell Ltd

Mar 2025 — present

Backend Developer (contract)

  • Build backend services for Simpool, an ERP product for the SME sector, using NestJS
  • Develop and test features and modules, with automated tests covering core functionality

Dellyman Nigeria Ltd

Mar 2025 — Sep 2025

Backend Developer (contract)

  • Migrated a legacy PHP codebase to Laravel, modernising the architecture and improving maintainability
  • Built REST API endpoints and wrote tests verifying endpoint functionality

Also built

Business Health Checker

Cavidel Ltd

Multi-tenant survey platform assessing the loan readiness of SMEs — role-based access control, customisable dashboards, data collection and aggregation, separate authentication per user type, and 2FA.

Laravel, SQL Server

Street News

JafoFinance

CMS for a local news site with RBAC, a dashboard, and view-tracking analytics — category and news management, support ticketing, contact-form tracking, and newsletter subscriptions.

Laravel, React

03 — Stack

Skills & technologies

Organized the way I actually think about a system — by the layer each piece sits in.

Backend

  • PHP
  • Laravel
  • NestJS
  • Node.js
  • Domain-Driven Design
  • REST API design
  • Queue-driven architecture

Data & Infrastructure

  • PostgreSQL
  • Row-Level Security
  • MySQL
  • SQL Server
  • Multi-tenant architecture
  • CI/CD quality gates

Frontend

  • React
  • Inertia.js
  • TypeScript
  • Next.js
  • Tailwind CSS

AI Engineering — in progress

  • Python
  • Embeddings & RAG
  • pgvector
  • Structured LLM outputs
  • Agent / function-calling patterns

Practice

  • ADR-driven design
  • Propose → review → implement → verify
  • Adversarial code review
  • System design

04 — Projects

Build log

A running log of what I've shipped and what I'm building, in the same status vocabulary I use in my own specs and ADRs.

Brookstone Portal — invoicing & receivables

Building

Backend / full-stack engineer · Apr 2026 — present

A multi-tenant student information system for a group of Nigerian schools, currently growing an invoicing and receivables module that bills fees, records payments, and maintains a per-student subledger. The defining idea is that every money-affecting invariant is enforced at the lowest layer that can enforce it — MySQL triggers and CHECK constraints, not application convention — so a bug in an action, a seeder, or a tinker session cannot corrupt the ledger. Two-person team; I own the notifications and deliverability substrate, activity-log auditing, the guardian portal, and the approval path into finance.

  • Money is integer minor units plus an ISO-4217 currency in paired columns, cast through one immutable value object that throws on cross-currency arithmetic — with the currency-shape CHECK forced to utf8mb4_bin, since the default collation matches 'ngn' and would have been a false all-clear
  • Maker–checker holds in three independent places: a CHECK (submitted_by <> decided_by) on every approval table, a route gate derived from the permission catalog, and an exclusion carving *.approve / *.reject out of the super_admin bypass — platform authority can raise a credit note but never sign one off
  • The subledger is append-only at the database via no_update / no_delete triggers, so a void reverses a charge instead of erasing it; balances are a projection maintained by an atomic balance = balance + :delta upsert, with lockForUpdate on the invoice before posting
  • Two reconciliation commands with deliberately different truth models — reconcile-accounts repairs the balance projection from the ledger, while audit-ledger-coherence ships with no --fix at all, because a void invoice missing its reversal has two equally consistent stories and repairing either way writes money on a guess
  • school_id is the only isolation boundary, with super_admin bypassing authorization but never isolation — off-request jobs and commands may enter school context only through an explicit runFor() carrying the school id, and a CI lint fails the build on reading the actor's own school_id
  • Laravel 13
  • PHP 8.3
  • MySQL 8
  • React 19
  • Inertia
  • TypeScript
  • AWS SES + SNS

Brookstone Portal — RBAC & impersonation

In production

Lead engineer, authorization subsystem · Jul — Aug 2026

The authorization layer of the same multi-school SIS: 80 permissions across 14 roles, granted per school through spatie teams, with maker–checker separation enforced from the database up. The defining decision is that the platform administrator has no ambient authority over school data — to act inside a school they impersonate a named user, on the audit trail, as that user.

  • Impersonation is a per-request swap of three captured values — the auth guard's user, the active-school override, and the spatie team id — each restored independently in a finally, because a partial restore is a silent team leak into the next request. The middleware's slot in the stack is the security decision: after the 2FA-enrolment check, before Inertia's prop sharing
  • Stopping a session is authorized by session state, never by the acting user's grants — inside the session the operator is the target, who does not hold rbac.impersonate, so a permission-gated stop route would 403 them and strand them inside someone else's identity with no way out
  • Every action inside a session is attributed to the operator, with the causer pinned once per session rather than at each write site; entry and exit both go through one helper shared by the explicit stop, the logout listener, and the 30-minute expiry, so no path can log a start with no end
  • Maker–checker pairs are derived from a naming convention over the permission catalog rather than a hand-kept list, so a future approval flow joins with no edit — granting one role both halves is refused at grant time with the offending pair named, and a grant crossing the school_id boundary is rejected by the same request object
  • The role: → permission: route-gate migration was verified against a fixture oracle derived before the change: allowed-role sets re-derived per baselined route and demanded equal, so new routes never block the suite while an access change to an existing one stays red until the fixture is regenerated as a reviewed diff
  • Laravel 13
  • PHP 8.3
  • MySQL 8
  • spatie/laravel-permission (teams)
  • React 19
  • Inertia
  • Pest

Brookstone Portal — academic records & results

Shipped

Full-stack engineer, academic subsystem · Apr 2026 — present

The academic core of the same multi-school SIS: school structure (sections, year groups, arms, streams), curricula and their subjects, score capture against a weighted marking scheme, and the reports built from them — per-student result sheets and per-class broadsheets. The defining decision is that a student's enrolment is an immutable episode rather than a mutable row, so history stays readable after promotion, withdrawal or a subject change.

  • Enrolment is an episode, not a field on the student, and its school_id is derived from the student rather than filled from whoever is logged in — with a composite foreign key (student_id, school_id) → students(id, school_id) making the derivation checkable instead of trusted. The model deliberately skips the shared tenant trait, whose creating hook would make an enrolment's school a function of the current session
  • Withdrawing a student soft-deletes the student row while the enrolment and subject rows outlive it, so $enrolment->student legitimately resolves to NULL on a row that still reads 'active'. Two endpoints dereferenced it and died; the fix made the null a first-class case in the read paths rather than cascading the delete, because the historical result must survive the withdrawal
  • Bulk import treats the spreadsheet as untrusted input at three layers: a client-side pre-flight that names the offending row and column before anything uploads, a per-row validator on the server, and a dedup pass keyed on normalised phone/email so a parent listed across sibling rows is linked to both children — with an in-file cache for the duplicate-within-one-upload case a database lookup alone would miss
  • Admission and staff numbers are generated in a creating hook wrapped atomically by a save() override — a 'you must use save()' convention that failed repeatedly, so it is now enforced by a CI lint forbidding DB::table('students')->insert, createQuietly, and upsert anywhere in app/
  • Curriculum subjects carry no school_id of their own — they are owned through their curriculum — so route-model binding alone will happily resolve another school's UUID. The score-entry route checks the parent curriculum's school explicitly and 404s, using getOrFail() rather than id() because with no active school both sides would be null and a bare comparison would pass, which is the fail-open direction on an isolation check
  • Laravel 13
  • PHP 8.3
  • MySQL 8
  • React 19
  • Inertia
  • maatwebsite/excel
  • Pest

Simpool ERP Backend

In production

Backend engineer · Jan 2026 — present

Multi-tenant ERP API for small businesses covering inventory, warehousing, invoicing, and double-entry accounting, where every operational document (customer invoice, supplier bill, stock transfer) posts into a shared journal-line ledger rather than carrying its own balance fields.

  • Invoice payment posting is idempotent by source, not by call — recordPayment back-posts a missing recognition entry before writing cash, so an invoice paid straight from draft can't leave an orphaned credit on the receivable account
  • Access is gated by two independent checks that must both pass: the plan must include the module and the staff role must carry the feature permission, so downgrading a plan can't silently widen staff access
  • Paystack webhooks are the only path that mutates subscription state — HMAC-SHA512 verified, dispatched on the stored transaction's intent, and re-checked for ACTIVE, so at-least-once delivery can't double-provision
  • Stock is a movement ledger, not a counter — signed quantityDelta with stockBefore/stockAfter and unit cost captured at write time, which is what makes an invoice cancellation reversible and the reports derivable
  • NestJS
  • TypeScript
  • PostgreSQL
  • TypeORM
  • Redis
  • Paystack
  • Docker

Proctora

Building

Founder & sole engineer · Jul 2026 — present

A multi-tenant computer-based testing platform where organizations author, administer, and score exams on their own subdomain, embedded in an iframe, or over an API. Every tenant-owned table carries an org_id under FORCE ROW LEVEL SECURITY, so isolation survives raw SQL, a forgotten model trait, and a queued job that resolved no tenant — the Eloquent scope is convenience, the policy is the guarantee.

  • Tenant isolation is enforced twice, independently: a global Eloquent scope plus ENABLE + FORCE ROW LEVEL SECURITY on every tenant-owned table, with policies reading a per-request Postgres session variable that fails closed when unset. FORCE is the load-bearing half — without it the table owner, which is the role the app connects as, bypasses its own policies. Every isolation test issues at least one raw query with no WHERE clause, because an Eloquent-only test cannot distinguish 'RLS works' from 'RLS is absent'
  • One human is one global identity with a per-organization membership row, and users carries a membership-existence policy — so a candidate already held at another organization is invisible to every query, including the subquery that would attach them. The one sanctioned way through is a SECURITY DEFINER function owned by a NOLOGIN role that is neither the app role nor any admin role, consuming the verification token and attaching the identity in a single call
  • Starting an attempt: a count computes the attempt number and a UNIQUE(test_id, org_candidate_id, attempt_number) constraint decides the race, because two simultaneous starts both pass the same count. The paper is drawn inside the transaction and throws rather than returns, so a pool that has shrunk since publish rolls the attempt back instead of burning one of the candidate's attempts on an exam they never saw
  • closes_at bounds entry, not duration — the deadline is fixed once at start as started_at + duration + accommodation minutes and never recomputed. The earlier min(started_at + duration, closes_at) rule silently shortened the exam for anyone starting late, and cut hardest into extra-time accommodations, since a cap takes the accommodation first
  • A test suite can prove code behaves correctly when invoked and structurally cannot ask whether anything invokes it — a fully tested route shipped with no button. A reachability test now ties every server-declared nav key to a client destination and every destination to a registered route, and refuses a 'not built yet' placeholder naming a capability that already ships
  • Laravel 13
  • PHP 8.3
  • PostgreSQL (RLS)
  • React 19
  • Inertia 3
  • Pest 4
  • Playwright

Workers Institute

Building

Solo engineer · Jun 2026 — present

A hiring and workforce-management platform where employment records — hires, exits, incidents, reviews — attach to a verified government identity rather than a self-asserted profile, making work history portable and checkable across employers. The whole product hangs off one primitive: an identity whose assurance level is derived from evidence and can only change through a single audited transition authority.

  • Identity verification is an enum-backed state machine with one write path: every change takes a row lock, validates the from→to edge, derives assurance level from the evidence method, and appends an immutable transition record — rejected and revoked are terminal, so a failed claim cannot be edited back into a verified one, only re-attempted
  • Provider verification was pulled out of the claim transaction — the claim commits at provider_pending, then a queued job calls the KYC gateway with no lock held and resolves under an expectedTransitionId guard, so a slow reply cannot overwrite a newer attempt. A scheduled sweep re-dispatches stranded attempts, keeping a provider outage retryable instead of a silent negative identity decision
  • Government ID numbers are never stored or queried in plaintext — encrypted at rest and matched by an HMAC-SHA256 blind index over a canonicalised country|type|issuer|number tuple with a key-version column, so claim lookup is an indexed equality match rather than decrypt-and-scan
  • The credit wallet is an append-only ledger: every entry takes lockForUpdate on the wallet row inside a retrying transaction and writes balance_after. Entries are idempotent on a caller-supplied reference — a replay returns the original entry, and reusing one with a different amount or direction throws rather than double-posting; debits spend balance minus locked_balance
  • An employer cannot unilaterally write a permanent adverse record — submitting an incident opens a fixed rider response window, serious and critical findings land in pending_moderation for platform review instead of finalising, and every state change appends a versioned revision row
  • Laravel 13
  • PHP 8.3
  • MySQL
  • React 19
  • Inertia 3
  • Laravel Reverb
  • Paystack

05 — Off the clock

Passions & hobbies

The parts of the week that aren't about a keyboard.

Football

Plays for a local team — the one part of the week that has nothing to do with a keyboard.

Running & the gym

Running and strength training, mostly for the same reason good systems need one: consistency compounds.

Teaching

Volunteer programming teacher and Head of Technical at his church — introducing kids and youth to code.

06 — Contact

Get in touch

Open to remote, USD-paying engineering roles and select client engagements.

Download CV (PDF)