<?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.us-east-2.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 added Gemini + GPT-4 + Claude support in one settings toggle (Next.js SaaS)</title>
      <dc:creator>Кирилл Несмелов</dc:creator>
      <pubDate>Tue, 23 Jun 2026 12:30:06 +0000</pubDate>
      <link>https://dev.to/__615f3acc0/how-i-added-gemini-gpt-4-claude-support-in-one-settings-toggle-nextjs-saas-4elb</link>
      <guid>https://dev.to/__615f3acc0/how-i-added-gemini-gpt-4-claude-support-in-one-settings-toggle-nextjs-saas-4elb</guid>
      <description>&lt;p&gt;Build log from this week: I added multi-provider AI support to Melororium — users can now switch between Gemini, ChatGPT, and Claude in workspace settings, and it applies across the whole platform.&lt;/p&gt;

&lt;p&gt;Here's the pattern I used.&lt;/p&gt;

&lt;p&gt;The problem with hardcoding one provider&lt;br&gt;
I had Gemini wired directly into every AI action. Every ai.ts function called the Gemini SDK. If a user wanted a different provider, I'd have to touch every function.&lt;/p&gt;

&lt;p&gt;Beyond the code problem: different teams have different constraints. Some can't send data to Google's servers. Some are standardized on OpenAI internally. Forcing one provider was my product decision intruding on their infrastructure policy.&lt;/p&gt;

&lt;p&gt;The solution: one adapter layer&lt;br&gt;
I created a single getAIClient() function that reads the workspace's aiProvider setting from the DB and returns the right SDK client:&lt;/p&gt;

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

&lt;p&gt;switch (settings.aiProvider) {&lt;br&gt;
    case 'openai':&lt;br&gt;
      return new OpenAI({ apiKey: process.env.OPENAI_API_KEY });&lt;br&gt;
    case 'anthropic':&lt;br&gt;
      return new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });&lt;br&gt;
    default:&lt;br&gt;
      return new GoogleGenerativeAI(process.env.GEMINI_API_KEY);&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
Every AI action calls getAIClient() first. Prompt construction, response parsing, and error handling stay the same regardless of which client gets returned.&lt;/p&gt;

&lt;p&gt;Why BYOK matters here&lt;br&gt;
Melororium uses BYOK (bring your own key). Users paste their own API key in settings. This keeps costs transparent — they see exactly what they're spending on AI inference. It also keeps me out of the billing relationship entirely.&lt;/p&gt;

&lt;p&gt;The adapter pattern works cleanly with BYOK because each provider's key is stored separately in workspace settings.&lt;/p&gt;

&lt;p&gt;One thing I'd do differently&lt;br&gt;
Response shapes differ between providers. OpenAI, Anthropic, and Gemini return structured responses in slightly different formats. Right now I normalize them after the call in each action. A better approach would be a thin response adapter inside getAIClient() that normalizes everything before it returns. Planning to refactor that.&lt;/p&gt;

&lt;p&gt;Also shipped this week&lt;br&gt;
Along with the provider switching:&lt;/p&gt;

&lt;p&gt;AI analytics at two levels: all-projects overview and per-project drill-down&lt;br&gt;
Subtasks inside the task drawer (kanban nested structure)&lt;br&gt;
Work timer day view redesigned to show graphical time blocks per team member&lt;br&gt;
Per-worker timer breakdown with task-level detail&lt;br&gt;
Building Melororium as a pay-once team workspace (one-time purchase, no subscription). Writing about the build as I go.&lt;/p&gt;

&lt;p&gt;Anything you'd do differently with the provider switching pattern? Curious whether others use a similar adapter or something more formal.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>typescript</category>
      <category>ai</category>
      <category>saas</category>
    </item>
    <item>
      <title>I built a modular SaaS and let users bring their own AI. Here's why.</title>
      <dc:creator>Кирилл Несмелов</dc:creator>
      <pubDate>Tue, 16 Jun 2026 08:18:08 +0000</pubDate>
      <link>https://dev.to/__615f3acc0/i-built-a-modular-saas-and-let-users-bring-their-own-ai-heres-why-3e51</link>
      <guid>https://dev.to/__615f3acc0/i-built-a-modular-saas-and-let-users-bring-their-own-ai-heres-why-3e51</guid>
      <description>&lt;p&gt;I've been building Melororium — a lifetime project management workspace for small teams — and the two most interesting product decisions I made weren't about features. They were about what not to include by default.&lt;/p&gt;

&lt;p&gt;The architecture problem with "all-in-one" tools&lt;/p&gt;

&lt;p&gt;Every project management tool eventually tries to be everything. ClickUp added Docs. Notion added tasks. Asana added timelines. The result is tools that are bloated for most users and still missing the one thing you actually need.&lt;/p&gt;

&lt;p&gt;When I started building Melororium, I kept asking: what does every team actually use every single day?&lt;/p&gt;

&lt;p&gt;The answer was shorter than I expected:&lt;/p&gt;

&lt;p&gt;Task management (Kanban + List + Calendar)&lt;br&gt;
Time tracking inside tasks&lt;br&gt;
Client CRM&lt;br&gt;
Financial reporting + invoicing&lt;br&gt;
Team management + work reports&lt;br&gt;
Dashboards&lt;br&gt;
That became the 12 core modules — shipped in every plan, no exceptions.&lt;/p&gt;

&lt;p&gt;Everything else became opt-in. Not locked behind a higher tier. Just... not forced on you if you don't need it.&lt;/p&gt;

&lt;p&gt;The AI decision&lt;/p&gt;

&lt;p&gt;This is the one I want to talk about most.&lt;/p&gt;

&lt;p&gt;When I built the first version of AI features, the obvious move was to pick one provider, wrap it, charge for it in a premium tier.&lt;/p&gt;

&lt;p&gt;I didn't do that. Here's why.&lt;/p&gt;

&lt;p&gt;First: teams already have AI preferences. Some are locked into OpenAI for compliance reasons. Some prefer Claude's reasoning for analytical tasks. Some use Gemini because they're already in the Google ecosystem. Making them switch providers — or pay twice for the same model through my platform — felt wrong.&lt;/p&gt;

&lt;p&gt;Second: the margin math is bad for both sides. If I host the AI calls, I'm paying inference costs and either subsidizing them or marking them up. Neither is great. The user pays more than they should. I'm running a business I didn't sign up for.&lt;/p&gt;

&lt;p&gt;Third: AI usage patterns are unpredictable. Some teams will use the AI analytics module ten times a day. Others will open it once a month. Flat-rate AI pricing is always wrong for someone.&lt;/p&gt;

&lt;p&gt;So I built BYOK — bring your own API key.&lt;/p&gt;

&lt;p&gt;export async function getAIClient(workspaceId: string) {&lt;br&gt;
  const settings = await getWorkspaceSettings(workspaceId);&lt;br&gt;
  switch (settings.aiProvider) {&lt;br&gt;
    case 'openai':    return new OpenAI({ apiKey: settings.apiKey });&lt;br&gt;
    case 'anthropic': return new Anthropic({ apiKey: settings.apiKey });&lt;br&gt;
    default:          return new GoogleGenerativeAI(settings.apiKey);&lt;br&gt;
  }&lt;br&gt;
}&lt;br&gt;
One function. Every AI action in the codebase calls getAIClient() first. Provider switching is a single workspace setting. Keys are encrypted at rest, never logged.&lt;/p&gt;

&lt;p&gt;The user connects the provider they already use. They control the costs directly. I don't touch their inference budget.&lt;/p&gt;

&lt;p&gt;The modular pricing that followed&lt;/p&gt;

&lt;p&gt;Once I made AI opt-in, the same logic applied to other modules. Not every team needs the same stack.&lt;/p&gt;

&lt;p&gt;The architecture became:&lt;/p&gt;

&lt;p&gt;Core (12 modules) — included in every plan&lt;br&gt;
Add-ons — picked at purchase, locked per license&lt;br&gt;
Three tiers:&lt;/p&gt;

&lt;p&gt;Starter — $149 once → 4 users, all 12 core modules&lt;br&gt;
Agency — $299 once → 10 users, 12 core + pick 5 add-ons&lt;br&gt;
Studio — $499 once → 25 users, 12 core + all announced add-ons&lt;br&gt;
No recurring fees. No seat tax as you grow within your tier.&lt;/p&gt;

&lt;p&gt;What this forced technically&lt;/p&gt;

&lt;p&gt;Making add-ons genuinely modular — not just hidden behind a feature flag — meant the core and add-on layers had to be cleanly separated from day one.&lt;/p&gt;

&lt;p&gt;Every module has its own data schema, its own API routes, its own UI surface. The workspace config object tracks which add-ons are unlocked. Every protected route checks the license before rendering.&lt;/p&gt;

&lt;p&gt;It's more work upfront. But it means the codebase scales to 40 modules without becoming a tangled mess of conditional rendering and permission checks scattered everywhere.&lt;/p&gt;

&lt;p&gt;Stack, if you're curious&lt;/p&gt;

&lt;p&gt;Next.js 14 App Router, TypeScript, Tailwind. Vercel for deployment. The AI layer sits behind a clean adapter so swapping providers is a 5-line change per new integration.&lt;/p&gt;

&lt;p&gt;Pre-launch now, founding price closes July 30. If you're building something and want to see how the modular add-on system works in practice, drop a question below — happy to go deeper on any part of the architecture.&lt;/p&gt;

</description>
      <category>buildinpublic</category>
      <category>saas</category>
      <category>nextjs</category>
      <category>typescript</category>
    </item>
    <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>
