Work
4 case studies
Platforms that stay up, and the AI built on top of them
Nine years of enterprise software: multi-tenant messaging infrastructure, Angular architecture, and the last three years spent putting language models inside all of it. Here is how the decisions actually went.
- Years building enterprise software
- 9
- Roles since 2017
- 5
- Messaging channels run as one platform
- 3
- Angular, one major version at a time
- v2→v18
Selected work
How the decisions actually went
Enterprise messaging platform
CPaaS · In production
One send, from API to report - APITenant request
- QueuePer-tenant limits, ordered retries, idempotency key
One model, thin adapters
- RCS
- SMS
- CarriersUpstream limits and outages
- ReceiptsArrive as webhooks
- Reporting storeKept apart from sending
Hard ruleA retry can never send, or bill, the same message twice.
Challenge
Enterprise messaging is sold as one product and is really three. RCS, the WhatsApp Business API and SMS each arrive with their own provisioning model, their own template rules, their own carrier relationships and their own idea of what "delivered" means. Sales sells a platform. Engineering inherits the differences.
Constraints
Multi-tenant from the first line, so one tenant’s traffic spike cannot become another tenant’s outage and no tenant can ever see another’s data. Every send costs real money, which makes a retry that fires twice a bill rather than a warning. And the carriers sit upstream: their rate limits, outages and schema changes are facts to absorb, not bugs to fix.
Trade-offs
A queue between the API and the carriers instead of sending inline. It costs immediacy and a lot of operational surface, and buys back per-tenant rate limiting, ordered retries and somewhere to stand when a provider goes down. Idempotency keys on every send, paying the storage and lookup cost, because double-charging a client is unrecoverable in a way that latency never is. One internal message model with thin per-provider adapters, so a new carrier costs an adapter rather than a fork. Delivery receipts arrive as webhooks and land in a reporting store kept separate from the operational one, so an analytics query can never slow down sending.
Outcome
It runs as the core of the CPaaS product across all three channels, and onboarding a new enterprise tenant became a configuration exercise rather than an engineering project. The send path was never the hard part. The work that decided whether this was any good was the provisioning, the delivery tracking and the reporting around it - none of which anyone has ever asked to see demoed.
Keywords
- CPaaS
- RCS Business Messaging
- WhatsApp Business API
- SMS · OTP
- Multi-tenant SaaS
- Queue-based delivery
- Idempotency & retries
- Webhook processing
- Node.js
- MySQL · ClickHouse
Conversational AI in production
Enterprise LLM agents · In production
One customer turn - Customer turnUnder a client’s brand
- RouterPicks the model the turn earns
- Smaller modelOrdinary turns
- Larger modelTurns that earn it
- Structured outputParse, don’t trust prose
Confidence
- ReplyHigh
- HumanLow, on purpose
In CIEvery prompt change clears a golden set of real conversations, model-graded, before it ships.
Challenge
Support conversations that a person handled well and a scripted bot handled badly. Putting a language model in front of them is the obvious move. The non-obvious part is that a model which is right most of the time is a liability in a channel where every reply goes out under a client’s brand, to their customer, with no way to take it back.
Constraints
It runs across many tenants, each with their own catalogue, tone and rules, so there is no single prompt to tune and no single definition of a good answer. Every token is billed and every second is felt, because someone is watching a typing indicator while it thinks. And a wrong answer is not a bug report - it is a customer who now believes something untrue.
Trade-offs
An evaluation suite before any of the interesting work: a golden set of real conversations, model-graded scoring, and regression tests running in CI, so a prompt change became a reviewable diff instead of a vibe. Routing ordinary turns to smaller models and reserving the expensive one for the turns that earn it, which costs architecture and saves most of the bill. Prompt caching and context compression in preference to simply buying a longer context window. Structured outputs everywhere, because a parse failure is far easier to handle than a confidently wrong sentence. And a deliberate handover to a human when confidence drops, which lowers the automation rate on purpose.
Outcome
Automated resolution rose and escalations caused by wrong answers fell, but the durable result was the harness rather than any particular prompt. Once every change had to clear the same set of conversations, prompt engineering stopped being folklore and became something a team could review, argue about and roll back.
Keywords
- LLM agents
- Evaluation harnesses
- LLM-as-judge
- Tool calling
- RAG
- Model routing
- Prompt caching
- Structured outputs
- Langfuse tracing
- OpenAI · Anthropic Claude
Enterprise frontend platform
Angular architecture · Multi-product
Angular v2 to v18, shipping at every step - v2
- v4
- v5
- v6
- v7
- v8
- v9
- v10
- v11
- v12
- v13
- v14
- v15
- v16
- v17
- v18
- Before
- After
Challenge
Several products, years of accumulated Angular, and a framework that had moved a long way underneath all of it. The application booted by loading nearly everything it might ever need, real-time conversation state was handled by whoever happened to write that screen, and each product had quietly grown its own version of the same button.
Constraints
These are tools people use to do their job, so there is no maintenance window and no appetite for a rewrite. The upgrade had to cross several major versions without a release where the product stopped moving. And the team was shipping features throughout - nobody was ever going to fund a quarter of pure refactoring.
Trade-offs
One major version at a time, shipping after each, which is slower and considerably more boring than jumping straight to the latest and never leaves the product broken. Legacy modules refactored into a lazy-loaded architecture so the first paint stopped paying for screens most users never open. Real-time state standardised on RxJS with a single pattern for subscription lifecycles, because the race conditions were not hard bugs - they were the same bug written many times over. And a shared component library across products, accepting the coordination cost, so that a fix lands once.
Outcome
The applications load faster on a smaller bundle, and a framework upgrade is now routine rather than an event. The more useful outcome is quieter: new screens start from a component that already exists, so the interesting decisions get spent on the product instead of on re-deciding what a table looks like.
Keywords
- Angular v2 → v18
- Lazy-loaded architecture
- RxJS state management
- Design systems
- Component libraries
- Web performance
- TypeScript
Atlas
Personal operating system, in daily use
How Atlas answers: cheapest step first - Rule
- Cache
- Reasoner
- Projection
- ModelOn device first
Never movesPrepare anything, send nothing. No email, no payment, no irreversible action.
Challenge
A busy life is spread across a wearable, three inboxes, a bank, a broker, a calendar and a notes app. Every one of them organises information and none of them remove work. The assistants shipping today make that worse: they are reactive, so you have to ask; siloed, so each sees one domain; and theatrical, so they impress on a stage and change nothing on a Tuesday.
Constraints
It reads health, cycle, money and mail, so private data cannot leave the device by default. It is self-hosted and privately deployed, built in the hours around a full-time job. And it has to hold one line that never moves: prepare anything, send nothing. No email, no payment, no irreversible action, at any level of trust.
Trade-offs
On-device inference first with the cloud as a fallback that never sees raw data, which spends capability to buy privacy. A deterministic ladder - rule, cache, reasoner, projection, then a model - rather than calling an LLM for everything, trading elegance for latency and near-zero running cost. Autonomy is earned by matching how the user would have judged it, in shadow, about ninety per cent of the time, rather than granted by a settings toggle. And it counts what it decided not to say, because in this category silence is the feature.
Where it stands
The reasoning is built and in daily use: the cross-domain brain, the model of the user, forecasting, and the per-domain agents. The honest read is that it knows considerably more than it reliably acts on, so the bottleneck now is reach and action rather than intelligence. That is the better problem to be left with, because behaviour can be measured and iterated and intelligence mostly cannot.
Keywords
- Agentic AI
- Multi-agent orchestration
- RAG & vector search
- On-device LLM inference
- Event-driven architecture
- Python · FastAPI
- PostgreSQL · pgvector
- SwiftUI · HealthKit
- Model Context Protocol
Areas of focus
Six places the engineering time goes
| Area | 01Enterprise messaging platform | 02Conversational AI in production | 03Enterprise frontend platform | 04Atlas |
|---|---|---|---|---|
| Platform architectureMulti-tenant systems, where one tenant having a bad afternoon must never become everyone’s bad afternoon. Isolation, per-tenant limits, role-based access, and retries that are safe to run twice. | No | No | ||
| Conversational messagingRCS, the WhatsApp Business API and SMS presented as one platform rather than three integrations. Agent and template provisioning, delivery tracking, and the carrier differences underneath all of it. | No | No | ||
| AI product engineeringLanguage models inside software people use at work. The model is the easy part. The job is evaluation, cost, latency, and what happens the moment someone asks it something nobody planned for. | No | No | ||
| Frontend architectureAngular from v2 through v18, and the structural work that keeps a large application quick: lazy loading, a single pattern for reactive state, and a component library people actually reach for. | No | No | No | |
| Real-time systemsSockets, concurrent conversation state, and operator dashboards that have to stay correct while several thousand things change underneath them at once. | No | |||
| Backend and dataNode.js services, queue-based processing, and the reporting pipelines that turn a firehose of delivery events into something a business can actually read on a Monday. | No | No |
Track record
2017 - present · hover a bar for the role
- MEAN Stack DeveloperSummitworks TechnologiesApr 2017 - Sep 2018 · 1 yr 5 moMEAN-stack enterprise applications, multi-tenant marketplaces, and cross-platform mobile builds in Ionic.
- Software EngineerBuilder.aiOct 2018 - May 2020 · 1 yr 7 moInternal delivery platforms used daily by engineers and product managers to run projects from idea through to production.
- Software EngineerValueCodersJun 2020 - Apr 2021 · 10 moAngular dashboards and multi-tenant SaaS interfaces for enterprise clients, on REST APIs and CI/CD-aligned release workflows.
- Senior Software DeveloperOneClick Technologies (Infotel Group)Jun 2021 - Mar 2022 · 9 moAngular chatbot platforms for real-time enterprise messaging, and the shared component libraries underneath them.
- Technical SpecialistNexG Platforms (Infotel Group)Apr 2022 - present · 4 yrs 7 moApplied AI and platform architecture across a CPaaS product and a CRM, and delivery for an eight-person cross-functional team.
How I think
Five rules, arrived at the hard way
None of these are original. They are the things nine years of shipping kept proving, usually by charging me for the lesson first.
Reliability beats demos
A demo has one path through it and a friendly audience. Production has neither. The work that decides whether software is any good happens after the impressive part is finished.
Simplicity is what scales
Every clever mechanism is a thing someone has to hold in their head at 4pm on a bad day. The systems that survive are the ones a new engineer can be wrong about safely.
Users are not reading your architecture
Nobody outside the team has ever been impressed by a diagram. They notice whether the thing was fast, whether it was right, and whether it did what they expected. That is the entire scoreboard.
Good products remove decisions
Most software adds choices and calls it flexibility. The better move is usually to make the sensible thing the default and let the exception cost something.
Design for tired people
The person using this is busy, half distracted and mid-way through something else. Software written for an attentive user is software written for a user who does not exist.
Everything else
Writing · Talking · Shipping
- WritingThe archiveYears of articles on AI and product engineering, plus the personal writing that has nothing to do with either.
- PodcastPositively YoursWeekly conversations about growth, healing and the mindset behind a life you actually want.
- CodeGitHubExperiments, half-finished ideas and the things that never made it as far as a landing page.