Terriyaki: Transforming Coding Practice Through Social Accountability and AI
What Inspired Us
As computer science students preparing for technical interviews, we've all experienced the same struggle: maintaining consistency with LeetCode practice. We'd start with enthusiasm, solve a few problems, and then... life happens. Motivation fades, accountability disappears, and we find ourselves weeks later wondering where our coding practice went.
The problem isn't a lack of resources—LeetCode has thousands of problems. The problem is human psychology. We needed something that combines:
- Social accountability: Knowing your friends are counting on you creates real pressure
- Gamification: Turning practice into a game with stakes and progress tracking
- Automation: Removing friction so you can focus on coding, not tracking
- Real interview prep: Beyond solving problems, you need to explain your solutions
This is how Terriyaki was born—a platform that transforms the solitary grind into a collaborative, engaging experience.
What We Learned
Building Terriyaki was a crash course in full-stack development, API integrations, and solving real-world technical challenges. Here's what we discovered:
Full-Stack Architecture
We learned to design a clean separation between frontend and backend:
- Backend (Go + Gin): RESTful API with PostgreSQL, handling authentication, task management, and real-time progress tracking
- Frontend (Next.js + TypeScript): Modern React with Material-UI, Zustand for state management, and Framer Motion for smooth animations
- Chrome Extension: Our first foray into browser extension development, learning about content scripts, background workers, and cross-origin communication
AI Integration Challenges
Integrating ElevenLabs (voice AI) and Google Gemini (LLM) taught us about:
- Webhook architecture: ElevenLabs needed to call back to our backend, requiring ngrok for local development
- Conversation state management: Maintaining interview context across multiple API calls
- Real-time voice streaming: Handling audio streams and transcription in a web environment
- Prompt engineering: Crafting system prompts that guide AI interviewers to ask relevant technical questions
Browser Extension Development
Building the Chrome extension was particularly educational:
- DOM monitoring: Using MutationObserver to detect LeetCode submission success states
- Cross-origin communication: Securely passing data between content scripts, background workers, and the popup
- Badge API: Creating dynamic countdown timers on the extension icon
- Token synchronization: Automatically importing authentication tokens from browser cookies
Database Design
We learned to model complex relationships:
- Many-to-many relationships: Users participating in multiple grinds
- Temporal data: Daily tasks tied to specific dates with completion tracking
- JSON storage: Storing conversation history and problem metadata flexibly using PostgreSQL's JSONB
How We Built It
Architecture Overview
Terriyaki follows a microservices-inspired architecture with three main components:
┌─────────────────┐ ┌──────────────┐ ┌─────────────┐
│ Next.js Frontend│────▶│ Go Backend │────▶│ PostgreSQL │
│ (React + TS) │ │ (Gin API) │ │ Database │
└─────────────────┘ └──────────────┘ └─────────────┘
│ │
│ │
▼ ▼
┌─────────────────┐ ┌──────────────┐
│ Chrome Extension│ │ ElevenLabs │
│ (Content Script)│ │ + Gemini │
└─────────────────┘ └──────────────┘
Backend Implementation
Technology Stack:
- Go 1.25.3 with Gin framework for the REST API
- PostgreSQL 15 with GORM for ORM
- JWT for authentication
- CORS middleware for cross-origin requests
Key Features:
- Grind Management: Users create "grinds" (commitment periods) with duration, budget, and participants
- Task System: Daily LeetCode problems assigned to each participant, with lazy loading from LeetCode API
- Progress Tracking: Real-time completion statistics and leaderboards
- Interview Sessions: Webhook endpoints for ElevenLabs to store conversation history
Notable Implementation Details:
- We used GORM migrations to manage database schema evolution
- Implemented a participate_records join table to track user-grind relationships with additional metadata (quit status, completion rates)
- Created a LeetCode service that parses HTML problem descriptions, extracts examples and constraints using regex and DOM traversal
- Built a NeetCode 250 CSV parser to curate high-quality problem lists
Frontend Implementation
Technology Stack:
- Next.js 15 with App Router
- TypeScript for type safety
- Material-UI v7 for components
- Zustand for global state management
- Framer Motion for animations
- Axios for API calls
Key Features:
- Landing Page: Animated introduction with feature grid
- Grind Dashboard: Real-time progress visualization, today's task display, participant leaderboard
- Interview Interface: Voice-powered interview practice using ElevenLabs React SDK
- Message System: Invitation management and notifications
Notable Implementation Details:
- Used server-side token verification on page load to handle authentication
- Implemented optimistic UI updates for better user experience
- Created custom hooks for API calls with error handling
- Built responsive design that works on mobile and desktop
Chrome Extension
Architecture:
- Manifest V3 for modern Chrome extension standards
- Content Script (
content.js): Injected into LeetCode pages to monitor submissions - Background Worker (
background.js): Handles API calls and badge updates - Popup (
popup.html/js): User interface for viewing tasks and stats
Key Features:
- Auto-Detection: Monitors DOM for LeetCode success indicators using MutationObserver
- Code Extraction: Parses submitted code and language from LeetCode's editor
- Auto-Submission: Sends solution to backend automatically when problem is solved
- Badge Countdown: Dynamic timer showing hours/minutes until deadline
- Token Sync: Automatically imports auth token from Terriyaki website cookies
Technical Challenges Solved:
- Detecting LeetCode's dynamic success messages required multiple fallback strategies
- Extracting code from Monaco editor required understanding LeetCode's internal data structures
- Badge updates needed efficient scheduling (every 5-60 minutes based on urgency)
AI Integration
ElevenLabs Integration:
- Used ElevenLabs Agents API for voice-powered interviews
- Configured webhook endpoints to receive transcribed user speech
- Implemented conversation history storage in PostgreSQL JSONB format
- Handled session management and automatic interview termination after 4 user responses
Gemini Integration:
- Used Google Gemini 2.5 Flash for generating interview questions
- Created sophisticated system prompts that include:
- Problem context (title, difficulty, tags, description)
- Candidate's submitted code
- Interview guidelines (focus on complexity, edge cases, optimization)
- Implemented context-aware question generation based on conversation history
Interview Flow:
- User completes LeetCode problem
- User clicks "Start Interview" in extension or web app
- Backend creates interview session and configures ElevenLabs agent
- ElevenLabs handles voice interaction, transcribes speech, calls webhook
- Backend stores conversation, can optionally use Gemini for question generation
- Interview ends automatically after 4 user responses
Challenges We Faced
1. ElevenLabs Webhook Local Development
Problem: ElevenLabs servers need to call back to our backend, but localhost:8080 isn't accessible from the internet.
Solution: We used ngrok to create a public tunnel to our local server. This taught us about:
- Webhook architecture patterns
- The importance of proper error handling in webhook endpoints
- Security considerations (validating webhook requests)
Code Example:
// Webhook endpoint that receives transcribed text from ElevenLabs
func LLMWebhookAPI(c *gin.Context) {
var req struct {
TranscribedText string `json:"transcribed_text"`
SessionID string `json:"session_id"`
}
// Store conversation history and manage interview state
}
2. LeetCode Problem Parsing
Problem: LeetCode's problem descriptions are HTML-formatted, and we needed to extract structured data (description, constraints, examples).
Solution: We built a custom HTML parser using Go's golang.org/x/net/html package:
- Recursive DOM traversal to extract text content
- Regex fallbacks for edge cases
- Cleaning HTML entities and whitespace
- Parsing example input/output pairs from
<pre>tags
Challenge: LeetCode's HTML structure varies between problems, requiring robust fallback strategies.
3. Chrome Extension Auto-Detection
Problem: Detecting when a user successfully solves a problem on LeetCode is non-trivial. LeetCode uses dynamic DOM updates, and success indicators vary.
Solution: We implemented a multi-layered detection strategy:
// Multiple detection methods with fallbacks
const successIndicators = [
document.querySelector('[data-e2e-locator="submission-result"]'),
document.querySelector('.text-success'),
// Check for "Accepted" text with runtime info
// Monitor submission button state changes
// Check for success in console logs
];
We also used MutationObserver to watch for DOM changes and periodic polling as a fallback.
4. State Management Across Components
Problem: Managing authentication state, current grind, and task data across the Next.js app without prop drilling.
Solution: We used Zustand for global state management:
- Separate stores for auth, grinds, and messages
- Automatic token verification on app load
- Optimistic updates for better UX
5. Real-Time Progress Updates
Problem: Showing live leaderboard stats (e.g., "5/6 participants completed") requires keeping data fresh.
Solution: We implemented:
- Server-side calculation of completion stats on each request
- Efficient database queries using JOINs and aggregations
- Client-side polling for real-time updates (could be upgraded to WebSockets)
6. Interview Session Management
Problem: Managing conversation state across multiple webhook calls from ElevenLabs, ensuring interviews end at the right time, and handling edge cases (user disconnects, network errors).
Solution:
- Stored full conversation history in PostgreSQL JSONB
- Implemented response counting logic to auto-end after 4 user responses
- Added session status tracking (active, completed, ended)
- Created proper error handling and recovery mechanisms
7. CORS and Authentication
Problem: Chrome extension, web app, and backend all need to communicate securely with proper CORS configuration.
Solution:
- Configured CORS middleware in Gin to allow requests from frontend and extension
- Implemented JWT-based authentication with secure cookie storage
- Created token import feature in extension to sync with web app authentication
Mathematical Insights
While building Terriyaki, we discovered some interesting patterns:
Completion Rate Calculation: For a grind with $n$ participants and $d$ days, the total possible completions is $n \times d$. The completion rate is:
$$\text{Completion Rate} = \frac{\text{Completed Tasks}}{n \times d} \times 100\%$$
Streak Probability: If a user has a daily completion probability of $p$, the probability of maintaining a streak of $k$ days is:
$$P(\text{streak} = k) = p^k$$
This helped us understand why social accountability (increasing $p$ through peer pressure) is so effective.
Future Enhancements
While we're proud of what we built, we see many opportunities for improvement:
- WebSocket Integration: Real-time updates for leaderboards and notifications
- Payment Integration: Actually implementing the budget/stakes system with real money
- Problem Recommendations: AI-powered problem suggestions based on user performance
- Mobile App: Native iOS/Android apps for on-the-go practice
- Team Challenges: Multi-team competitions and tournaments
- Analytics Dashboard: Detailed insights into problem-solving patterns and improvement trends
Conclusion
Building Terriyaki taught us that solving real problems requires more than just coding skills—it requires understanding user psychology, designing intuitive interfaces, and making smart technical trade-offs. We're excited to see how this platform can help fellow developers stay motivated and improve their coding skills.
The journey from idea to working product was challenging but incredibly rewarding. We learned to integrate multiple APIs, build browser extensions, design databases, and create engaging user experiences. Most importantly, we built something we'd actually want to use ourselves.
Tech Stack Summary:
- Backend: Go, Gin, PostgreSQL, GORM
- Frontend: Next.js, TypeScript, Material-UI, Zustand
- Extension: Chrome Extension Manifest V3, Content Scripts
- AI: ElevenLabs Agents API, Google Gemini 2.5 Flash
- Infrastructure: Docker, ngrok (for development)
Key Metrics:
- ~5,000 lines of Go code
- ~3,000 lines of TypeScript/React code
- ~500 lines of Chrome extension JavaScript
- 8 database models with complex relationships
- 20+ API endpoints
- 3 major integrations (LeetCode, ElevenLabs, Gemini)
Built With
- gemini
- go
- graphql
- llm
- next.js
- postgresql
Log in or sign up for Devpost to join the conversation.