<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: WojciechowskiApp</title>
    <description>The latest articles on Forem by WojciechowskiApp (@wojciechowskiapp).</description>
    <link>https://forem.com/wojciechowskiapp</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3577757%2F3d9afea8-618f-43ec-bc6b-f7d88b96cb61.png</url>
      <title>Forem: WojciechowskiApp</title>
      <link>https://forem.com/wojciechowskiapp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/wojciechowskiapp"/>
    <language>en</language>
    <item>
      <title>Kinetiq Infrastructure: .NET 10, LiveKit, Yjs CRDT in Production</title>
      <dc:creator>WojciechowskiApp</dc:creator>
      <pubDate>Tue, 21 Oct 2025 15:39:29 +0000</pubDate>
      <link>https://forem.com/wojciechowskiapp/kinetiq-infrastructure-net-10-livekit-yjs-crdt-in-production-1o40</link>
      <guid>https://forem.com/wojciechowskiapp/kinetiq-infrastructure-net-10-livekit-yjs-crdt-in-production-1o40</guid>
      <description>&lt;h2&gt;
  
  
  System Overview
&lt;/h2&gt;

&lt;p&gt;Kinetiq is a global education platform with real-time collaboration and multi-language support. Current scale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;500+ tutors, 30 countries&lt;/li&gt;
&lt;li&gt;3,500+ completed sessions&lt;/li&gt;
&lt;li&gt;150+ languages supported&lt;/li&gt;
&lt;li&gt;99.9% uptime in production&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post covers the technical infrastructure and key architectural decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture Layers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────────────────────────────────────────┐
│ Frontend: Next.js 15.5.4 + React 19.1.0             │
│ - Server Components for data-heavy pages            │
│ - React Query 5.90.2 for server state               │
│ - Zustand 5.0.8 for client state                    │
│ - TypeScript 5.x                                     │
└─────────────────────────────────────────────────────┘
                      ▼ HTTPS/WSS
┌─────────────────────────────────────────────────────┐
│ Backend: .NET 10 Minimal APIs                        │
│ - Dapper ORM for queries                            │
│ - SignalR for WebSocket connections                 │
│ - PostgreSQL 16 (primary data)                      │
│ - Redis 7 (cache, sessions, pub/sub)                │
└─────────────────────────────────────────────────────┘
                      ▼
┌─────────────────────────────────────────────────────┐
│ Real-Time Services                                   │
│ - LiveKit (self-hosted WebRTC SFU)                  │
│ - Yjs CRDT (collaborative whiteboard + code editor) │
│ - Deepgram (real-time STT, &amp;lt;500ms latency)          │
│ - Azure Cognitive Services (translation)            │
│ - OpenAI GPT-4 + Whisper (content generation)       │
└─────────────────────────────────────────────────────┘
                      ▼
┌─────────────────────────────────────────────────────┐
│ Infrastructure: Kubernetes + Docker                  │
│ - Dapr 1.16 Service Mesh                            │
│ - Cloudflare R2 (video/file storage)                │
│ - Serilog → Seq (structured logging)                │
│ - Zipkin (distributed tracing)                      │
└─────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Key Technical Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. .NET 10 Minimal APIs Over Node.js
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Decision:&lt;/strong&gt; Use .NET 10 for backend instead of Node.js/Express.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rationale:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; TechEmpower benchmarks show .NET 10 Minimal APIs at 7M req/s vs Node.js (Fastify) at 1.2M req/s&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency:&lt;/strong&gt; SignalR handles 100K+ concurrent WebSocket connections on single instance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CPU-intensive workloads:&lt;/strong&gt; Better for AI prompt processing and real-time translation orchestration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory:&lt;/strong&gt; Lower memory footprint under load compared to Node.js event loop&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Production metrics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;100K concurrent WebSocket connections at 40% CPU&lt;/li&gt;
&lt;li&gt;P50 API response: 8ms&lt;/li&gt;
&lt;li&gt;P95 API response: 45ms&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Yjs CRDT for Collaborative Editing
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Decision:&lt;/strong&gt; Use Yjs (Conflict-free Replicated Data Types) for real-time collaboration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rationale:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Conflict-free:&lt;/strong&gt; Multiple users can edit simultaneously without merge conflicts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Eventual consistency:&lt;/strong&gt; All clients converge to identical state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline-capable:&lt;/strong&gt; Operations queue locally and sync when reconnected&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance:&lt;/strong&gt; Operations are lightweight, minimal network overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Implementation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Whiteboard (tldraw):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Tldraw&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tldraw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;YjsEditor&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@tldraw/yjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Y&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;yjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;WebsocketProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;y-websocket&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Doc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebsocketProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wss://kinetiq.one/sync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createTLStore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;YjsEditor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tldraw&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code editor (Monaco):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;MonacoBinding&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;y-monaco&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Y&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Doc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;yText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;monaco&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebsocketProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;wss://kinetiq.one/sync&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MonacoBinding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;yText&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;editor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getModel&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;editor&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="nx"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;awareness&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Production metrics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Zero sync conflicts since deployment (6 months, 3,500+ sessions)&lt;/li&gt;
&lt;li&gt;P50 sync latency: 12ms&lt;/li&gt;
&lt;li&gt;P95 sync latency: 85ms (cross-continent)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Self-Hosted LiveKit Over Managed Services
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Decision:&lt;/strong&gt; Self-host LiveKit WebRTC SFU instead of using Twilio/Agora/Vonage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rationale:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cost:&lt;/strong&gt; Managed services charge $0.004-0.015/minute/participant. At 3,500+ sessions, this is $2K+/month&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Control:&lt;/strong&gt; Full control over SFU configuration, recording format, storage location&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scalability:&lt;/strong&gt; Horizontal scaling in Kubernetes with auto-healing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration:&lt;/strong&gt; Direct recording to Cloudflare R2 (zero egress fees)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LiveKit deployed as Kubernetes StatefulSet&lt;/li&gt;
&lt;li&gt;Auto-scaling based on active rooms&lt;/li&gt;
&lt;li&gt;Health checks + liveness probes&lt;/li&gt;
&lt;li&gt;Recording pipeline: LiveKit → Cloudflare R2 → Deepgram/Whisper&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cost comparison:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Managed service: ~$2,000/month&lt;/li&gt;
&lt;li&gt;Self-hosted (Kubernetes + compute): ~$200/month&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Savings: $1,800/month&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. PostgreSQL + Redis Architecture
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Decision:&lt;/strong&gt; PostgreSQL 16 as primary database, Redis 7 for caching and real-time features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PostgreSQL usage:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User data, courses, sessions&lt;/li&gt;
&lt;li&gt;Full-text search with GIN indexes&lt;/li&gt;
&lt;li&gt;JSONB for flexible schemas (course content, user preferences)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Redis usage:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Session storage (distributed across instances)&lt;/li&gt;
&lt;li&gt;Translation cache (85% hit rate, saves $680/month on Azure API calls)&lt;/li&gt;
&lt;li&gt;Real-time presence (online users, typing indicators)&lt;/li&gt;
&lt;li&gt;Rate limiting (sliding window algorithm)&lt;/li&gt;
&lt;li&gt;Pub/Sub for SignalR backplane (multi-instance WebSocket)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Query optimization example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Tutor search with full-text + filters&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_tutors_search&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;tutors&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;GIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;to_tsvector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_tutors_skills&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;tutors&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;GIN&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;skills&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Query time: 8s → 40ms after indexing&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;tutors&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;to_tsvector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'english'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;bio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;@@&lt;/span&gt; &lt;span class="n"&gt;to_tsquery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'python &amp;amp; react'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;skills&lt;/span&gt; &lt;span class="o"&gt;@&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'typescript'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'node.js'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Translation Caching Strategy
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Azure Translator API costs $10 per 1M chars. At scale: $800/month for repetitive phrases.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Redis caching with intelligent key strategy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// C# caching implementation&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;TranslateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;targetLang&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cacheKey&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;$"translate:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;ComputeHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;targetLang&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Check cache first&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cached&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StringGetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HasValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cached&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToString&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Miss: call Azure API&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;translated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_azureTranslator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;TranslateAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;targetLang&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Cache for 7 days&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;StringSetAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;translated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TimeSpan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromDays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;translated&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Results:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cache hit rate: 85%&lt;/li&gt;
&lt;li&gt;API calls reduced from 8M/month to 1.2M/month&lt;/li&gt;
&lt;li&gt;Cost: $800/month → $120/month&lt;/li&gt;
&lt;li&gt;P50 latency: 450ms → 15ms (cache hit)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Production Metrics
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Uptime &amp;amp; Reliability:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;99.9% uptime (Kubernetes auto-healing)&lt;/li&gt;
&lt;li&gt;Zero-downtime deployments (rolling updates)&lt;/li&gt;
&lt;li&gt;Mean time to recovery: &amp;lt;2 minutes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Performance:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API P50: 8ms, P95: 45ms&lt;/li&gt;
&lt;li&gt;WebSocket sync P50: 12ms, P95: 85ms&lt;/li&gt;
&lt;li&gt;Translation P50: 15ms (cached), P95: 450ms&lt;/li&gt;
&lt;li&gt;Full-text search: 40ms average&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scale:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;100K+ concurrent WebSocket connections supported&lt;/li&gt;
&lt;li&gt;500 concurrent tutoring sessions (peak)&lt;/li&gt;
&lt;li&gt;3,500+ completed sessions&lt;/li&gt;
&lt;li&gt;150+ languages supported&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cost Efficiency:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Infrastructure: ~$400/month (compute + storage + CDN)&lt;/li&gt;
&lt;li&gt;LiveKit self-hosted: $200/month vs $2K managed&lt;/li&gt;
&lt;li&gt;Translation caching: $120/month vs $800 without cache&lt;/li&gt;
&lt;li&gt;Total savings: ~$2,500/month vs managed alternatives&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Technology Stack
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 15.5.4 (App Router, Server Components)&lt;/li&gt;
&lt;li&gt;React 19.1.0&lt;/li&gt;
&lt;li&gt;TypeScript 5.x&lt;/li&gt;
&lt;li&gt;Tailwind CSS 4.0&lt;/li&gt;
&lt;li&gt;React Query 5.90.2&lt;/li&gt;
&lt;li&gt;Zustand 5.0.8&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Backend:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.NET 10 (Minimal APIs)&lt;/li&gt;
&lt;li&gt;Dapper ORM&lt;/li&gt;
&lt;li&gt;SignalR (WebSockets)&lt;/li&gt;
&lt;li&gt;PostgreSQL 16&lt;/li&gt;
&lt;li&gt;Redis 7&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Real-Time:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LiveKit (self-hosted SFU)&lt;/li&gt;
&lt;li&gt;Yjs CRDT + y-websocket&lt;/li&gt;
&lt;li&gt;tldraw 2.4.6 (whiteboard)&lt;/li&gt;
&lt;li&gt;Monaco Editor + y-monaco (code editor)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AI/ML:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;OpenAI GPT-4 (content generation)&lt;/li&gt;
&lt;li&gt;Whisper (transcription)&lt;/li&gt;
&lt;li&gt;Deepgram (real-time STT)&lt;/li&gt;
&lt;li&gt;Azure Cognitive Services (translation)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kubernetes + Docker&lt;/li&gt;
&lt;li&gt;Dapr 1.16 (service mesh)&lt;/li&gt;
&lt;li&gt;Cloudflare R2 (object storage)&lt;/li&gt;
&lt;li&gt;Serilog + Seq (logging)&lt;/li&gt;
&lt;li&gt;Zipkin (distributed tracing)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Read More
&lt;/h2&gt;

&lt;p&gt;Full case study with implementation details, challenges, and lessons learned:&lt;br&gt;
&lt;strong&gt;&lt;a href="https://wojciechowski.app/en/articles/kinetiq-case-study" rel="noopener noreferrer"&gt;wojciechowski.app/en/articles/kinetiq-case-study&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Portfolio: &lt;strong&gt;&lt;a href="https://wojciechowski.app" rel="noopener noreferrer"&gt;wojciechowski.app&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;




</description>
      <category>dotnet</category>
      <category>architecture</category>
      <category>programming</category>
      <category>saas</category>
    </item>
  </channel>
</rss>
