WriteDaily Architecture Upgrade: Moving to Laravel with Gamification and Cached Sentiment Analysis
WriteDaily has been running on its original PHP codebase since 2013. The raw-PHP architecture served well for an MVP — it shipped fast and handled the initial user base without issues. The product has continued to serve writers throughout, quietly growing from 25,000 sessions when I last wrote about it in 2015 to over 60,000 today.
But six years of incremental features on a slim foundation have made further iteration slower than it should be. The framework migration I’ve been planning since 2015 is finally underway. Here’s what’s changing and why.
The Migration Plan
The key principle: keep the existing product running throughout. Users shouldn’t notice the transition. The old codebase handles production traffic while the new Laravel stack takes shape in parallel.
From Raw PHP to Laravel
The original WriteDaily had no ORM, no routing layer, no migrations, and no test framework. Every domain object — entries, users, pace data, sentiment results — was managed through hand-written SQL queries scattered across PHP files.
The Laravel stack introduces several improvements.
Every domain object gets a proper Eloquent model — Entry, User, Pace, Stats, LiwcDefinition, LiwcCategory, Badge, Condition, Advice, Notification — all mapped to migrations that track schema changes over time.
Clean URLs through Laravel’s router replace the ad-hoc switch statement in the original index.php.
Sentry provides proper authentication — registration, login, password reset, and role management. The original had a simple session-based system designed for a single user.
jqPlot, php-markdown, and the LIWC parser are now managed through Composer rather than vendored in manually.
Gamification Engine
The biggest feature addition is a badge and condition system. The concept draws from the gamification framework in PrestaShop, which I’ve been working with on client projects.
The architecture is declarative: a JSON configuration defines conditions (word counts, streaks, time-of-day patterns, distraction thresholds), operators (>, >=, <, <=, ==, !=), and target values. A cron-driven engine evaluates conditions hourly and awards badges when thresholds are met.
Example badges in the initial build:
| Badge | Condition |
|---|---|
| First Light | First 750-word entry completed |
| Steady Hand | 7 consecutive days of writing |
| Early Bird | 10 sessions completed before 8am |
| Deep Focus | 10 consecutive sessions with zero distractions |
| Silver Tongue | 50,000 total words written |
The system is extensible by design — new badges can be added by updating the JSON configuration without touching application code.
Cached Sentiment Analysis
In the original architecture, every page load that displayed sentiment charts triggered a fresh LIWC analysis pass. The LIWC2007 dictionary has 4,500 tokens, and parsing a 750-word entry against it takes around 80ms. For a single user, that’s fine. For a growing user base viewing historical entries, it adds up.
The new pipeline: LIWC analysis runs once when the entry is saved. Results are serialised into the entry record as a JSON array. Subsequent views read from the cached data. Analysis time drops to near-zero for repeat views.
The parser itself is now a standalone Composer library under the Ringalpha\Writedaily namespace, with the dictionary loaded as a file asset.
What Stays the Same
The user-facing experience doesn’t change. The 750-word daily target with real-time word counting, auto-save every 35 seconds, LIWC sentiment charts, pace-tracking graphs, and the streak calendar all work as before. The migration is an infrastructure upgrade, not a product redesign.
Timeline
The Laravel stack is functionally complete for the core writing loop — entries save, sentiment analysis runs, charts render. What’s in progress:
- Badge condition evaluation wiring
- Email notifications for streaks and badge unlocks
- Markdown and plain-text export
- Mobile-responsive editor
- Test coverage (Laravel’s PHPUnit integration makes this straightforward)
The old codebase handles production traffic until the migration is complete and tested. When the switch happens, existing user data migrates cleanly through Laravel’s seeding and migration tools.
If you’re curious about the architecture or want to follow the migration, writedaily.co has the current build. The code will be open-sourced once the migration stabilises.