The numbers are real operations
There is something different about shipping software that an entire business uses to open its day, sell products, move stock, print receipts and close its books. The bug reports stop being theoretical. Someone is standing at a counter waiting for the system to respond.
Express Cloud, an enterprise resource planning platform I built through Zivora Labs, has reached that point. In its initial deployment, it is being used across approximately 30 workstations, five departments, three branches and three warehouses, with roughly 150 concurrent sessions. Its accounting view reflects approximately ₦1.2 billion in recorded assets. More than ₦23 million in sales was processed during the first week after launch. During busy periods, the workflow has handled about six transactions per minute, with an average sale value of roughly ₦50,000.
These are operational figures from the deployment, not an audited financial statement or a synthetic load-test benchmark. The recorded asset figure is not cash held by the application, and an active-period transaction rate should not be mistaken for a constant seven-day throughput. I care about that distinction because inflated numbers make for good posts and bad engineering.
The milestone matters to me because it represents a transition I used to think about only in design discussions: software becoming the operational backbone for real people, inventory and financial records.
This article describes engineering decisions and lessons from a real deployment. The client, individual staff, suppliers, internal addresses, infrastructure details, private integrations, account records and proprietary source are intentionally omitted. The examples below are illustrative pseudocode, not copied application code. Exact company figures have been rounded.
Designing around the way the business works
One of the most important decisions I made was to learn the operational workflow before settling the software architecture. A growing trading business with multiple locations is not a generic demonstration tenant. Its processes cross departments, staff roles, sales counters, banks and warehouses, and the system has to preserve one account of what happened.
One customer might pay through a bank transfer while another pays cash. A transaction may begin at one branch and require stock from a different warehouse. Staff may recognise a product by brand or the name used at the counter, not by its perfect catalogue description. A new price needs to be reflected where the sale is happening, not the next morning after a refresh. A receipt printer may have a completely different idea of what a valid print command looks like.
The software used before this deployment could not adequately support the business's particular workflows. That did not make every international enterprise product unsuitable for African businesses. It made this engineering problem specific: adapt to local operational practices without sacrificing financial correctness, traceability or maintainability.
My starting question became: what does the business actually do between one action and the next? Not what should it do in a polished demo. What happens when the wrong branch is selected, a product arrives late, an invoice is corrected, a customer owes money, or two people attempt to sell the same stock?
From sales screens to an operating system
The repository history starts in July 2026. Express Cloud did not remain a simple point-of-sale application. The work grew from connected sales, purchasing, customer and stock workflows into a broader, modular ERP with financial controls, document handling, access boundaries, reporting, and operational recovery.
Early architectural choices centred on a common product and transaction model. Separate screens for Create Sale and the POS workstation are useful to different operators, but they must not disagree about what a product is, which branch owns it, what it costs, or who is allowed to sell it. That meant consolidating catalogue lookup, branch context, server-authoritative calculations, and sale lifecycle rules instead of implementing each screen as an island.
As more of the business moved into the system, the edges became the main workload: quotations and sales drafts; deposits and customer credit; purchasing and supplier balances; warehouse transfers and stock counts; journal entries and accounting reports; account permissions and approval paths. Later changes focused increasingly on predictable behaviour under concurrency, missing images, stale searches, auditing, offline error handling and document compatibility.
I have learned not to romanticise that progression. A feature added quickly can create a second source of truth. Every extra module increases the number of relationships that must remain correct. The job is to keep the system coherent while the business keeps moving.
Inventory isn't a single number. It has a location, a history and a consequence.
Stock in a warehouse is not automatically stock available at the sales counter. Stock transferred between locations is not the same as stock sold. Quantity on a product card is not proof of what can be fulfilled from the selected branch. If a transfer is received partially, or a count reveals a discrepancy, the system needs to tell the truth about both locations.
I had to build around branch-scoped inventory, transfers, stock adjustments, purchasing, reconciliation and the relationship between a physical movement and its financial consequence. Those are different events and should not become the same database update merely because both change a number.
One generic pattern captures the principle. This is illustrative pseudocode, deliberately not the application's implementation:
transaction:
authorise(actor, branch)
stock = lock_stock(product, warehouse)
require(stock.available >= quantity)
sale = record_sale_with_unique_request_id(...)
record_stock_movement(sale, -quantity)
record_payment_allocation(sale, ...)
commit
publish_catalogue_change(branch)
It is not enough to show a success toast. The stock, payment and sale records need to agree, and a retry must not create a second sale. Under concurrent requests, the system must have one authoritative decision about available quantity.
A sale is more than a receipt
One of the mistakes in business software is to make the POS screen look complete while leaving the rest of the transaction lifecycle disconnected. The actual workflow can start as a quotation, become a draft, turn into a sale, include a deposit, create receivables, and eventually require a return, credit note or adjustment. Different staff may be allowed to perform different parts of that journey.
Express Cloud's sales workflow grew to cover those transitions as connected states. The same product lookup powers multiple sales surfaces. The system needs to resolve live prices and availability for the selected context and keep document generation consistent with the authoritative transaction, not whichever values happen to be visible in a browser field.
There is also a surprisingly human detail here. When staff add a new customer at the counter, that customer must become selectable immediately. When a colleague adds a product elsewhere, the person already on the POS screen should not have to close the sale and start again. Small interaction gaps become serious operational friction when repeated dozens of times a day.
Accounting is not an attractive balance sheet
It is tempting to treat accounting as a reporting layer that can be attached once sales and inventory are working. In practice, financial correctness reaches back into every operational event. How was an opening balance imported? Which account receives the debit and which receives the credit? What is the source of the entry? What happens when the entry was wrong? Can it be reversed without quietly rewriting the original event?
The platform's accounting work expanded into double-entry journals, entry posting, ledgers, reconciliation, receivables, payables and controlled corrections. Some of the harder engineering was not calculating a total. It was ensuring that the total could be explained from durable records and that a correction left an understandable history.
This changed how I think about the word secure. Encryption and authentication are important, but financial systems also need consistent state, constrained authority, traceable changes, idempotent operations and recoverable failures. A perfectly encrypted wrong balance is still a wrong balance.
Then a receipt printer reminded me that production exists outside the browser
At one point, a receipt format worked on one thermal printer and produced unusable output on another. We could have kept trimming the document until the symptoms looked less dramatic. But the difference followed the device and the printing boundary, not the sale record.
Investigating that problem meant treating printing as a compatibility and transport problem, not only a CSS problem. The goal was to preserve one usable document format across supported workstation and printer combinations, including the transaction verification details that belong on the receipt.
It was a useful reminder: operational software is only as reliable as the point where people encounter it. If the printer fails at the counter, the feature has failed. A beautiful dashboard cannot compensate for that.
Fresh data, without punishing the database
Real-time behaviour sounds straightforward until several screens are searching products, branches are updating prices, and new records are appearing during active transactions. Re-querying the entire catalogue every few seconds is an easy way to create a different problem. Caching every search forever creates another: stale operational data.
The later search work moved toward a server-backed, bounded lookup shared between transaction screens, with input debouncing, pagination, request cancellation, branch-aware results, versioned cache invalidation and lightweight change probes. A new or updated item can become visible to an already-open sales screen while the data-heavy queries stay constrained.
The distinction is important. The catalogue index may be cached to make finding a product fast, but a cached listing should not be the final authority for a live stock quantity or the price committed to a sale. The server still has to validate the transaction against current data.
client: search("paper", branch)
server: return bounded_matches + catalogue_version
later:
client: check_catalogue_version(branch)
if version_changed:
client: refresh_search_results()
on checkout:
server: revalidate_price_stock_and_permission()
Again, this is an illustration of the design principle rather than a reproduction of private project code. The priority is bounded work, fresh enough discovery, and authoritative writes. Performance is not just response time; it is keeping the software dependable as the record count grows.
Trust is an engineering decision
With multiple departments and branches, permissions cannot be an afterthought. A staff member should see and change only the records they are authorised to handle. The application needs to enforce those boundaries on the server, not just hide a navigation item. Sensitive operations need audit records, and high-consequence workflows need explicit approvals and controlled reversals.
Then come the less visible responsibilities: backups that can be restored, service outages that fail clearly, jobs that can retry without duplicating their effect, and errors that do not leave half-finished state in the database. I spent time on those because the consequences become physical when a business is relying on the platform.
There will always be more to harden. A production deployment is not a certificate of perfection. It is a different kind of accountability. Every real session exposes assumptions that a development fixture will never challenge.
What I am taking into the next system
I'm proud of Express Cloud, but I don't want to reduce this work to a number on a dashboard. What matters is that it became the place where a business can connect sales, stock, people and financial records without maintaining separate versions of the truth.
I'm now working on one of the most ambitious products I have attempted: a financial platform intended to bring banking and lifestyle into one experience. Our target is to process more than ₦1 billion in transactions during its first six months after launch. That is a target, not money processed or a promise of a launch outcome.
Financial products raise the stakes further: ledger correctness, provider reconciliation, security, access control, regulatory requirements, incident response, and the simple question of what happens after a timeout when a person thinks their money has moved. I hope what we're building holds firm when it matters most. The work is to engineer that confidence rather than assume it.
When I look back at where Express Cloud started, the lesson is not that African businesses should avoid international software. It is that good enterprise engineering starts by respecting how the business actually works. The technology should carry the operation, not ask the operation to become something else for the technology's convenience.
And for me, that is still the most interesting part of building software.
Sources and further reading
- Laravel: database transactions
- MySQL: InnoDB locking and concurrency
- OWASP: authorization guidance
- NIST: security log management
- HTTP Semantics: idempotency
- When Software Starts Carrying Real Consequences
- When the Printer Wasn't the Problem
- Your Business Software Is Not Enterprise Because It Has More Features

