<?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: Gautam Suthar</title>
    <description>The latest articles on DEV Community by Gautam Suthar (@gautamsuthar).</description>
    <link>https://dev.to/gautamsuthar</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%2F3397153%2F4d00f2f0-f32f-4d9c-9de4-92ad2c2ed8ce.jpg</url>
      <title>DEV Community: Gautam Suthar</title>
      <link>https://dev.to/gautamsuthar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gautamsuthar"/>
    <language>en</language>
    <item>
      <title>I didn't want a stranger's code holding my Discord bot token, so I wrote my own MCP server</title>
      <dc:creator>Gautam Suthar</dc:creator>
      <pubDate>Sat, 31 Oct 2026 15:24:07 +0000</pubDate>
      <link>https://dev.to/gautamsuthar/i-didnt-want-a-strangers-code-holding-my-discord-bot-token-so-i-wrote-my-own-mcp-server-2a0o</link>
      <guid>https://dev.to/gautamsuthar/i-didnt-want-a-strangers-code-holding-my-discord-bot-token-so-i-wrote-my-own-mcp-server-2a0o</guid>
      <description>&lt;p&gt;I wanted Claude Code to manage my Discord server. Create channels, sort out roles, clean up messages, check the audit log, the boring admin stuff.&lt;/p&gt;

&lt;p&gt;Community Discord MCP servers already exist. Every one of them needs your bot token. My bot has admin-level permissions on a server with real people in it. Handing that token to code I hadn't read felt like a bad trade for saving a weekend.&lt;/p&gt;

&lt;p&gt;So I wrote my own. It's small enough to read end to end, it runs locally in Docker, and it only does what I put in it.&lt;/p&gt;

&lt;p&gt;It's called discord-mcp, it's MIT licensed, and it's on 1.3.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;p&gt;67 tools across the parts of Discord you'd actually manage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;channels and categories, including permission overwrites and cloning&lt;/li&gt;
&lt;li&gt;roles: create, edit, reorder, assign&lt;/li&gt;
&lt;li&gt;moderation: kick, ban, timeout, nicknames, audit log&lt;/li&gt;
&lt;li&gt;messages: read, send with embeds, edit, delete, bulk delete, pins, reactions&lt;/li&gt;
&lt;li&gt;threads and forum posts&lt;/li&gt;
&lt;li&gt;invites, webhooks, emojis, stickers, scheduled events, server settings&lt;/li&gt;
&lt;li&gt;a permission audit, and server templates you can export and apply&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The direction matters: this isn't a way to talk to Claude from Discord. Claude manages the server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhxyvvywsqy8cgmsius25.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhxyvvywsqy8cgmsius25.gif" alt="GIF" width="760" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Safety first, then write tools
&lt;/h2&gt;

&lt;p&gt;The first release with write tools came after a release that was only safety rails. If something is going to delete channels on my server, the guard rails come first.&lt;/p&gt;

&lt;p&gt;Three settings narrow down what the bot can do beyond its Discord permissions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;READ_ONLY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;true&lt;/code&gt; exposes only read-only tools. Nothing can change.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALLOWED_GUILD_IDS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Servers the bot may touch. Every other server is hidden.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DISABLED_TOOLS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Switch off single tools by name.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A typo in any of them stops the server at startup. An unknown tool name or a server ID the bot isn't in is an error, not something silently ignored.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read-only mode can't drift
&lt;/h2&gt;

&lt;p&gt;The usual way to build a read-only mode is a list of "write tools" to skip. That list goes stale the first time someone adds a tool and forgets it.&lt;/p&gt;

&lt;p&gt;discord-mcp uses the MCP annotations instead. Every tool has to declare &lt;code&gt;readOnlyHint&lt;/code&gt;, every write tool has to declare &lt;code&gt;destructiveHint&lt;/code&gt;, and every destructive tool has to accept a &lt;code&gt;reason&lt;/code&gt; that ends up in the Discord audit log. Startup refuses any tool that breaks those rules.&lt;/p&gt;

&lt;p&gt;Read-only mode then just drops anything not marked read-only. The client and the filter read the same annotations, so they can't disagree.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tools I actually use
&lt;/h2&gt;

&lt;p&gt;The CRUD tools are the boring part. Two newer ones are why I keep it running.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;audit_permissions&lt;/code&gt; reads the whole server and flags what's risky: dangerous permissions on &lt;code&gt;@everyone&lt;/code&gt;, roles and bots with Administrator, staff channels everyone can see, verification level, content filter, moderator 2FA. It only reads, so it works in read-only mode too. The first time I ran it on my test server it flagged the bot's own Administrator role. Fair.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;export_template&lt;/code&gt; and &lt;code&gt;apply_template&lt;/code&gt; turn a server's roles, categories and channels into JSON and back. Apply only adds, never edits or deletes, and skips anything that already exists, so running it twice is safe. The whole template gets checked before the first change, with &lt;code&gt;dry_run&lt;/code&gt; if you just want to see what would happen. There's also a &lt;code&gt;setup_community_server&lt;/code&gt; prompt: describe the community you want, Claude drafts the layout, dry runs it, and applies it after you say yes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Errors that tell you what to fix
&lt;/h2&gt;

&lt;p&gt;Discord's answer to a missing permission is a bare 403. Write tools check permissions and role order before calling Discord, so you get this instead: which permission the bot is missing, or which role sits at or above the bot's highest role, and how to fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tested against a real server
&lt;/h2&gt;

&lt;p&gt;Unit tests cover the logic. They can't tell you how Discord actually behaves.&lt;/p&gt;

&lt;p&gt;So there are 35 integration tests that spawn the server over stdio and call tools through the MCP SDK client, the same path Claude uses, against a throwaway test server. The harness locks itself to that one server and refuses any server with more than 10 members, so a wrong ID can't hit a real community.&lt;/p&gt;

&lt;p&gt;They found two bugs the unit tests never would have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;list_role_members&lt;/code&gt; fetched all members on every call. Discord rate limits that to about once per 30 seconds per server, so the second call failed. Now it loads members once and the cache stays current after that.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_message&lt;/code&gt; kept showing reactions that had already been removed. Discord leaves out &lt;code&gt;reactions&lt;/code&gt; when there are none, and discord.js only patches its cached copy with fields that are present. Dropping the cached message before fetching fixed it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;You need a Discord bot with the permissions you want it to have. The README walks through that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker pull ghcr.io/callmegautam/discord-mcp:1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put your token in a &lt;code&gt;.env&lt;/code&gt; and add it to Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add discord &lt;span class="nt"&gt;-s&lt;/span&gt; user &lt;span class="nt"&gt;--&lt;/span&gt; docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;--env-file&lt;/span&gt; /absolute/path/to/.env ghcr.io/callmegautam/discord-mcp:1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works with any MCP client, same command. Start with &lt;code&gt;READ_ONLY=true&lt;/code&gt; if you just want it to look around first.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/callmegautam/discord-mcp" rel="noopener noreferrer"&gt;https://github.com/callmegautam/discord-mcp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tool reference: &lt;a href="https://github.com/callmegautam/discord-mcp/blob/main/docs/tools.md" rel="noopener noreferrer"&gt;https://github.com/callmegautam/discord-mcp/blob/main/docs/tools.md&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's also an HTTP transport now (&lt;code&gt;TRANSPORT=http&lt;/code&gt; with a bearer token) if you'd rather run one long-lived container than spawn one per session.&lt;/p&gt;

&lt;p&gt;If something breaks on your server, open an issue. The things I couldn't test live yet are kick, ban and timeout on a real member, announcement and stage channels, and private threads.&lt;/p&gt;




&lt;p&gt;I hang out with other devs in The Basement, my Discord server. Questions, side projects, and a contributor cup every month or so. &lt;a href="https://discord.gg/BqAK3RNWeP" rel="noopener noreferrer"&gt;https://discord.gg/BqAK3RNWeP&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>discord</category>
      <category>typescript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Templates don't scale, so I rebuilt my scaffolder around composable blueprints</title>
      <dc:creator>Gautam Suthar</dc:creator>
      <pubDate>Tue, 20 Oct 2026 05:54:28 +0000</pubDate>
      <link>https://dev.to/gautamsuthar/templates-dont-scale-so-i-rebuilt-my-scaffolder-around-composable-blueprints-2f6c</link>
      <guid>https://dev.to/gautamsuthar/templates-dont-scale-so-i-rebuilt-my-scaffolder-around-composable-blueprints-2f6c</guid>
      <description>&lt;p&gt;Every project scaffolder I've used works the same way. You pick a template, it copies a folder, and you start deleting what you didn't want.&lt;/p&gt;

&lt;p&gt;Atarashi v0.6 worked that way too. It had five template folders. Then the obvious problem showed up: every new combination of framework, database and ORM meant another folder to write and maintain. Fastify with Postgres and Drizzle? New folder. Same thing with Prisma? Another one. The number of folders grows with every option you add, and none of them is ever quite what you wanted.&lt;/p&gt;

&lt;p&gt;So for 1.0 I threw the template engine out and rebuilt it around composition.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with templates
&lt;/h2&gt;

&lt;p&gt;A template folder is a snapshot of one set of decisions. Two things go wrong with that.&lt;/p&gt;

&lt;p&gt;First, you can't change one decision without hand-editing generated code. Want Hono instead of Express? You're now rewriting the template's output by hand.&lt;/p&gt;

&lt;p&gt;Second, nothing records what you chose. Three weeks later you want structured logging, and the scaffolder can't help, because it has no idea what your project is made of.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blueprints instead of folders
&lt;/h2&gt;

&lt;p&gt;Atarashi 1.0 builds a project out of small units called blueprints. There are 36 of them: HTTP frameworks (express, fastify, hono), databases (postgres, mysql, mongodb, sqlite), ORMs (drizzle, prisma, mongoose), auth, testing, linting, docker, CI, and React, Angular and Next front ends.&lt;/p&gt;

&lt;p&gt;The important part: blueprints never name each other. They declare capabilities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"orm/drizzle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"provides"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"orm:drizzle"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"requires"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"database:sql"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"conflicts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"orm"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drizzle doesn't know about Postgres. It says "I need a SQL database", and anything that provides &lt;code&gt;database:sql&lt;/code&gt; satisfies it. That one indirection is what makes every combination work without a template for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  It refuses things that can't work
&lt;/h2&gt;

&lt;p&gt;Because every blueprint declares what it needs and what it conflicts with, Atarashi can check the whole plan before it writes a single file.&lt;/p&gt;

&lt;p&gt;Ask for Drizzle with no database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ atarashi new my-api --add orm/drizzle
✖ orm/drizzle requires `database:sql`, and 3 blueprints provide it
    → Add `--add db/mysql` to choose it
    → Add `--add db/postgres` to choose it
    → Add `--add db/sqlite` to choose it
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ask for two databases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ atarashi new my-api --add db/postgres --add db/mongodb
✖ Cannot combine db/mongodb with db/postgres: both claim `database`, and only one is allowed
    → Remove one of them: `--remove db/postgres`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No half-generated directory to clean up. And when exactly one blueprint can satisfy a requirement, it gets added for you. Pick Fastify and it pulls in &lt;code&gt;validation/zod&lt;/code&gt;, because the env config needs it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ atarashi new my-api --http fastify --db postgres --orm drizzle --auth jwt
✔ Created my-api

  14 files · 14 dependencies · npm
  + validation/zod (required by http/fastify → config:env)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  It remembers what you chose
&lt;/h2&gt;

&lt;p&gt;Every generated project gets an &lt;code&gt;atarashi.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"blueprints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"core/node-ts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"validation/zod"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"db/postgres"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"orm/drizzle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http/fastify"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"auth/jwt"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"answers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"auth.expiry"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"7d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"db.name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"my-api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"db.pool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what templates are missing. Because the project knows what it's made of, you can keep adding to it later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ atarashi add obs/pino
Adding obs/pino to my-api
  · .env.example (update)
  · atarashi.json (update)
  · package.json (update)
  · src/config/env.ts (update)
  + src/lib/logger.ts (new)
✔ Added obs/pino
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;add&lt;/code&gt; re-renders only what the new blueprint contributes and splices it into your existing files. Files you've edited by hand are never silently overwritten.&lt;/p&gt;

&lt;p&gt;The same file also makes generation reproducible: &lt;code&gt;atarashi new --from atarashi.json&lt;/code&gt; gives you a byte-identical project (generated secrets in &lt;code&gt;.env&lt;/code&gt; are the exception, on purpose).&lt;/p&gt;

&lt;h2&gt;
  
  
  The bar every project has to clear
&lt;/h2&gt;

&lt;p&gt;A generated project has to install, build, lint, test, boot and answer &lt;code&gt;GET /health&lt;/code&gt; with no edits from you. If a combination can't hit that, it's a bug.&lt;/p&gt;

&lt;p&gt;A few other things I cared about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--dry-run&lt;/code&gt; prints the plan and writes nothing&lt;/li&gt;
&lt;li&gt;it works offline, the blueprints ship inside the package&lt;/li&gt;
&lt;li&gt;nothing it generates depends on Atarashi, and &lt;code&gt;atarashi eject&lt;/code&gt; cuts the last link&lt;/li&gt;
&lt;li&gt;third-party blueprints use the same format and validator as the built-in ones. Publish one as &lt;code&gt;atarashi-blueprint-*&lt;/code&gt; on npm and anyone can use it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The fastest way is a preset, a set of blueprints known to work together:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx atarashi@latest new my-api &lt;span class="nt"&gt;--preset&lt;/span&gt; backend-ts &lt;span class="nt"&gt;--yes&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;my-api &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pnpm dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;backend-ts&lt;/code&gt; is Express, Postgres, Drizzle, JWT and Docker with strict TypeScript. There are six presets in total, from &lt;code&gt;backend-minimal&lt;/code&gt; to &lt;code&gt;fullstack-react&lt;/code&gt;. Or pick pieces yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx atarashi@latest new my-api &lt;span class="nt"&gt;--http&lt;/span&gt; hono &lt;span class="nt"&gt;--db&lt;/span&gt; sqlite &lt;span class="nt"&gt;--orm&lt;/span&gt; drizzle &lt;span class="nt"&gt;--tests&lt;/span&gt; vitest &lt;span class="nt"&gt;--lint&lt;/span&gt; biome
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or run &lt;code&gt;atarashi&lt;/code&gt; with no arguments for the wizard.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I want to hear
&lt;/h2&gt;

&lt;p&gt;It's 1.0 but brand new. The most useful thing you can tell me is which combinations break: a set of flags that should work but gets rejected, or a generated project that doesn't build. There's a pinned tracking issue for exactly that.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/callmegautam/atarashi" rel="noopener noreferrer"&gt;https://github.com/callmegautam/atarashi&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://atarashi.gautamsuthar.in" rel="noopener noreferrer"&gt;https://atarashi.gautamsuthar.in&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tracking issue: &lt;a href="https://github.com/callmegautam/atarashi/issues/4" rel="noopener noreferrer"&gt;https://github.com/callmegautam/atarashi/issues/4&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>node</category>
      <category>typescript</category>
      <category>opensource</category>
      <category>cli</category>
    </item>
    <item>
      <title>Developer Matureness</title>
      <dc:creator>Gautam Suthar</dc:creator>
      <pubDate>Fri, 14 Nov 2025 13:30:16 +0000</pubDate>
      <link>https://dev.to/gautamsuthar/developer-matureness-3777</link>
      <guid>https://dev.to/gautamsuthar/developer-matureness-3777</guid>
      <description>&lt;p&gt;I seriously can’t stand these junior developers who treat every tiny error like a national emergency. The moment something breaks, they sprint to a senior or drop a screenshot in the group chat without even reading the first line of the stack trace. It’s wild. When I was at their stage, I used to sit with an error for hours, sometimes days, poking at it from every angle, searching, reading docs, trying things, breaking things, rebuilding the project twice because maybe the universe just needed a reset. That’s how you build intuition. That’s how you grow.&lt;/p&gt;

&lt;p&gt;The funniest part? They act like the internet doesn’t exist. Docs? Never heard of them. Stack Overflow? Apparently a myth from ancient times. Even AI is right there, literally begging to help, but somehow the default response is: “bhaiya error aa gaya, dekhlo.” It’s not even about being a hero and solving everything yourself. it’s about respecting the craft. Respecting the people around you. Seniors aren’t Google. They’re not a walking Stack Overflow clone with free time to babysit your missing comma.&lt;/p&gt;

&lt;p&gt;I don’t get why some people treat debugging like a personal insult. As if the moment something goes wrong, it’s proof that they suck and someone else needs to come pat their head and fix it. Debugging is half the job. Half of engineering is staring at error messages and slowly negotiating with your own sanity until the code finally behaves. If you avoid that part, you never develop the instincts that make you a real dev.&lt;/p&gt;

&lt;p&gt;And the worst part is when they skip the basics. They don’t isolate the issue, don’t try to reproduce it, don’t check what they changed five minutes ago. They don’t even &lt;em&gt;read&lt;/em&gt; the error. They just panic. They just want someone else to tell them the answer as fast as possible so they can paste it, pretend they understand it, and move on without learning anything.&lt;/p&gt;

&lt;p&gt;Meanwhile seniors have their own fires to put out. They’re juggling deployments, code reviews, deadlines, and their own bugs. and junior devs casually drop in like, “bro, help.” Some juniors are even busy with their own tasks. Time is expensive. Attention is expensive. Running to AI is fine, that costs no one anything. But running to a human for every hiccup? That’s just straight-up disrespectful.&lt;/p&gt;

&lt;p&gt;This isn’t about gatekeeping or flexing. It’s about maturity. Writing code is easy. Searching for answers is easy. What takes maturity is the willingness to sit with your mistakes, poke at them, dig into the logs, question your assumptions, and slowly unravel the mess you made. The uncomfortable truth is: if you keep running from the discomfort, you’ll always be stuck at the same level.&lt;/p&gt;

&lt;p&gt;Growing as a developer is not some magical jump. It’s built from tiny moments where you choose to think instead of panic, read instead of whine, experiment instead of complain. Debugging is not punishment. Debugging is training. And if you always outsource that part, you’re basically outsourcing your future self.&lt;/p&gt;

&lt;p&gt;So yeah, stop behaving childish. Stop treating every error like a dead end. Slow down, breathe, investigate, try things, break things intentionally, learn why things fail. Ask for help when it’s truly needed, not when you’re just feeling lazy or scared. Be a developer, not a notification.&lt;/p&gt;

&lt;p&gt;This industry rewards people who can think. Errors are invitations to think. If you keep running from them, you’ll never cross the threshold from junior to actual engineer. Debug with some dignity. That's where the real growth lives.&lt;/p&gt;

</description>
      <category>developer</category>
      <category>maturness</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
