<?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>DEV Community: Кирилл Несмелов</title>
    <description>The latest articles on DEV Community by Кирилл Несмелов (@__615f3acc0).</description>
    <link>https://dev.to/__615f3acc0</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%2F3973737%2F4b39cd82-017c-405c-898d-5bf027ccd31b.jpg</url>
      <title>DEV Community: Кирилл Несмелов</title>
      <link>https://dev.to/__615f3acc0</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/__615f3acc0"/>
    <language>en</language>
    <item>
      <title>How I built a switchable AI provider system in Next.js (Gemini GPT-4 Claude, one workspace setting)</title>
      <dc:creator>Кирилл Несмелов</dc:creator>
      <pubDate>Wed, 10 Jun 2026 10:38:52 +0000</pubDate>
      <link>https://dev.to/__615f3acc0/how-i-built-a-switchable-ai-provider-system-in-nextjs-gemini-gpt-4-claude-one-workspace-pjk</link>
      <guid>https://dev.to/__615f3acc0/how-i-built-a-switchable-ai-provider-system-in-nextjs-gemini-gpt-4-claude-one-workspace-pjk</guid>
      <description>&lt;p&gt;I had Gemini hardcoded into every AI function in my codebase. Then a beta user told me her clients have data policies about Google. I rebuilt the whole AI layer in two days. Here's the architecture.&lt;/p&gt;

&lt;p&gt;The wrong way (what I had)&lt;/p&gt;

&lt;p&gt;import { GoogleGenerativeAI } from "@google/generative-ai";&lt;br&gt;
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);&lt;/p&gt;

&lt;p&gt;export async function summarizeProject(projectId: string) {&lt;br&gt;
  const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });&lt;br&gt;
  const result = await model.generateContent(prompt);&lt;br&gt;
  return result.response.text();&lt;br&gt;
}&lt;br&gt;
Works fine until you need to support another provider. Then you're touching every function.&lt;/p&gt;

&lt;p&gt;The pattern: one factory, one interface&lt;/p&gt;

&lt;p&gt;I defined a single AIClient interface that every provider implements:&lt;/p&gt;

&lt;p&gt;export interface AIClient {&lt;br&gt;
  complete: (prompt: string, systemPrompt?: string) =&amp;gt; Promise;&lt;br&gt;
}&lt;br&gt;
One factory function reads the workspace setting and returns the right client:&lt;/p&gt;

&lt;p&gt;export async function getAIClient(workspaceId: string): Promise {&lt;br&gt;
  const { aiProvider } = await getWorkspaceSettings(workspaceId);&lt;/p&gt;

&lt;p&gt;switch (aiProvider) {&lt;br&gt;
    case "openai":    return createOpenAIClient();&lt;br&gt;
    case "anthropic": return createAnthropicClient();&lt;br&gt;
    default:          return createGeminiClient();&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
Each factory wraps the provider SDK behind the same interface:&lt;/p&gt;

&lt;p&gt;function createAnthropicClient(): AIClient {&lt;br&gt;
  const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY! });&lt;br&gt;
  return {&lt;br&gt;
    complete: async (prompt, systemPrompt) =&amp;gt; {&lt;br&gt;
      const res = await client.messages.create({&lt;br&gt;
        model: "claude-sonnet-4-6",&lt;br&gt;
        max_tokens: 2048,&lt;br&gt;
        ...(systemPrompt ? { system: systemPrompt } : {}),&lt;br&gt;
        messages: [{ role: "user", content: prompt }],&lt;br&gt;
      });&lt;br&gt;
      return res.content[0].type === "text" ? res.content[0].text : "";&lt;br&gt;
    },&lt;br&gt;
  };&lt;br&gt;
}&lt;br&gt;
Every AI action now looks like this — provider-agnostic:&lt;/p&gt;

&lt;p&gt;export async function summarizeProject(projectId: string, workspaceId: string) {&lt;br&gt;
  const ai = await getAIClient(workspaceId);&lt;br&gt;
  const data = await getProjectContext(projectId);&lt;br&gt;
  return ai.complete(buildPrompt(data), "You are a project analyst.");&lt;br&gt;
}&lt;br&gt;
Three decisions worth explaining&lt;/p&gt;

&lt;p&gt;Workspace-level, not user-level. Project data belongs to the workspace. The whole team should have the same answer to "what AI provider processes our data?" — not "depends who's logged in."&lt;/p&gt;

&lt;p&gt;No silent fallbacks. If the selected provider fails, the error propagates. I don't retry with another provider. If someone chose Claude for compliance reasons, silently falling back to Gemini is worse than an explicit error.&lt;/p&gt;

&lt;p&gt;Per-request factory call, not module-level cache. Workspace settings can change. Module-level caching means the old provider keeps running until the function cold-starts. The extra DB read per AI call is negligible next to the actual inference cost.&lt;/p&gt;

&lt;p&gt;The one thing the pattern doesn't solve&lt;/p&gt;

&lt;p&gt;Different models behave differently on the same prompt. Claude follows system prompts very tightly. GPT-4 tends verbose without explicit length instructions. I handle this with thin per-provider prompt adjustments in the factory. It's not elegant but it works.&lt;/p&gt;

&lt;p&gt;If you've solved the "same prompt, different model behavior" problem more cleanly — I'm curious how.&lt;/p&gt;

&lt;p&gt;Building Melororium — pay-once project management for freelancers. Launching July 30.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>saas</category>
      <category>ai</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Day 7: 51 people answered my question. I wasn't ready for what they said.</title>
      <dc:creator>Кирилл Несмелов</dc:creator>
      <pubDate>Mon, 08 Jun 2026 13:18:22 +0000</pubDate>
      <link>https://dev.to/__615f3acc0/day-7-51-people-answeredmy-question-i-wasntready-for-what-they-said-17na</link>
      <guid>https://dev.to/__615f3acc0/day-7-51-people-answeredmy-question-i-wasntready-for-what-they-said-17na</guid>
      <description>&lt;p&gt;Day 7: 51 people answered my question. I wasn't ready for what they said.&lt;br&gt;
Last week I asked: "What convinced you to switch away from a subscription tool — or what's keeping you locked in?"&lt;br&gt;
51 people answered. I read every single one.&lt;br&gt;
I expected arguments about SaaS economics. Or "subscriptions are fine, you get what you pay for."&lt;br&gt;
Instead, almost every answer was some version of the same thing:&lt;br&gt;
"I already left. I use free Notion + Trello + a spreadsheet I built myself. I stopped paying because I refused to keep feeding the machine."&lt;br&gt;
Nobody is sitting around waiting for a better subscription-free tool. They've already quit — and they're living with the consequences of their DIY stack. Things that don't connect. Manual exports every Friday. Three browser tabs open at once just to see where a project stands.&lt;br&gt;
That one insight shifted how I think about who I'm building &lt;a href="https://melororium.com" rel="noopener noreferrer"&gt;https://melororium.com&lt;/a&gt; for.&lt;br&gt;
It's not someone who's currently paying $50/month for ClickUp and angry about it. It's someone who already said "no" — months ago — and built their own janky workaround. I'm building for the person who quit before I showed up.&lt;br&gt;
So let me show you what I've actually built.&lt;br&gt;
This is the part where I want to be specific, because "almost done" is the oldest lie in indie building.&lt;br&gt;
I recorded a short walkthrough this week — nothing polished, just the real product as it exists right now:&lt;br&gt;
&lt;a href="https://youtu.be/Sa35DwMXAOQ" rel="noopener noreferrer"&gt;https://youtu.be/Sa35DwMXAOQ&lt;/a&gt;&lt;br&gt;
Here's what you'll see working:&lt;br&gt;
Kanban board — full drag-and-drop, columns, task cards, assignees. Not a prototype. The thing I actually use every day to run my own work. You can move a task from "In Progress" to "Done" and it updates across every view instantly.&lt;br&gt;
Tasks — complete system. Priorities, deadlines, subtasks, descriptions, comments. The boring stuff that actually matters when you're juggling 6 clients and can't afford to forget anything.&lt;br&gt;
Clients — bring your clients in, attach projects to them, see everything related to one client in a single view. No more searching through Notion pages or scrolling through Slack to find what you promised someone.&lt;br&gt;
Team members — add the people you work with. Assign tasks to them. See who has too much on their plate before they send you a panic message at 11 PM.&lt;br&gt;
Work hours analytics — full time tracking with real reports. Not just "hours logged" — actual breakdown by client, by project, by task. The numbers a freelancer or small agency actually needs to answer: am I making money on this client or just staying busy?&lt;br&gt;
Calendar integration — deadlines and tasks sync so your week looks like one thing, not three disconnected apps all demanding attention at the same time.&lt;br&gt;
We're on the finishing straight. The core is done. Right now I'm working through the last small fixes before the founding release — and building one more thing.&lt;br&gt;
What I'm building next:&lt;br&gt;
A direct AI assistant integration. Not "AI features" as a marketing checkbox — a way to create tasks, log work, and get a weekly project summary just by talking to it.&lt;br&gt;
The goal: you finish a client call, you tell the assistant what was discussed, the workspace updates itself. No context switching. No manual note-taking after every conversation.&lt;br&gt;
I'm building this because I tracked my own time one week and found I was spending 5+ hours just on admin — logging work, reorganizing tasks, writing status updates that nobody reads. That time should not exist.&lt;br&gt;
The wishlist is open right now.&lt;br&gt;
While the product isn't publicly released yet — founding access opens July 30 — the waitlist is live at &lt;a href="https://melororium.com" rel="noopener noreferrer"&gt;https://melororium.com&lt;/a&gt;&lt;br&gt;
If you reserve a spot now, you lock in the founding price: $199 one-time. No subscription. Ever.&lt;br&gt;
After launch the price goes up permanently. I'm not doing artificial scarcity — I just can't keep the founding price open indefinitely once real support costs kick in.&lt;br&gt;
We also built a referral system this weekend: bring 1 paying friend → 5% off your price. Bring 5 → 10%. Bring 30 → 50%. All the discount stacks get applied at launch in July. Full pricing breakdown at &lt;a href="https://melororium.com/pricing" rel="noopener noreferrer"&gt;https://melororium.com/pricing&lt;/a&gt;&lt;br&gt;
Day 7 real numbers:&lt;br&gt;
2 clicks from Google. First organic visitors ever. The site is already appearing at position 5 on some queries after 7 days — which genuinely surprised me&lt;br&gt;
Product Hunt launch: Tuesday June 9. Auto-scheduled. No PR, no budget, no influencer outreach. Just the story and whatever this community thinks of it&lt;br&gt;
0 paying customers. Still intentional. I want founding members who understand what they're getting into, not impulse purchases&lt;br&gt;
40+ published articles on the site. Probably only 8 of them are indexed by Google so far. The rest are sitting there waiting to be discovered&lt;br&gt;
The scary part about being this close to done: "done" isn't really a thing in software. You ship, people use it, something breaks, something's missing, something that seemed obvious to you makes no sense to anyone else.&lt;br&gt;
The real work starts July 30.&lt;br&gt;
But I know this: the people who've been running DIY stacks for two years — free Notion plus a spreadsheet plus three browser tabs — are going to open this and immediately understand it. No pitch needed. No onboarding email sequence. Just: oh, this is what I was trying to build myself.&lt;br&gt;
That's who I built it for.&lt;br&gt;
Question for this week: If you're running a DIY productivity stack right now — what's the one thing about it that drives you the most crazy? The thing you've accepted as "just how it is" but secretly hate?&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
