Upkaarr tms
A logistics platform for a company that ran on notebooks




industry
Road Transport & Freight
Role
Full-Stack Developer
Timeline
Multi-month build with iterative feature delivery
Platform
Web (React SPA + Go API + PostgreSQL)
Replaced a paper-based freight operation — five documents, four departments, multiple branches — with a single digital system of record.
An operator hand-wrote a receipt when goods arrived. It sat in a pile until someone consolidated it with others onto a truck — another hand-written form. The truck left. At the other end, someone opened the doors, counted packages, and scribbled notes. A gate pass was written for pickup. Payment went in a ledger notebook.
Every handoff was paper, across branches that couldn't see each other's documents. Nobody could answer "what does this client owe us" without opening the notebook and adding by hand. Numbers collided. Documents got lost. Nobody knew what was in transit until a driver called to say he'd arrived.
Two constraints shaped everything that came after:
The interface had to look like the paper it replaced. Operators had never used software for work — if the screen didn't match the form they already knew, adoption would fail on day one.
The money had to be exact, always. Freight charges, tolls, GST, lorry hire — real rupees across branches, with zero tolerance for a rounding error becoming a client dispute weeks later.
The parts that required judgment, not just implementation.
Whole rupees, stored as integers. Floating-point math compounds errors invisibly across hundreds of transactions. The business never needed paisa-level precision, so the fix removed the entire bug class structurally — every monetary field is a BIGINT, no floats anywhere in the system.
Document numbers generated by the database, not the app. Two operators at two branches booking a consignment in the same second will eventually collide under MAX(number) + 1. Numbering moved to PostgreSQL sequences — atomic, collision-proof, no application logic in the critical path.
A ledger that's a journal plus a cache, not a live calculation. The first version summed full transaction history on every balance query — correct, but slower as the business grew, which is the wrong direction. The fix: an immutable journal as source of truth, with a balance cache updated in the same database transaction as every journal entry. That detail is the whole decision — update the cache separately and it drifts silently. Tying it to the transaction makes drift structurally impossible, not just unlikely.
Branch isolation — every query and permission scoped to a branch — was the right call, but I underestimated how much it would touch. It's not a feature, it's a constraint every handler has to respect forever. Next build, that gets designed as a first-class layer on day one, not layered in as the schema grew.
The hardest afternoon was cross-origin auth: cookies set on one domain aren't readable by JS on another when frontend and API live on different hosts. HttpOnly cookies plus a CSRF token echoed on every authenticated response solved it, after three wrong attempts. Worth knowing going in.
More projects, one message away.
Upkaar TMS wasn’t built to showcase technology. It was built to remove friction from a business that runs on movement, paperwork, and trust. The challenge was less about software and more about translating a real-world operation into a system people could depend on every day.




