Document control platform
The system the retrieval layer reads from. Multi-stage review across engineering disciplines, versioned deliverables, and five kinds of user who each needed a different answer to the same question.
The problem
Engineering deliverables move through review before they go anywhere. Multiple disciplines check them, comments come back, revisions are issued, and the cycle repeats until the document is approved and released.
Before the platform, that process lived across spreadsheets, shared folders and email. Each of those is fine on its own and useless in combination: there was no single answer to "where is this document right now", and every status question became a person asking another person.
At 1,500 concurrent projects, that does not scale. It also has no memory — once a project closes, the reasoning behind its review history is gone.
What I built
A React front end against an ASP.NET Core REST API, authenticating against Active Directory, with MSSQL behind an availability group listener. Anything slow — text extraction, report generation, external synchronisation, notification — runs as a background job rather than on a request thread.
Decisions that mattered
The API owns writes; jobs own everything slow. Document processing is unpredictable — a large drawing set takes as long as it takes. Keeping that work off request threads means a heavy upload never degrades the experience for everyone else. It also makes retries a property of the job system rather than something each feature reimplements.
Versions are first-class, not a filename convention. 125,000 documents carry 350,000 versions between them. Superseding is an explicit operation with its own state and notification rules, because "which revision is current" is the single most consequential question the system answers. Encoding that in filenames — the way most organisations start — is how you end up with three documents that all claim to be Rev C.
Approval routing is rule-driven, and skips what it can. The review chain is multi-stage and role-aware, and stages that a given document does not require are not manufactured just to be signed off. The rules are configuration rather than code, which is what lets the process change without a release.
Five roles, five dashboards. Project engineers, lead discipline engineers, discipline engineers, contract managers and department managers all want to know "what needs me today," and the honest answer is different for each. Rather than one generic screen everyone filters by hand, each role gets its own view. That work collapsed a four-screen navigation path into a single actionable home view and cut document status lookup time by roughly 60% — and unlike most dashboard projects, all five roles actually adopted theirs.
Permissions reload without a deployment. Access changes are a daily occurrence and used to mean a redeploy. Making permission state reloadable at runtime removed an entire category of after-hours release.
Reports were made fast rather than made asynchronous. Key reports ran over 60 seconds; tuning stored procedures and indexes brought them under 5. The tempting fix is to move a slow report to a background job and email the result, which hides the problem and makes it permanent. It was a query problem, so it got a query fix.
Leading the work
I lead a team of three on this, owning architecture, code review and release planning. Bringing the team onto Git branching standards and pull-request-based delivery produced 90+ merged pull requests in the first year, and moved review from something that happened after a problem to something that happened before one. I've mentored 10+ junior developers and interns and led hiring for the current team.
The part I'd defend hardest is the boring part: design-review sessions with the people who actually use the system, held before the build rather than after. Most of what makes this platform fit the work came out of those conversations, not out of the architecture.