LimeFlight, a Swiss company providing inflight catering management and logistics software for airlines, engaged me through Toptal for a focused one-month performance investigation. Their main interface was taking 12 to 30 seconds to load - clearly unacceptable for operational use.
The engagement was purely technical: investigate the performance issues, identify hotpaths, implement quick wins, and document a roadmap for improvements beyond my engagement. I joined a team of five developers, working around 20 hours per week alongside my other commitments.
The root cause was a classic pattern I've seen in legacy systems: inefficient database interactions reminiscent of early Entity Framework days, where developers didn't appreciate that each lazy-loaded property access triggered a database call. The codebase was a modular monolith using a service-repository pattern with some CQRS elements (separate read and write repositories), but heavy inheritance dependencies made navigation difficult.
The database issues ran deep. One critical table had grown exponentially large - tens of millions of rows over just four years - because they'd implemented a custom versioning system storing start and end dates for every record change, similar to temporal tables but baked into the main tables. This versioning data spread across three to five primary tables, and the query patterns required joining them constantly.
I investigated SQL partitioning on the problematic tables, which showed dramatic improvements in isolation. However, the ripple effects made it impractical - the partitioning would need to cascade across related tables that didn't support the same date-range patterns. I also recommended migrating to proper SQL temporal tables, but that too would require significant restructuring.
What I could deliver was a caching abstraction layer. I implemented a memory cache with an interface designed for future extension to Redis or .NET's hybrid cache (they were on .NET 6, pre-dating the hybrid cache feature). This brought load times down from 12-30 seconds to 2-3 seconds - not perfect, but a dramatic improvement that made the system usable while they evaluated the larger architectural changes.
The roadmap I left them included: rolling out the caching pattern to other areas using my documentation and examples, evaluating database partitioning feasibility, and considering temporal table migration. The deeper database issues were flagged for leadership discussion after my departure.