CEChibuzor Ezeamaku

A document engine that replaced hand-coding.

Every customer of the platform wanted their reports to look like their reports. For years that meant writing code for each one. I replaced that with an engine, so a new report became a template instead of a project.

RoleDesigner and principal author, concept to production
When2023 to 2026
StackC# / .NET, Word and Excel object models, PDF
ContextA construction-technology SaaS company. Customers unnamed.
95%+less custom code for a new client deliverable
150existing report generators migrated onto it, in stages
14,111lines in the engine, almost all of them mine

The problem

The platform serves municipalities and engineering consultancies. Their inspection reports, payment certificates and progress summaries are contractual documents, and each organization has its own format. The old approach was one generator class per client report. Every new customer meant new code, new bugs and one more thing to maintain forever.

I had written dozens of those generators myself. That is how I knew which parts were always the same.

What I built

  • Templates carry the structure. A Word or Excel file, authored like a normal document, holds the layout and a set of placeholder patterns.
  • A small template language of fifteen placeholder patterns, resolved against live data in a fixed, repeatable order, including repeating rows and tables that grow with the data.
  • An adapter layer puts Word and Excel behind one interface, so the same logic fills either format, with PDF export on top.
  • Read-only data models replaced a legacy path that wrote to the database while building a report. Generating a document can no longer change anything.
  • An image pipeline that respects DPI, EXIF orientation and modern phone formats, so site photos print at the size and angle they were taken.

Migrating a live system

There was no moment when everything switched over. The engine grew beside the old path for two years. It started as one method, became a set of focused modules, and took over report by report. Only then was the old monolith deleted, nearly fifteen thousand lines in one change, with the callers already moved and verified.

A new report type is now a matter of authoring a template, not writing and maintaining another one-off codebase.

Proof, not adjectives

One customer’s weekly report had started timing out. A quick CPU check showed the process was waiting on the database, not computing. The cause was a nested lookup that ran thousands of queries per report. With the lookups cached, the report went from over 16 minutes to 32 seconds. The output was diffed cell by cell against the old version: 21,241 cells, zero differences. Every other caller of the changed code was audited, and one hidden bug found along the way was fixed too.