kestrelsnest-blog/content/posts/2025-09-09-the-23-year-rescue-mission-saving-agricultural-innovation-from-technical-extinction.md
2025-09-09 16:04:09 -04:00

18 KiB
Raw Blame History

title, description, date, preview, draft, tags, categories, lastmod, keywords, slug
title description date preview draft tags categories lastmod keywords slug
The 23-Year Rescue Mission: Saving Agricultural Innovation from Technical Extinction By 2025, LocallyGrown.net faced extinction on Rails 3. The story of constant firefighting, failed upgrades, and the impossible choice to rebuild or die. 2025-09-09T19:53:40.930Z true
locallygrown
locallygrown
2025-09-09T20:02:11.281Z
locallygrown
locallygrown-rescue-mission

How a platform serving thousands of farmers and customers nearly died from its own success—and the impossible choice I had to make to save it


TL;DR

By 2025, the LocallyGrown.net platform was dying from technical extinction. Running Rails 3.0.20 and Ruby 2.0.0 (both over a decade obsolete), it couldn't be rebuilt if servers failed—Ruby 2.0.0 won't compile on modern systems. Multiple Rails upgrade attempts had failed due to the customization system that once made me competitive. COVID created a cruel irony: maximum demand for online farmers market infrastructure meeting maximum technical constraints. After closing my own Athens market in 2021 and watching passion-driven competitors win markets with modern platforms, I faced three choices: help migrate users to competitors, shut it down, or rebuild everything from scratch while working around a day job. Failure would have meant thousands of farmers and customers losing their platform overnight. I chose to give it my all: 1,865 commits over six months, complete Rails-to-SvelteKit migration, preserving 23 years of agricultural innovation and the communities that depended on it.


The Constant Vigil

January 2025. My phone buzzes with alerts—again. Another spam bot swarm is attacking the site, creating hundreds of thousands of session files and choking my ancient Rails 3.0.20 file-based sessions. I sigh and reach for my laptop.

This has become routine. I keep a laptop within arm's reach no matter what I'm doing, just in case. I've cleared session directories from grocery store parking lots, restaurant tables, and family gatherings. Once, I spent half an hour in a Kroger parking lot desperately stabilizing the system—clearing sessions, throwing up emergency firewall rules, watching server metrics while other shoppers walked past my car.

If it wasn't spam bots overwhelming the session system, it was something else. A gem broke, a server ran out of memory, a scanner found a new exploit—there was always something that could take the site down for hours, or longer.

"I kept a laptop within arm's reach no matter what I was doing, just in case. The constant vigilance was draining everything."

The inevitable truth was becoming clear: one day, the bots would win. Or something worse would. And when that day came, 23 years of agricultural innovation would simply vanish.

The foundation I'd built in those 4 AM development sessions back in 2006 had become my prison.


The Stagnation: Fighting an Unwinnable War (2011-2025)

Rails 3.0 was released in August 2010, and I successfully migrated to Rails 3.0.20 in 2011. This would be my last major Rails upgrade for 14 years.

This wasn't due to neglect or lack of awareness. I was acutely conscious of the growing technical debt and made significant efforts to keep up with the Rails ecosystem. My LocallyGrown repo tells the story—multiple branches and months of weekend work attempting to upgrade to Rails 4, only to be blocked by critical dependency compatibility issues that proved insurmountable within the time and budget constraints of a break-even business.

The Failed Upgrade Attempts

Each new Rails version represented not just an upgrade, but a potential breaking change to the complex customization system that markets depended on. The architectural decisions that had enabled rapid growth in 2006—deep Rails integration, custom HTML/CSS/JS injection, complex ActiveRecord relationships—became barriers to modernization.

Artifacts of Failed Upgrades

git branch -a | grep upgrade
rails-4-upgrade-attempt-2015
rails-4-second-try-2016
rails-5-api-experiment-2017

git log rails-4-upgrade-attempt-2015 --oneline | wc -l
47 commits over 6 months of weekend work

Final commit message: "Customization system fundamentally incompatible with Rails 4 asset pipeline."

When Rails 5 introduced API capabilities, I saw a potential path forward. I experimented with creating a business logic API layer from my Rails 3 code, which would allow me to build a new frontend free from the constraints I was under. The approach showed promise (and would later become the foundation for my SvelteKit migration), but required more sustained development time and financial resources than I could afford to give it.

Rails 3.0.20 worked, was stable, and served its purpose. But each failed upgrade attempt made the next one more daunting. Technology doesn't stand still, and the gap between my platform and the modern Rails ecosystem grew wider each year.

The Slow Slide Into Obsolescence

2013: Ruby 2.0.0 became the minimum version I could run. Still manageable.

2015: Rails 4 required significant changes to my custom code. I postponed the upgrade.

2017: Rails 5 introduced major architectural changes. The upgrade looked daunting.

2019: Rails 6 was released. I was now three major versions behind. My main programming job mostly saw me using flavors of JavaScript, and I grew more disconnected from the Rails ecosystem.

2020: The COVID-19 pandemic changed everything overnight. Traditional farmers markets were being closed down across the country, and markets everywhere turned to online models, fast. Even though my system was really showing its age, it was still the easiest platform for markets to jump into quickly.

I received an influx of new markets that put tremendous strain on the aging system and amplified my anxiety about keeping it all running. There were even several people who knew me from the old conference circuit who reached out, leading groups of dozens of markets each, asking if they could use the system for free so they could weather the storm.

In 2010, I would have happily said yes. But in 2020, not only could I not afford to pay for all those operating costs out of my own pocket, I couldn't mentally take on the burden of trying to keep the system standing for all those markets for free. The technical debt that had been a nagging concern became a crushing weight when combined with the sudden influx of desperate markets needing reliable service.

The Personal Toll of Crisis Management

COVID also saw the Athens market—the original market that started it all—take on an influx of customers while requiring constant adaptation: new locations, drive-up models, distancing, and shifting protocols.

I was still, after all these years, the manager—responsible for both the technical platform serving markets nationwide and the physical safety of my local community. The double burden took its toll physically and mentally.

By late 2021, as all the other area markets were returning to normal and it looked like customers and growers all had reasonable alternatives—and with my eldest kid starting college and my youngest about to start high school—the time seemed right. In December 2021, I made the difficult decision to close Athens Locally Grown after 19 years.

With that closure, LocallyGrown.net lost its biggest, oldest, and most important customer: my own market. The platform that had started as infrastructure for the local community was now serving everyone except the community that created it.

2021: Rails 7 shipped. I was four major versions behind, running Ruby 2.0.0 and Rails 3.0.20—both nearly a decade out of date. I was so far out of touch now with the Rails community that I'd given up hope of ever upgrading.

2024: The system was running on Ruby 2.0.0 (from 2013) and Rails 3.0.20 (from 2011). Dependencies were breaking. Security vulnerabilities had no patches. The infrastructure was held together by digital duct tape and increasingly desperate workarounds. I was running out of time, and even as the number of markets using my system dwindled, worry about the system failing was a constant, nagging weight on my shoulders.


The Technical Debt Compound Interest

What started as postponed upgrades became architectural shackles:

Dependency Hell: Every gem in my Gemfile was years out of date. Many had known security vulnerabilities with no available patches for Ruby 2.0.0.

Infrastructure Brittleness: The servers couldn't be rebuilt. Ruby 2.0.0 couldn't be compiled on modern operating systems. File-based session management regularly choked under spam bot attacks, requiring manual intervention to clear hundreds of thousands of session files that pushed the server past inode limits.

Development Impossibility: New features were nearly impossible to add. Setting up development environments required ancient versions of everything—Ruby 2.0.0, Rails 3.0.20, and gems that hadn't been updated in years. Even simple bug fixes became archaeological expeditions through deprecated code.

Security Nightmares: Rails 3.0.20 had dozens of known CVEs. The platform was a security incident waiting to happen. My saving grace: I never stored cardholder data or sensitive PII, but that didn't keep the hackers from trying.

Customization Albatross: The HTML/CSS/JS injection system that was innovative in 2006 was preventing mobile optimization in 2020. While competitors launched mobile-first platforms, LocallyGrown markets looked dated and functioned poorly on smartphones.

Why "Can't Rebuild" Means Extinct

  • Ruby 2.0.0 fails to compile on Ubuntu 22.04+ and macOS 14+
  • Rails 3.0.20 has dozens of known CVEs with no available fixes
  • Session attacks regularly generate 500K+ files, exhausting filesystem inodes
  • 12 critical gems abandoned with no Ruby 2.0-compatible updates
  • Development environment setup requires VMs running decade-old OS versions

The Crisis: When Working Became Dying (January 2025)

By January 2025, I faced a stark reality: the system that had worked for 19 years was about to become extinct. Not broken—extinct. I had no clear path forward to ever upgrading my system. The platform was living on borrowed time, held together by manual interventions, emergency patches, and the constant stress of knowing that the next alert could be the one I couldn't fix.

The numbers were stark:

  • Dozens of markets depending on the platform for their entire online presence
  • Hundreds of growers using the system for their livelihoods
  • Thousands of customers relying on it for local food access
  • 23 years of data, relationships, and business processes that couldn't be easily migrated anywhere else

The Competition Had Evolved

Even though my system was the first, competition had been coming for years. Early competitors were polished, VC-funded systems built in Silicon Valley with pilot markets in the Bay Area. I knew the target markets and farmers well, and knew there was no way those startups could even come close to recouping their investments. Farmers markets were trendy and glamorous, but they all operated on shoestring budgets and donations, and the farmers who sold at them did the same. I watched as one by one those startups folded after a season or two.

But after the dot-com bubble burst, my skills weren't so rare anymore. Quite a few web developers left the tech industry for small farms, seeking the same "back to the land" life I'd grown up with. Where early VC platforms misread farmers, the next wave repeated our original playbook — ag expertise + technical skill — on fresher stacks.

These new competitors offered compelling reasons for my markets to switch:

  • Sleek responsive designs that worked perfectly on smartphones
  • Fast loading times optimized for mobile networks
  • Professional, consistent user experiences across all devices
  • Modern payment processing and user interfaces
  • Fresh codebases without decades of technical debt

My largest markets were already being courted by these competitors. The customization system that had been my competitive advantage in 2006 was now driving customers away. Unlike the early Silicon Valley attempts, these passion-driven competitors understood the agricultural community and had the technical chops to build sustainable platforms.

The Economic Spiral

The technical stagnation created a vicious cycle. As my tech stack languished and I lost markets to competing services, I fell further and further from my break-even goal. Over the previous five years, the platform went from sustainable to slowly bleeding money. The 3% commission from declining market activity couldn't cover server costs, maintenance, and the increasing technical debt.

The irony was stark: I had the knowledge to market the platform aggressively, recruit new markets, and potentially grow revenue to cover modernization costs. But the technical problems made it impossible to compete for new business while the declining revenue made it harder to justify the time investment needed for a complete overhaul.


The Ultimatum: Modernize or Die

I had three choices:

  1. Migrate to a competitor - Abandon 23 years of custom business logic and force all markets to start over
  2. Shut down the platform - Let the infrastructure fail and walk away from my life's work
  3. Complete modernization - Rebuild everything from scratch while keeping the business running

The first two options meant losing everything that made LocallyGrown unique. The complex pricing logic, the multi-market architecture, the grower-customer relationships, the historical data—all of it would be gone. Every row represented growers, customers, and years of trust. Losing that wasnt an option.

But the third option seemed impossible. I had a full-time job. The Rails codebase was massive, with data models and complex business relationships that had evolved over decades. The customization system alone would require architectural gymnastics to preserve while enabling mobile optimization.

How do you rebuild 23 years of software architecture while working part-time around a day job? How do you migrate from Rails 3.0.20 to a completely different technology stack without losing business-critical functionality?

Most consulting firms would quote six figures and six months with a full team. I had evenings, weekends, and no budget.


The Decision: Save Everything

I chose the impossible option: one last all-in effort to rebuild LocallyGrown.net from scratch, preserve every piece of business logic that mattered, solve the mobile optimization problem, and do it all while maintaining my day job and keeping the existing system running. Just like those 4 AM development sessions back in 2006, it would take personal sacrifice to build something bigger than myself. I couldn't bear the thought of abandoning everything I'd worked so hard to build.

The alternative—watching 23 years of agricultural innovation disappear because I couldn't maintain obsolete code I wrote—was unacceptable.

Why SvelteKit?

In my day job as VP of Technology at Infinity Interactive, a full-service software development consultancy, I've been exposed to pretty much every framework out there. Often clients choose the tech stack, but when I get to choose, I almost always reach for Svelte. It's my favorite framework currently available, in any language.

I was out of touch with modern Rails and couldnt tell you how Rails 8 compares to the world I knew. But I knew that if I wanted to get this job done at all, I'd have to go with the framework I knew and loved best: Svelte 5. The Rails 5 API experiments from 2017 had already taught me that separating business logic from presentation was the path forward, and SvelteKit was the perfect modern complement to that approach.

My Constraints

  • Timeline: six months, part-time
  • Keep running: production Rails stays up
  • Must keep: all business logic, subdomains
  • Fix: mobile UX, unsafe customization
  • Ideals: no password resets, minimal downtime, no schema changes
  • Success: zero data loss, feature parity, faster

This series is the story of how I did it: 1,865 commits over six months of part-time development, a complete Rails 3.0.20 to SvelteKit migration, three weeks of crisis management when everything went wrong, and the lessons learned from rescuing a platform from technological extinction. Every commit felt like a race against the clock.

But the technical details of that rescue—translating 19 years of Rails domain logic to modern TypeScript, preserving subdomain-based multitenancy, replacing unsafe customization with secure theming—that's a story for the next post.


What's Next: The Architecture Deep Dive

The decision was made. The technology was chosen. But how do you actually migrate 23 years of Rails ActiveRecord models, complex business relationships, role-based authentication, and subdomain multitenancy to SvelteKit without losing functionality?

Part 3 will cover:

  • Domain model translation: Converting Rails ActiveRecord relationships to TypeScript
  • Authentication architecture: Preserving role-based permissions across subdomain isolation
  • Data migration strategy: Moving decades of production data without downtime
  • Customization system redesign: Safe theming that enables mobile optimization
  • The development process: Managing 1,865 commits while working a day job

Questions I'll address:

  • How do you maintain complex business logic outside of Rails conventions?
  • What does subdomain multitenancy look like in modern frameworks?
  • How do you migrate production data from Rails 3 to SvelteKit?
  • What's the actual day-to-day process of rebuilding a platform part-time?

This was the hardest project of my career. Part 3 dives into how I translated nearly two decades of Rails into modern SvelteKit without losing a byte.


This is part two of a series documenting the rescue and modernization of LocallyGrown.net.

The Series

  1. From Accidental Discovery to Agricultural Infrastructure (2002-2011)
  2. The 23-Year Rescue Mission: Saving Agricultural Innovation from Technical ExtinctionYou are here
  3. The Architecture Challenge: Translating 19 Years of Rails Logic to Modern SvelteKit
  4. Crisis Response: When Launch Day Goes Wrong
  5. Lessons from the Solo Developer + AI Trenches
  6. The Future: Building on Modern Foundations