Skip to main content
Founder, Full-stack Developer2026
#Python#FastAPI#PostgreSQL#Redis#Kafka#Celery#OpenRouter#Next.js#TypeScript#Docker#OpenTelemetry

Tech Path Finder

A preparation platform for IT professionals: 65 courses with quizzes plus three AI trainers where the PR author argues back, the negotiation counterpart bargains, and interview answers get scored

About the Project

Tech Path Finder is a platform for preparing IT professionals for career advancement. It started as courses with quizzes. Over the past year it turned into something else: alongside the theory and the questions there are now three trainers where the other side is not a form with radio buttons but a counterpart with interests of their own.

The problem it solves is simple. Developers don't know exactly what to study to reach the next level, and they waste time on chaotic content consumption without feedback about their actual gaps. A quiz shows what someone knows. A trainer shows how they behave when someone argues back.

The core idea is personalization through data: the system builds a competency profile over time. Knowledge that isn't applied gradually decays, and the platform reminds you before that becomes a problem in an interview.

What's Inside

Learning and Assessment

65 courses across categories: Agile/Scrum, Python, Django, FastAPI, Kubernetes, Docker, AWS, System Design, LLM/RAG, ClickHouse, Kafka, Playwright, LeetCode and others. In total 940 topics, 941 theory articles and 8457 quiz questions. Each course contains:

  • Theory in Markdown with diagrams and a glossary
  • Quizzes at three difficulty levels (junior / middle / senior) with detailed explanations
  • Exams of 20 deterministic questions per variant, with attempt history
  • Progress tracking for topic completion and answer accuracy

Knowledge Decay Algorithm

A proficiency score for each topic decreases over time when the user doesn't return to the material:

proficiency = accuracy × recency_factor × 100
recency_factor = max(0.3, 1 - days_since_last_answer / 30)

Topics are automatically classified as "critical" (< 40%), "weak" (40–65%) or "not started," and the system builds a personal review queue.

AI-Evaluated Mock Interviews

Structured sessions by language and level, 1236 questions in the bank. Answers are scored by a model through OpenRouter, producing a final score and per-answer feedback. Express or full format, 10 credits per session.

Code Review Arena

The hardest thing added this year. The user gets a problematic PR, writes free-form comments, and the code author responds and argues back. A regular code quiz was not enough here: spotting the bug is only half of it, you also have to hold your ground.

The code_review_arena domain is assembled from separate parts: scenario generation, seed synchronization, round-zero detection scoring, dialogue with the author, finalization by a judge, and moderation. The library holds 133 scenarios.

Negotiation Trainer

78 difficult-conversation scenarios. On the other side sits a role-playing character with a hidden card: interests, fears, and the conditions under which they open up or concede. The character answers in role while a separate analysis runs in parallel, invisible to the character itself.

The output is the negotiation result, a profile across six process parameters, and a PDF report quoting the conversation. A voice mode is available.

Code Review Exercises

Practical assignments with real code in Python, Go, Java, Ruby and other languages. The user finds bugs and violations, the system compares against a reference on a three-point scale (0 / 0.5 / 1.0). There is a daily challenge and per-language tracking of weak error types.

Gamification

An XP system with event triggers (topic completion +50, exam +100, streaks +25/100), milestone-based achievements, daily streaks, and a progress dashboard with an activity heatmap and exam history.

Architecture

PythonFastAPIPostgreSQLRedisKafkaCeleryOpenRouterNext.jsReactTypeScriptTailwind CSSDockerNginxOpenTelemetryJaegerPrometheus

Backend: Domain-Driven Design

14 independent domains, each with its own api.py, service.py, models.py, schemas.py. Seven of them appeared this year, which is the clearest indicator of where the project has been heading:

DomainResponsibility
authJWT, OAuth (Google/Yandex/GitHub), password recovery
usersProfiles, achievements, XP, settings
contentQuizzes, exams, progress, recommendations
interviewMock interviews, AI scoring, credit spending
code_reviewCode exercises, daily challenges
code_review_arenaPR scenarios, author dialogue, judge, seed moderation
negotiationsRole-playing character, reply analysis, voice, PDF
billingCredit system, promo codes, refunds
adminAnalytics, user management, moderation
seoWordstat, audits, scoring, goals, diagnostics
mediaUpload, processing and storage
commentsComments with likes
notificationsEmail delivery, reports
feedbackUser feedback

Roughly 54,000 lines of Python in app/ and 92 migrations.

Frontend: Feature-Sliced Design

Next.js 16.2.12 (App Router) and React 19.2.3, 75 pages, three route groups:

  • (main) for authenticated pages with navigation
  • (auth) for public pages: sign-in, sign-up, recovery
  • (admin) for the admin panel

All API calls go through Server Actions with 'use server'. Tokens are read from httpOnly cookies on the server only, so no token ever reaches client code.

Content as Code

Courses are versioned in Git as JSON and Markdown under content/data/:

content/data/
  agile/
    ceremonies.json     # Subtopic questions with explanations
    ceremonies.md       # Theory article
    glossary.json       # Term glossary
    exam.json           # 20 deterministic questions

Updating content means a Git commit and a redeploy. No CMS, no content database. The 133 arena scenarios, 78 negotiation scenarios and 1236 mock-interview questions live the same way.

Infrastructure

nginx → next.js (:3000) + fastapi (:8000)
├── postgres     # Primary database
├── redis        # Sessions, rate limiting, cache
├── kafka        # Event streaming, audit log
├── celery       # Background tasks, email
├── jaeger       # Distributed tracing
└── plausible    # Privacy-first analytics

Technical Challenges

A role-playing character that can't peek at the analysis

Problem: in the negotiation trainer the same LLM has to play the counterpart and grade the user's replies at once. If both modes share one conversation, the character starts playing along: it sees the analysis and adjusts toward the "right" answer.

Solution: the protocols are separated. The character answers in role, driven by a hidden card of interests, fears and concession conditions, while the analysis runs as a parallel stream the character never sees. The LLM core is reused from the interview domain; the protocol on top of it is its own.

A healthcheck that killed celery with SIGBUS

Problem: the container healthcheck spent three days accumulating prometheus_client mmap files, filled the tmpfs, celery forks started dying with SIGBUS, acks_late tasks looped in the queue, and the daily email quota burned down in ninety seconds.

Solution: unset PROMETHEUS_MULTIPROC_DIR for the healthcheck command, a larger tmpfs, counting emails after SMTP rather than before, and a dedicated alert for worker death.

Shuffling answers without leaking the correct index

Problem: shuffling options on the frontend alone lets a user memorize the position of the correct option in the original array.

Solution: Fisher-Yates runs on the server for every request. The client sends an index in shuffled space and the server maps it back for validation.

Rate limiting with Lua and Redis

Problem: protecting sensitive endpoints from brute force without external dependencies.

Solution: an atomic sliding window via a Lua script in Redis, INCR and EXPIRE in one transaction. Separate limits per endpoint:

POST /auth/login        → 5 / 15 minutes / IP
POST /auth/register     → 3 / hour / IP
POST /auth/forgot-pw    → 3 / hour / IP

Credit billing without races

Problem: atomic credit deduction under concurrent requests without double spending.

Solution: a ledger pattern, an append-only CreditTransaction table plus a denormalized User.credits_balance cache, updated atomically via SELECT FOR UPDATE. Mock-interview sessions are idempotent by key.

Refresh tokens without a race condition

Problem: with concurrent requests on an expired token, both may try to refresh and one ends up with an invalidated token.

Solution: an atomic GETDEL from Redis on refresh. The token is read and deleted in one operation, so the second request gets None and goes back to /auth/refresh.

Observability from scratch

A full observability stack without vendor lock-in:

  • Tracing: FastAPI, SQLAlchemy and Redis instrumented via OpenTelemetry into Jaeger
  • Metrics: Prometheus plus custom metrics (registrations, XP, topic completions, lost workers)
  • Health checks: /health and /health/ready with database and Redis probes
  • Graceful shutdown: in-flight request draining with a 30-second timeout

Security

  • Passwords: bcrypt with 12 rounds
  • JWT: HS256, 30-minute access and 7-day refresh tokens in httpOnly cookies
  • OAuth: state parameter verified through Redis (1-hour TTL) against CSRF
  • Sensitive fields: Fernet encryption when APP_ENCRYPTION_KEY is present
  • SQL: parameterized queries through SQLAlchemy
  • Linting: Bandit and Ruff in pre-commit hooks

Results

65
Courses
9693
Questions
3
AI trainers
643
Tests

The 9693 questions are 8457 in course quizzes and 1236 in mock interviews. Tests: 643 across 87 files. Commits in the repository: 1062.

What Got Built

  • A complete SaaS from scratch, from architecture to deployment
  • A knowledge decay algorithm delivering personalization without ML overhead
  • Three AI trainers that score behavior in a dialogue rather than a choice among options
  • Content as code: 65 courses and 211 scenarios updated through Git without a CMS
  • A production stack with tracing, metrics and graceful shutdown

Lessons Learned

Content as code beats a CMS for technical courses: versioning, content code review and rollback come free with Git.

The knowledge decay algorithm turned out to be a non-obvious simple win. Full spaced repetition with a scheduler was never needed; a time-decay function was enough to make recommendations noticeably better.

DDD partitioning paid off from the first month and especially over the past year: seven new domains grew without touching the old ones. Arena and negotiations reuse the interview LLM core yet live their own lives.

A trainer differs from a quiz not in its interface but in the fact that the other side has an interest. The PR author argues, the negotiation counterpart bargains and withholds. Once the counterpart has goals of its own, the task stops being about checking knowledge and becomes about behavior, and that turned out to be a very different piece of engineering.

JWT in httpOnly cookies via Server Actions solves the frontend token problem: client code never sees them, so XSS cannot steal them.