The problem
An English teacher with five classes of thirty gets a stack of essays every week. Detailed feedback on each one takes hours, so students wait days and often get a single line back, long after they've stopped thinking about the essay. The obvious shortcut makes it worse: an AI tool that rewrites the essay teaches the student nothing and makes it easy to hand in work that isn't theirs. And some of the best writing in a classroom is still on paper, where no AI tool can see it.
So the job was a coach, not a ghostwriter: feedback that arrives in seconds and points at the exact words to fix, a teacher who stays in charge of every grade, and a system that copes when a whole institution hands in essays on the same evening.
My role
I've led Engagemo's engineering since June 2025, across the client and the server, with a team of about six. I own the architecture and the delivery, and I still write a large share of the code.
What I built myself:
- The first queue for bulk AI feedback, so a teacher can draft feedback for a whole class in one go
- The move to a new AI provider, with reasoning effort tuned for each kind of task
- Student and class reports with AI analysis and PDF export
- The gradebook and rubric grading
- Stripe billing for independent teachers, and activation codes for institutions
- Sign-in hardening: role-based permissions, token revocation and detection of reused refresh tokens
- The recording and integrity side of Oral Defense
- The plan for moving from a single VPS to AWS
What I led: reading handwriting (OCR), the core of Oral Defense, and the security review.
How it works
What people use
- Students write in an editor with four coaching checks: grammar, vocabulary, content and organisation. Each check points at the exact phrase and explains the fix, and each can be used only a few times per assignment.
- Teachers set tasks with a rubric, get AI-drafted feedback and grades for a whole class to review and edit, and keep a gradebook. Handwritten essays can be photographed, read and corrected beside the original.
- Oral Defense: after submitting, a student answers a few spoken questions about their own essay. The AI rates how well they understand what they wrote, and the teacher decides the verdict.
- Institutions get class reports with focus areas for the next lessons, and licences that switch features on per institution.
Under the hood
A React and Vite client talks to an Express and TypeORM API on PostgreSQL. Anything slow, like feedback for a whole class, a report or an Oral Defense analysis, runs as a background job on BullMQ and Redis, with progress pushed to the browser over Socket.IO. Language models are called through LangChain, so the provider can change without touching features. Google Cloud Vision reads handwriting, Gemini handles Oral Defense audio, and files live in S3. It all runs on AWS behind Cloudflare.
Decisions
One background job per essay, with a lock per teacher. When a teacher asks for feedback on 150 essays, each essay becomes its own job, and jobs are ordered so one teacher's batch can't hold up everyone else's. The cost: more moving parts, so we added a recovery sweep that finds stuck jobs and picks them up again.
The AI provider sits behind one layer. We've moved between AI providers without rewriting a feature. I also tune how much the model reasons for each kind of task, trading speed for depth only where it pays. The cost: every switch means re-testing the prompts against real essays.
The teacher has the final word. The AI drafts feedback and grades; the teacher reviews, edits and approves. A teacher's grade always overrides the AI's, and Oral Defense never fails a student on its own: signals like switching tabs during a defense are shown to the teacher as evidence, not verdicts. The cost is the teacher's review time, which buys feedback they trust and stand behind.
Features per institution are data, not code. Handwriting, reports and Oral Defense are switched on per institution, along with limits on classes and AI use, so one codebase serves every plan. Institutions hand out activation codes; independent teachers pay by card through Stripe. The cost: every new feature needs its switch and its limits from day one.
The hard part
AI "ghost" corrections. A student fixes a mistake, asks for feedback again, and gets told off for the very error they just fixed. Or the AI quotes a phrase that isn't in their essay at all. Either way the student stops trusting the coach, and a coach nobody trusts is just noise.
The model can't be made perfect, so I stopped trusting its output instead. Every correction now passes a check against the essay as it is right now, before a student sees it:
- The quoted words must exist, exactly. A correction survives only if its original phrase appears in the current essay, matched on whole words, so "class time" doesn't match "class times".
- Corrections that change nothing are dropped. If the "fix" is identical to the original, it never reaches the student.
- Don't throw away good work. The AI also quotes a little surrounding text so the editor can find the spot. When only that surrounding quote is wrong, the correction is kept, the bad quote is stripped, and "Locate in Text" falls back to a simple search.
- Log every stage. Each step records what it removed and why, so when a teacher reports a strange correction, we can see exactly where it slipped through.
I also rebuilt the prompt for second and later drafts. What it taught me: never let a model's words reach a student unchecked. The cheapest guardrail is the text you already have.
Another one, briefly. Students in Kazakhstan reported a blank page. The app was sending 1.1 MB of JavaScript and 9 MB of images on first load, which on slower connections meant nothing showed up at all. We cut that to 320 KB of JavaScript and 0.3 MB of images.
Outcome so far
Engagemo is used by more than 7,000 people across 30–40 institutions, and 368 accounts were activated in just two weeks this autumn.
What I'd change: background workers and web requests share one pool of database connections, so a busy queue can slow the site down. I'd give each its own. And I'd move deploys to CI/CD instead of the scripted deploys we run today.
Next: live speaking practice in small groups.
