DEV Community

Ahad Nawaz
Ahad Nawaz

Posted on

Building a Cloud ERP from Zero: What I Learned Shipping to 15 Companies

When I started building UPVC Cloud, I had no idea it would end up processing billions in transactions across 15 companies.

I thought it was a standard enterprise project. It turned out to be a two year lesson in what actually makes production software survive real business conditions.

This is that lesson.

What Is UPVC Cloud

UPVC Cloud is a full scale cloud ERP built specifically for Pakistan's UPVC manufacturing and construction industry. It handles inventory, production orders, sales pipelines, customer management, invoicing, financial reporting, and HR workflows, all in one system.

I built it from scratch at BuildSpark starting in late 2023. It is now live across 15 plus companies and processing a volume of financial transactions that I could not have predicted when I wrote the first line of code.

The Stack and Why It Works

The tech stack is Angular on the frontend and Node.js with NestJS on the backend, backed by PostgreSQL. Everything runs containerized on AWS.

Angular gets criticism online but for an ERP with dozens of complex forms, data tables, nested workflows, and role based access, its structure is an asset not a liability. You do not want a flexible framework when you are building something this large. You want opinions.

NestJS gives you the same thing on the backend. Modules, guards, interceptors, dependency injection. It reads like a Spring application but runs on Node. For enterprise software with strict separation of concerns, it is the right tool.

PostgreSQL was never a question. ERP data is relational by nature. Inventory levels depend on production orders which depend on raw material purchases which connect to supplier accounts. You need a relational database with proper constraints, transactions, and joins. NoSQL would have been a disaster here.

The Mistakes I Made in Month One

I underestimated the domain complexity.

ERP systems look simple from the outside. You read requirements, you build forms, you store data. But the relationships between modules run deep. A change in how inventory is tracked ripples through production planning, which changes how invoices get generated, which affects financial reports.

I built the inventory module first, got it working, then started the production module and realized my data model was wrong. I had to refactor two weeks of work because I had not mapped out the full entity relationship diagram before writing code.

The lesson: for any system with more than five interconnected modules, spend a full week on data modeling before you touch code. The ERD is not documentation. It is architecture.

Multi Tenant Architecture From Day One

One of the best decisions I made early was designing for multi tenancy from the start. Each company on the platform has its own data namespace, its own user roles, its own configuration. They share the same codebase and infrastructure but are completely isolated from each other.

This is how you build SaaS. You do not build a single company system and then try to retrofit multi tenancy later. That retrofit will cost you more time than building it right the first time.

In practice this meant every database query is scoped by a tenant ID. Every API route goes through a tenant resolution middleware. Every user session carries tenant context. It adds some initial complexity but removes an enormous amount of pain later.

Role Based Access Control at Scale

UPVC Cloud has five distinct user roles: super admin, company admin, sales manager, production staff, and viewer. Each role has different access to different modules, different permissions within those modules, and different views of the same data.

I implemented RBAC using a permissions matrix stored in the database, not hardcoded in the application. This means adding a new role or adjusting permissions for an existing one is a configuration change, not a code deployment.

If you are building any enterprise application, build your permission system this way from day one. Hardcoded role checks in your route handlers will eventually create a maintenance nightmare.

The Performance Problems That Hit in Production

Three months after launch, one company started generating significantly more data than the others. Suddenly reports that loaded in under a second were timing out.

The culprit was a financial summary query that joined six tables without proper indexes. In development with small datasets it was fine. In production with hundreds of thousands of records across two years of operations, it was catastrophic.

The fix required adding composite indexes on the most common join and filter columns, rewriting two queries to use CTEs instead of nested subqueries, and adding a caching layer for reports that did not need real time data.

The deeper lesson: always seed your development database with realistic data volumes before you call a feature production ready. Testing with ten rows tells you nothing about how it behaves with a hundred thousand.

What 15 Live Deployments Actually Means

Getting your software into one company is a milestone. Getting it into fifteen means you have solved a different set of problems.

You have solved onboarding. Each new company needs to import their existing data, configure their chart of accounts, set up their users, and get trained. I built an admin onboarding flow that guides this process and reduced the average time from signup to first active use from three days to four hours.

You have solved support. When something breaks in production you need to be able to diagnose it quickly without accessing sensitive company data. I built a logging and audit trail system that captures every state change in the system with timestamps and user attribution. Most support tickets get resolved in under fifteen minutes because the answer is in the logs.

You have solved update management. Deploying a new version cannot break existing companies. I run database migrations with rollback support and use feature flags to roll out changes incrementally.

The Things I Would Do Differently

I would write integration tests for the cross module workflows earlier. Unit tests are not enough for an ERP. The bugs that matter are the ones where module A and module B interact in an unexpected way. Those only surface with integration tests.

I would invest in better developer tooling from the start. Seeding scripts, local environment parity with production, and a proper staging environment would have caught several issues before they reached live users.

I would document the data model as I built it, not after. The ERD exists now but it took effort to reconstruct it accurately because I did not maintain it during active development.

What This Project Taught Me About Building Software

Enterprise software is not glamorous. It does not get on Product Hunt. Nobody writes Twitter threads about ERP systems. But it is the category of software that businesses depend on daily, that processes real money, and that has zero tolerance for errors.

Building it well is a craft. And shipping it to fifteen companies that trust it to run their businesses is more validating than any launch tweet.

If you are building something in this space, focus on data integrity above everything else. An ERP that loses data or corrupts records is not just broken software. It is a destroyed business relationship.


I am Ahad, Founder of REIVEX Technologies and a full stack engineer with 5 plus years shipping production systems. Visit ahadnawaz.dev to see more of what I have built.

Top comments (0)