Skip to content
all work

D Plus+

A community platform for learning German

A feed pitched at your level, courses, AI pronunciation practice, full telc/Goethe/ÖSD mock exams with a fresh sheet every time, publish-first moderation, and Egyptian card payments.

Sole engineer — product, ranking, moderation, payments

D Plus+ product screen
89,492
TypeScript
158
Migrations
telc · Goethe · ÖSD
Mock exams
64
Commits
DE + EN
Interface
Next.js 16React 19TypeScriptSupabase / PostgresRow-level securityGeminiPaymobnext-intl (DE/EN)Tailwind v4Framer Motionreact-three-fiberWeb PushVercel

What it is today

D Plus+ is a community platform for people learning German — a feed pitched at your level, courses, exam training, pronunciation practice, and a community that answers.

The feed carries editorial posts by the platform's own voice, Rosina: a fifteen-minute daily routine, why Germans keep saying Ach so!, eight words for a first job application with Arabic glosses, telc strategies, and full episodes of Nicos Weg. Reactions, comments, reposts, saves, a daily posting allowance, a streak, and a level badge.

Ach so! is the community: questions until it clicks, named after the German for exactly that moment.

Kurse. Nicos Weg from zero at A1, A2, and a B1 exam-fit listening course, with ratings and progress.

Sprechen. Pronunciation training with a microphone — words, sentences, or free speaking from A1 to C2 — checked by AI.

Prüfungen. Full mock exams in the real telc, Goethe and ÖSD formats: reading, listening, writing and speaking, a fresh sheet assembled from a large task bank on every start, graded by AI, with open sheets you can resume and best scores tracked.

A placement test places a newcomer; a personal assistant is one button away; tasks and a calendar keep the routine.

Why it matters. Learners do not lack material. They lack a place where writing a bad sentence is normal, gets read, and gets answered — and where the next thing they see is pitched at where they actually are.

A feed that follows the learner

Chronological feeds reward whoever posts most; engagement-ranked feeds reward whatever already won. Neither knows whether a B1 learner should be reading this.

D Plus+ ranks on the learner: how close a post is to their interests, how well it fits their level, whether it comes from a community they joined, how the community responded, and how fresh it is — with every one of those signals kept on the same bounded scale so no single one can quietly take over the ranking. Interests are seeded by the onboarding survey and updated by what the learner actually does, and they fade over weeks, so a shifting focus moves the feed without any setting to change. What the feed merely showed someone is deliberately not treated as a signal, because that closes a loop in which the ranker confirms its own first guess forever.

Try it: move the sliders, then switch engagement to an unbounded count and watch a small weight become the whole ranking.

Why every signal is bounded

  • 0.40 · interest
    0.280
  • 0.20 · level fit
    0.200
  • 0.10 · community
    0.100
  • 0.10 · engagement
    0.065
  • 0.20 · recency
    0.168
score0.813

Bounded: 200 interactions reach the ceiling and so do 2,000 — the difference stopped being informative. Freshness fades smoothly. The weights mean exactly what they say.

Publish first, review after

Everything a member writes goes live immediately and is reviewed seconds later. Review takes content down; it is not what lets it up.

That order is a product decision, not a shortcut. A learner writes a sentence in a language they are still learning and then watches nothing happen — and that delay lands hardest on exactly the people the platform exists for. The reviewer transcribes audio, classifies what it finds, and the platform — not the model — decides the consequence from a written policy. When the reviewer cannot decide or breaks, the post stays visible for a person to look at: "not sure" is not a finding. Every hold is explained and appealable.

Technologies: Gemini for transcription and classification, Postgres, Supabase.

Publish first, review after

  1. 1

    post

    live immediately

  2. 2

    review

    seconds later

  3. 3

    finding

    what, and in which language

  4. 4

    policy

    keep · hold · remove

Reviewer finds

The model says what it found. The platform decides the consequence from a written policy — so changing a prompt can never change how long someone is suspended for.

status
approved
visible
yes

The post was live the whole time. Review confirmed it; the member never waited.

Payments and security

Paymob, the Egyptian gateway: authorise, capture, void, refund, verified callbacks and a settlement worker that pays teachers out. Every callback is verified before it is trusted; an unverified payment callback would be an endpoint that lets anyone mark a lesson as paid, so the integration fails closed.

Visibility and permissions are enforced in the database with row-level security, and no client can promote its own content or undo a moderation decision from the outside.

Technologies: Next.js 16, React 19, Supabase / Postgres with RLS, Paymob, Web Push, next-intl (German + English), Vercel.

Decisions I would defend

If I cannot say why the alternative is worse, I do not understand the code in my own repository — regardless of who typed it.

01Rank on the learner, on bounded signals
Interest, level fit, community, response and freshness — each kept on the same bounded scale, so a weight means what it says and no signal can run away with the ranking.
02Do not learn from what you merely showed
Treating impressions as interest closes a loop in which the feed confirms its own first guess. Only what the learner does moves their interests.
03Publish first, review after
The delay of review-then-publish is paid by every honest post forever, and it lands hardest on beginners. Review removes; it does not admit.
04The model classifies; the platform decides
A reviewer returns what it found. The consequence comes from a written policy, so changing a prompt can never change someone's suspension.
05Payments fail closed
An unverified payment callback is a public endpoint for marking things paid. Without verification, nothing is accepted.

Honestly — what is not true yet

There is no automated test suite. Correctness lives in the database — permissions, generated state, explicit grants — which is a real design position and still not a substitute for tests. It is the first thing I would close. The AI review path also degrades gracefully when no model key is present, by design, which means the safety net is exactly as live as the key.