Senior full-stack engineer · Barbados · Atlantic Time

There's no off switch

Nine years in PHP, five of them Laravel. Most of what I do is work on software that is already running — the kind with years of decisions baked into it and customers inside it while you change things. The interesting problems there are rarely new features. They are the migrations, the search indexes, the payments, and the things that break in a way nobody wrote a plan for.

Since 2022 I have been with Bandwidth Productions, working remotely from Barbados on the back end of a CMS that has been running fifteen years and is now on its second codebase. Starting from nothing is fun, but keeping something alive while you rebuild it underneath is the part I'm good at.

Laravel MySQL OpenSearch Stripe Redis & queues AWS
Javon Webster, in a burgundy tuxedo, speaking at a podium.

The evidence

The next three are the same problem wearing different clothes: change something people are actively depending on, without them finding out the hard way.

CASE STUDY

Search, rebuilt on a platform where nobody could find anything

Search here was ancient and everyone knew it — clients had been filing versions of the same complaint for years. I replaced it with two OpenSearch indexes, one public and one for the CMS. Records reach the index as soon as they are saved, a sweep every ten minutes picks up anything the queue dropped, and results are filtered by what the person asking is actually allowed to see. When the cluster is unreachable, search quietly falls back to MySQL instead of taking the site down with it.

The second index gave staff something the platform had never had: a way to search the CMS itself. Pages, users, templates, settings and reports — findable, rather than remembered.

Read the case study →
IndexesDual
Reindex window10 min
Sync triggerModel events
Cluster downMySQL fallback
Result gatingPer role
Migration

Fifteen years of CodeIgniter, moved while everyone kept using it

Two data models that disagreed about almost everything had to become one, and 148,000 customer accounts and 133,000 orders had to cross over. I built the importers to run in stages and to be safe to run twice — dry run first, re-link afterwards — because on live data you only get to be wrong quietly.

Payments

Payments, and what happens when they go wrong

Orders, customers and payments are mine to look after — the schema underneath and the services on top. Stripe Checkout, webhooks, subscriptions and promotions now; before that, microservices for a payment authentication platform and a First Atlantic Commerce integration. Plugging in a provider takes an afternoon. The rest of the job is the webhook that arrives twice, the one that never arrives at all, and the provider having a bad morning.

The seam, in eleven lines
// Every read goes through here. The fallback is the whole point.
public function search(Query $query, User $as): Results
{
    try {
        return $this->cluster->search($query, $this->accessTerms($as));
    } catch (SearchUnavailable $e) {
        Log::warning('search degraded', ['reason' => $e->getMessage()]);

        // Worse search, working site.
        return $this->database->search($query, $as);
    }
}
Trimmed for reading — the real one carries metrics and a circuit breaker so a flapping cluster is not retried on every request.
And the tools around them

Working on live systems means most of the leverage is in what surrounds them — what editors can do without a developer, and what has to be true before anything ships.

Language design

BAML — a template language, and the compiler for it

Editors needed to assemble pages without a developer sitting beside them, so I designed a small template language for it — 35 tags — and wrote the compiler that runs it. It went from a sketch to something the platform leans on every day, and it is still the most enjoyable thing anyone has handed me.

Delivery

The pipeline underneath all of it

Every push runs the suite, PHPStan and Pint through Bitbucket Pipelines before it goes anywhere, and deploys run per environment from there. There are close to a thousand tests on the platform now, which is the actual reason the team will touch old code at all. Production errors land in Slack, so we usually hear about them before a client does.

Bettuh Be · my own infrastructure

The bytes I tested are the bytes running

Bettuh Be is where I own everything below the application as well as the application itself: Laravel 13 and Filament 5 sites taking payments, on two provisioned Ubuntu servers — one for development and staging, one for production — with Caddy handling TLS, per-project database roles and isolated Docker networks.

Stack

Where the day goes, roughly in order of how much of it each thing takes.

LanguagesPHP · JavaScript · SQL · Python · Go · Java · HTML5 · CSS3
FrameworksLaravel · Symfony · CodeIgniter · Blade · Vue.js · React · Flutter
Data & searchMySQL · MariaDB · PostgreSQL · AWS OpenSearch · Redis
APIsREST design · webhooks · secure API authentication · service contracts · third-party integration
PaymentsStripe — Checkout, webhooks, subscriptions, promotions · First Atlantic Commerce
Event-drivenLaravel queues · Redis · Supervisor · model-event sync · scheduled jobs · graceful degradation
Cloud & devopsAWS (OpenSearch, SES) · DigitalOcean · Docker · Caddy · Nginx · Apache · Linux · Git · GitHub Actions · Bitbucket Pipelines
TestingTDD · PHPUnit · PHPStan · Laravel Pint · peer review
SupportSecond-line escalation · tracing a fault across app, database and server · runbooks · writing the explanation the client actually reads

Background

RoleWhereWhenHow
Senior Software EngineerBandwidth ProductionsNov 2022 — presentUnited States · remote
PHP Application DeveloperEntermotion Design StudioSept 2021 — Nov 2022United States · remote
Server-Side DeveloperWepalaApr 2017 — Sept 2021Trinidad & Tobago

BSc Computer Science, University of the West Indies, St Augustine — 2016, Dean's Honour Roll.

Get in touch

Happy to talk about any of this

Email is the best way to reach me.