Full-Stack Modernization of a Civic Technology Platform

From Legacy Codebase to Modern Civic Engagement Tool

At a Glance

🏛️
Civic Tech / Elections
Industry
🔄
Full-Stack Modernization
Engagement Type
⏱️
~5 Weeks
Delivery Timeline
🐳
Production-Ready
Containerized Deployment

The Challenge

WinMyVote helps U.S. citizens explore ballots, research candidates, and make informed voting decisions. After several years in production, the platform had slowed down:

  • Aging technology. The backend and frontend were built on outdated tools, making every change risky and slow.
  • Unreliable releases. Tests were failing, and deploying updates had become a manual, error-prone process.
  • Hard to add features. The old architecture made it difficult to build the new voter-facing experiences the product needed.

The goal: modernize the entire platform without pausing product development — new features had to ship alongside the overhaul.

Our Approach

We organized the engagement into four phases, executed over a continuous five-week sprint:

Phase 1 — Backend Platform Stabilization

The first priority was to get the backend running cleanly on a modern stack. In a single concentrated push, we:

  • Upgraded the runtime and framework. Migrated the application to the latest stable versions of Python and Django, ensuring long-term support and access to modern features.
  • Rewrote all dependencies. Overhauled the dependency manifest — upgrading, replacing, or removing dozens of packages to ensure compatibility with the modern runtime.
  • Eliminated legacy syntax. Systematically removed outdated language patterns across the entire codebase, bringing all source files in line with modern Python idioms.
  • Migrated framework internals. Updated middleware configuration, URL routing, model constraints, authentication adapters, and auto-field settings to align with current Django best practices.
  • Repaired migrations. Fixed incompatible artifacts in dozens of historical migration files and generated fresh migrations for every app after the upgrade.
  • Restored test stability. Fixed import and startup errors across all 15+ Django apps and got the test suite passing again.
  • Retired dead code. Disabled unused subsystems, cleaned up legacy settings files, and consolidated environment configuration.

Phase 2 — Frontend Rewrite

With the backend stable, we began building a brand-new frontend from scratch on a modern React stack:

  • Modern tooling: React with Vite for fast builds and hot module replacement, Tailwind CSS for utility-first styling, and accessible component libraries for composable UI.
  • Server-state management: TanStack React Query for efficient data fetching and caching, with lightweight client-state via Zustand.
  • Form handling and validation: React Hook Form paired with Zod schemas for type-safe, validated forms throughout the application.
  • Rich text editing: Tiptap-based WYSIWYG editor for candidate and group profile content.

The architecture follows a clean separation of concerns:

  • API layer — thin client modules per domain (ballots, candidates, comments, groups, propositions, search, trusted sources, polling places).
  • Custom hooks — reusable data-fetching and interaction logic that decouples UI from business rules.
  • Domain-organized components — UI components grouped by feature area (ballot, candidate, profile, groups, trusted sources, settings, layout, editor, media).
  • Shared utilities — avatar handling, date/time formatting, API error normalization, and profile editor logic.

This layered architecture means new features can be added by composing existing hooks and components rather than building from scratch each time.

Phase 3 — Feature Delivery

Running in parallel with the rewrite, we shipped a substantial set of new product capabilities:

Ballot Experience:
  • Full ballot layout with sidebar navigation and live ballot list
  • Candidate cards with improved styling and endorsement displays
  • Proposition display with dedicated detail pages
  • Ballot favorites with a printable "cheat sheet" for the polling booth
  • Voting flow — cast votes directly from the ballot view
  • Race-level comments with pagination and inline comment counts
Discovery and Engagement:
  • Full-text search with database-level indexes and a frontend search bar with debounced input
  • Trusted Sources ecosystem: follow/unfollow, trending sidebar module, dedicated browse page
  • Random Politician sidebar module for serendipitous candidate discovery
Profile and Account Management:
  • Comprehensive settings: email, personal info, location/districts, political party, password change, account deletion
  • Account type switching (voter / candidate / group)
  • Candidate profile pages with tabbed bio/issues/endorsements views
  • Rich profile editors with WYSIWYG, image cropping, and social links
  • Group pages with full editor support, reusing abstracted profile components
Civic Utility:
  • My Polling Place page with location-based lookup
  • Landing page with search-forward design, animated logo, and clear entry points
  • My Ballots and My Groups management pages

Phase 4 — Production Hardening

To prepare for production deployment, we added:

  • Production-optimized container builds with multi-stage Dockerfiles for minimal image size.
  • Nginx reverse proxy with production-tuned configuration and dedicated proxy parameters.
  • Orchestrated deployment via Docker Compose, coordinating the application server and reverse proxy.
  • Automated entrypoint and deployment scripts for repeatable, one-command updates.
  • Iterative build/deploy fixes to harden the entire pipeline.

Key Outcomes

  • Development velocity restored. The codebase runs cleanly on modern Python and Django. The test suite passes. Developers can iterate without fighting legacy breakage.
  • Modern frontend foundation. The new React architecture with hooks, query caching, and component composition allows new features to be shipped in hours rather than days.
  • Significant feature expansion. The platform went from a basic ballot viewer to a full civic engagement tool with search, social features (follows, comments, endorsements), profile editing, and polling place lookup — all delivered during the modernization window.
  • Production-ready infrastructure. Containerized deployment with Nginx reverse proxy, production-optimized builds, and a repeatable update workflow.

Technology Stack

Python Django Django REST Framework django-allauth Gunicorn React Vite Tailwind CSS shadcn/ui Radix UI TanStack React Query React Router Zustand React Hook Form Zod Tiptap Docker Docker Compose Nginx

Ready to Modernize Your Platform?

Whether you're dealing with technical debt holding back your product or need to rapidly ship new features on a legacy codebase, HexOcean delivers modernization without disruption. Let's discuss how we can help you move forward.

Speak to a Human Professional

Get a free consultation within 24 hours