Croptune — Agri-Tech Backend
Owning the central API of a plant-nitrogen-estimation platform — and re-architecting a fragile codebase without stopping feature work.
A fragile codebase under a growing product
The platform estimates plant nitrogen from photos — real science, real customers. But the API underneath had grown tangled: implicit globals, untestable handlers, changes in one corner breaking another.
Placeholder: describe the moment that made the rebuild non-optional.
Strangle the old design, one module at a time
Rather than a risky big-bang rewrite, I introduced a dependency-injection container and migrated module by module — each migrated unit gaining tests and explicit dependencies while the rest kept shipping.
Placeholder: name the DI approach/library and a concrete migration example.
1// before: handler reaches into globals 2app.post('/analyze', async (req, res) => { 3 const result = await analyze(req.body, db, mlClient); 4}); 5 6// after: dependencies are explicit and swappable 7container.register('analysisService', AnalysisService, 8 ['photoStore', 'mlClient', 'cropRepo']); 9router.post('/analyze', inject('analysisService')); One API, clean seams, two clients
The central Express API serves the farmer mobile app and the Vue.js admin panel, with MongoDB underneath and the ML estimation service behind a clean interface — swappable without touching business logic.
Maintainable foundations, uninterrupted roadmap
The rebuild landed without stalling feature delivery, and the codebase went from fragile to testable. Placeholder: add bug-rate change, onboarding time for new devs, or shipped-feature count.