<?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: Mothilal M</title>
    <description>The latest articles on DEV Community by Mothilal M (@mothilal).</description>
    <link>https://dev.to/mothilal</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%2F3784855%2Fffb9ab5f-2204-4ed8-9903-572931788ec6.png</url>
      <title>DEV Community: Mothilal M</title>
      <link>https://dev.to/mothilal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mothilal"/>
    <language>en</language>
    <item>
      <title>365 Days of Consistency: What My 1-Year GitHub Streak Taught Me</title>
      <dc:creator>Mothilal M</dc:creator>
      <pubDate>Mon, 27 Jul 2026 12:50:11 +0000</pubDate>
      <link>https://dev.to/mothilal/365-days-of-consistency-what-my-1-year-github-streak-taught-me-1gjh</link>
      <guid>https://dev.to/mothilal/365-days-of-consistency-what-my-1-year-github-streak-taught-me-1gjh</guid>
      <description>&lt;p&gt;Today I crossed one of the most meaningful milestones in my developer journey: &lt;strong&gt;a 1-year GitHub contribution streak&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It is not just a number on a calendar.&lt;/p&gt;

&lt;p&gt;It represents a year of showing up, writing code, fixing bugs, learning new tools, building projects, and staying consistent even on the days when motivation was low.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this streak matters to me
&lt;/h2&gt;

&lt;p&gt;A streak is easy to measure, but consistency is hard to build.&lt;/p&gt;

&lt;p&gt;For me, this year was not only about committing code. It was about developing a habit.&lt;/p&gt;

&lt;p&gt;A habit of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;learning something new every day&lt;/li&gt;
&lt;li&gt;pushing small improvements instead of waiting for perfect ones&lt;/li&gt;
&lt;li&gt;staying connected to my projects&lt;/li&gt;
&lt;li&gt;not letting gaps break momentum&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That mindset changed how I work.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned in one year
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Small work still counts
&lt;/h3&gt;

&lt;p&gt;Not every day is about massive features or big releases.&lt;/p&gt;

&lt;p&gt;Some days it was a tiny fix.&lt;br&gt;
Some days it was documentation.&lt;br&gt;
Some days it was refactoring.&lt;br&gt;
Some days it was just improving one small part of a project.&lt;/p&gt;

&lt;p&gt;But all of it added up.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Consistency beats intensity
&lt;/h3&gt;

&lt;p&gt;There were days when I felt highly productive and days when I did not.&lt;/p&gt;

&lt;p&gt;The key was not to be perfect every day.&lt;/p&gt;

&lt;p&gt;The key was to stay present.&lt;/p&gt;

&lt;p&gt;Even small progress kept the streak alive and kept my mind in building mode.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Momentum is powerful
&lt;/h3&gt;

&lt;p&gt;Once consistency becomes a habit, it starts working for you.&lt;/p&gt;

&lt;p&gt;You think more clearly.&lt;br&gt;
You start building faster.&lt;br&gt;
You recover from breaks sooner.&lt;br&gt;
You stop overthinking and start shipping.&lt;/p&gt;

&lt;h2&gt;
  
  
  What kept me going
&lt;/h2&gt;

&lt;p&gt;A few things helped me stay consistent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;having real projects to work on&lt;/li&gt;
&lt;li&gt;tracking progress visually&lt;/li&gt;
&lt;li&gt;making code a daily routine&lt;/li&gt;
&lt;li&gt;enjoying the process of building&lt;/li&gt;
&lt;li&gt;reminding myself that progress compounds&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What this year looked like
&lt;/h2&gt;

&lt;p&gt;This streak was not limited to one language or one stack.&lt;/p&gt;

&lt;p&gt;It included learning, building, debugging, experimenting, and improving across different parts of my workflow.&lt;/p&gt;

&lt;p&gt;What mattered most was showing up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;A 1-year streak is not the finish line.&lt;/p&gt;

&lt;p&gt;It is proof that consistency works.&lt;/p&gt;

&lt;p&gt;It taught me that real growth comes from repeated effort, not occasional bursts of motivation.&lt;/p&gt;

&lt;p&gt;One year later, I can say this clearly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;showing up every day changes you.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And I am excited to keep building from here.&lt;/p&gt;




&lt;p&gt;If you are trying to build a streak of your own, start small.&lt;br&gt;
Commit something meaningful.&lt;br&gt;
Repeat it tomorrow.&lt;br&gt;
Then keep going.****&lt;/p&gt;

</description>
      <category>github</category>
      <category>consistency</category>
      <category>productivity</category>
      <category>developers</category>
    </item>
    <item>
      <title>We Built an AI Coding Assistant That Refuses to Finish Your Code</title>
      <dc:creator>Mothilal M</dc:creator>
      <pubDate>Sun, 26 Jul 2026 15:06:19 +0000</pubDate>
      <link>https://dev.to/10xscale/we-built-an-ai-coding-assistant-that-refuses-to-finish-your-code-3ndg</link>
      <guid>https://dev.to/10xscale/we-built-an-ai-coding-assistant-that-refuses-to-finish-your-code-3ndg</guid>
      <description>&lt;p&gt;A student finished our twelve-week backend course with near-perfect assignment scores.&lt;/p&gt;

&lt;p&gt;In a mock interview two days later, he couldn't write a loop that summed a list of numbers. Not "wrote it inefficiently" — couldn't start. He sat there waiting for something to autocomplete.&lt;/p&gt;

&lt;p&gt;He hadn't cheated. He'd used ChatGPT and Copilot exactly the way we all do at work: describe the problem, accept the suggestion, move on. Twelve weeks of that. Every assignment passed. Nothing learned.&lt;/p&gt;

&lt;p&gt;That's when it clicked for us. &lt;strong&gt;For a working developer, an AI that finishes your code is a productivity tool. For a student, it's a machine that removes the struggle — and the struggle was the entire point.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So we built the opposite.&lt;/p&gt;




&lt;h2&gt;
  
  
  The rule: do the plumbing, stop at the lesson
&lt;/h2&gt;

&lt;p&gt;Every coding exercise has two kinds of code in it.&lt;/p&gt;

&lt;p&gt;There's the part that &lt;em&gt;is&lt;/em&gt; the lesson — the recursion, the reducer, the SQL join, the async coordination. Whatever the module is actually teaching.&lt;/p&gt;

&lt;p&gt;And there's everything else. Imports. Reading the CSV. The &lt;code&gt;if __name__ == "__main__"&lt;/code&gt; block. Wiring up the router. Error handling around the bit that matters. Boilerplate the student has already done fifteen times, or won't be tested on, or that's just noise between them and the concept.&lt;/p&gt;

&lt;p&gt;A tutor who does the second kind for you is helpful. A tutor who does the first kind for you is stealing your education.&lt;/p&gt;

&lt;p&gt;So that became the rule our assistant follows:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Write the scaffolding in full. Stop dead at the learning-critical part. Say what you did and what they must finish.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In practice a response looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;generate_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_filepath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;aggregated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;csv_filepath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;utf-8&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DictReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="c1"&gt;# TODO(student): Implement the core aggregation logic here.
&lt;/span&gt;                &lt;span class="c1"&gt;# Each `row` is a dict like {'Category': 'A', 'Value': '100'}.
&lt;/span&gt;                &lt;span class="c1"&gt;# Update `aggregated` based on what you're reporting.
&lt;/span&gt;                &lt;span class="k"&gt;pass&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;aggregated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Error: &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;csv_filepath&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; not found.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;I've scaffolded the CSV reader and the output formatting. You need to implement the aggregation inside the loop.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The file opens in their editor. It runs. It just doesn't do anything yet — and the one thing standing between "runs" and "works" is precisely the thing the module is teaching.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building it on AgentFlow
&lt;/h2&gt;

&lt;p&gt;We built this on &lt;a href="https://github.com/10xHub/Agentflow" rel="noopener noreferrer"&gt;AgentFlow&lt;/a&gt;, our own Python framework for AI agents. It's MIT licensed and on PyPI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;10xscale-agentflow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why not LangGraph?&lt;/strong&gt; We started there. We like the graph mental model — that part AgentFlow keeps. What we kept rebuilding by hand was everything &lt;em&gt;after&lt;/em&gt; the graph compiled: an API server, streaming that didn't fall over, persistence that survived a restart, a TypeScript client for the frontend. And we didn't want LangChain in the dependency tree.&lt;/p&gt;

&lt;p&gt;So AgentFlow ships the production stack in the box: graph orchestration, a REST + SSE server, a TypeScript SDK, three-layer memory (Redis cache, Postgres, vector store), native MCP, and parallel tool execution. It's LLM-agnostic — OpenAI, Gemini, Anthropic, or any OpenAI-compatible endpoint.&lt;/p&gt;

&lt;p&gt;The tutor itself is a small graph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;agentflow.graph&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StateGraph&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StateGraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TutorState&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tutor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tutor_agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tool_node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_entry_point&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tutor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_conditional_edges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tutor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;should_continue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_conditional_edges&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tools&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;route_after_tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tutor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tutor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;checkpointer&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;checkpointer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The checkpointer is the part that matters more than it looks. Conversation threads live in Postgres, so a student can close the laptop mid-exercise, come back the next evening, and the tutor still knows which milestone they're stuck on and what they already tried. No re-explaining.&lt;/p&gt;




&lt;h2&gt;
  
  
  The interesting bit: the agent's hands are in your editor
&lt;/h2&gt;

&lt;p&gt;Here's the architectural problem. The agent runs on our server. The student's code lives in their editor. How does a model on a server read a selection, or write a file, or highlight the three lines where the bug is?&lt;/p&gt;

&lt;p&gt;AgentFlow solves this with &lt;strong&gt;remote tools&lt;/strong&gt;, and it's my favourite feature in the framework.&lt;/p&gt;

&lt;p&gt;You register a tool from the &lt;em&gt;client&lt;/em&gt; — in our case, the VS Code extension:&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="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tools&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;read_code&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Read the student's code. Use want='selection' for what they've &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
               &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;highlighted, 'error_context' for the file plus last terminal output.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;want&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;range&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error_context&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;want&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// runs inside VS Code, with the full editor API available&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;editor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;activeTextEditor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;editor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&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="nx"&gt;editor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// publishes the schemas to the server&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model sees the tool schema and calls it like any other tool. But the server doesn't execute it — it returns a &lt;code&gt;RemoteToolCallBlock&lt;/code&gt;, the SDK spots it, runs your handler &lt;strong&gt;in the editor&lt;/strong&gt;, and sends the result back as a tool message. The graph picks up exactly where it left off.&lt;/p&gt;

&lt;p&gt;The model gets eyes and hands inside VS Code without a single line of code leaving the student's machine unless the agent explicitly asks for it. No shipping the whole file on every message. No stale context.&lt;/p&gt;

&lt;p&gt;The same mechanism drives the writing side — &lt;code&gt;write_file&lt;/code&gt;, &lt;code&gt;highlight&lt;/code&gt;, &lt;code&gt;open_file&lt;/code&gt;, &lt;code&gt;propose_edit&lt;/code&gt;. The agent decides &lt;em&gt;what&lt;/em&gt; should happen; the editor decides &lt;em&gt;how&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The pedagogy is a markdown file, not an if-statement
&lt;/h2&gt;

&lt;p&gt;The thing I'd most want another team to steal: &lt;strong&gt;none of the teaching rules live in Python.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AgentFlow has a skills system — playbooks in markdown, loaded per turn based on what the student is doing. Ours reads like guidance to a human tutor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## The Hardest-Part Boundary&lt;/span&gt;

Learning-critical core -&amp;gt; coach, never solve.
Boilerplate and plumbing -&amp;gt; write it freely.

Milestone is "fetch and display data"?
&lt;span class="p"&gt;  -&lt;/span&gt; Stuck on the fetch -&amp;gt; that IS the lesson. Scaffold around it, hand it back.
&lt;span class="p"&gt;  -&lt;/span&gt; Stuck on the loading spinner CSS -&amp;gt; not the lesson. Just write it.

&lt;span class="gu"&gt;## Understanding Gate&lt;/span&gt;

Before marking anything complete, ask WHY they chose their approach.
Ask what happens at [edge case]. Passing tests is not understanding.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our instructional designers edit that file. They don't open a Python file, they don't wait for a deploy, they don't file a ticket. When a cohort struggles with a module, the person who understands &lt;em&gt;why&lt;/em&gt; changes the rules directly.&lt;/p&gt;

&lt;p&gt;That separation — orchestration in code, pedagogy in markdown — has been worth more than any individual feature.&lt;/p&gt;




&lt;h2&gt;
  
  
  Measuring who actually wrote the code
&lt;/h2&gt;

&lt;p&gt;Here's the part I think most AI-in-education tools are missing.&lt;/p&gt;

&lt;p&gt;If your AI can write code into a student's file, you have created a brand new way to cheat, and you have an obligation to measure it. Otherwise "completed the assignment" becomes meaningless.&lt;/p&gt;

&lt;p&gt;So every AI-originated edit in our extension goes through exactly one function, and that function records the event &lt;strong&gt;before&lt;/strong&gt; it writes:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;applyBlock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ai_applied&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;block&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// ledger first&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;vscode&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;workspace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// then the write&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Student keystrokes get counted from &lt;code&gt;onDidChangeTextDocument&lt;/code&gt;. The ratio between them is displayed live in the panel header — a small &lt;code&gt;typed 78%&lt;/code&gt; badge that both the student and their trainer can see.&lt;/p&gt;

&lt;p&gt;Two hard-won lessons on this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It has to be a single choke point.&lt;/strong&gt; The moment one code path writes to a file without going through it, the number silently becomes fiction — and a fiction that looks like data is worse than no data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build it on day one.&lt;/strong&gt; You cannot retrofit authorship tracking. The edits you didn't record are gone forever.&lt;/p&gt;

&lt;p&gt;A high AI-ratio isn't automatically bad, by the way. It's a &lt;em&gt;signal&lt;/em&gt;. It tells the tutor to probe harder before marking a milestone complete, and it tells a trainer which students to check on before the mock interview, not after.&lt;/p&gt;




&lt;h2&gt;
  
  
  What we've seen so far
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;[FILL IN — replace this section with your own numbers before publishing.]&lt;/strong&gt;&lt;br&gt;
Good things to include if you have them: how many students are using it, change in&lt;br&gt;
mock-interview pass rate, average authorship ratio per module, or a direct student&lt;br&gt;
quote. If you don't have hard data yet, say so plainly — "we're one cohort in and&lt;br&gt;
still collecting data" is more credible than a number you can't defend, and readers&lt;br&gt;
on dev.to will absolutely ask.&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;AgentFlow is open source and MIT licensed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/10xHub/Agentflow" rel="noopener noreferrer"&gt;github.com/10xHub/Agentflow&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs:&lt;/strong&gt; &lt;a href="https://10xhub.github.io/agentflow-docs/" rel="noopener noreferrer"&gt;10xhub.github.io/agentflow-docs&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Install:&lt;/strong&gt; &lt;code&gt;pip install 10xscale-agentflow 10xscale-agentflow-cli&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're building anything that puts an AI inside an editor, the remote-tools pattern is worth an hour of your time even if you use a different framework entirely. Being able to run a tool handler in the client while the graph stays on the server unlocks a category of product that's genuinely painful to build otherwise.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The question I keep going back and forth on:&lt;/strong&gt; should a student be &lt;em&gt;able&lt;/em&gt; to override the tutor and just get the full answer? There's a real argument that fighting a tool teaches nothing but resentment, and adults should get to choose. There's an equally real argument that the override becomes the default within a week.&lt;/p&gt;

&lt;p&gt;We landed on "no override, but unlimited explanations." I'm genuinely not certain we got it right. What would you have done?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vscode</category>
      <category>python</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Built My Own VS Code Server in 5 Minutes, Now My IDE Lives in a Browser Tab</title>
      <dc:creator>Mothilal M</dc:creator>
      <pubDate>Sat, 25 Jul 2026 17:41:34 +0000</pubDate>
      <link>https://dev.to/mothilal/i-built-my-own-vs-code-server-in-5-minutes-now-my-ide-lives-in-a-browser-tab-1cnl</link>
      <guid>https://dev.to/mothilal/i-built-my-own-vs-code-server-in-5-minutes-now-my-ide-lives-in-a-browser-tab-1cnl</guid>
      <description>&lt;p&gt;I have a confession: I own three machines and none of them agree on anything.&lt;/p&gt;

&lt;p&gt;The desktop has my Node version. The laptop has the Python environment. The old ThinkPad in the corner has the one Docker setup that actually works. Every time I switch, I lose twenty minutes to &lt;code&gt;nvm install&lt;/code&gt;, missing extensions, and a settings.json that drifted apart three months ago.&lt;/p&gt;

&lt;p&gt;So last week I fixed it. Not with dotfile syncing, not with a $20/month cloud IDE subscription. I put &lt;strong&gt;VS Code itself in a browser tab&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Real VS Code. Real terminal. My extensions. Running at &lt;code&gt;localhost:8080&lt;/code&gt; in a Docker container.&lt;/p&gt;

&lt;p&gt;It took about five minutes. Here's exactly how.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 30-second version
&lt;/h2&gt;

&lt;p&gt;If you have Docker, you are one command away:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"demo1234"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  codercom/code-server:latest &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--bind-addr&lt;/span&gt; 0.0.0.0:8080 &lt;span class="nt"&gt;--auth&lt;/span&gt; password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:8080&lt;/code&gt;, type &lt;code&gt;demo1234&lt;/code&gt;, and you're staring at VS Code.&lt;/p&gt;

&lt;p&gt;Not a Monaco editor in a box. Not a "code playground." The actual workbench — activity bar, file explorer, tabs, command palette (&lt;code&gt;Ctrl+Shift+P&lt;/code&gt;), settings UI, extension marketplace.&lt;/p&gt;

&lt;p&gt;Now hit &lt;code&gt;Ctrl+`&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A real terminal.&lt;/strong&gt; Bash, running inside the container. &lt;code&gt;apt install&lt;/code&gt;, &lt;code&gt;npm install&lt;/code&gt;, &lt;code&gt;python3&lt;/code&gt; — it's a full Linux box, and you're driving it from a browser.&lt;/p&gt;

&lt;p&gt;That's the moment it clicked for me. This isn't a toy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wait — what is this thing?
&lt;/h2&gt;

&lt;p&gt;It's &lt;a href="https://github.com/coder/code-server" rel="noopener noreferrer"&gt;code-server&lt;/a&gt;, built by the team at Coder. It takes the open-source core of VS Code and runs it as a web server. Your browser is the window; everything else happens in the container.&lt;/p&gt;

&lt;p&gt;It's MIT licensed. It's free. There is no account, no telemetry you didn't ask for, and no monthly bill.&lt;/p&gt;




&lt;h2&gt;
  
  
  Making it survive a restart
&lt;/h2&gt;

&lt;p&gt;The command above uses &lt;code&gt;--rm&lt;/code&gt;, which means the moment you stop it, everything vanishes. Extensions, settings, files — gone. Fine for a test drive, useless for real work.&lt;/p&gt;

&lt;p&gt;For a setup you'll actually use, you want three things to persist: your &lt;strong&gt;projects&lt;/strong&gt;, your &lt;strong&gt;settings&lt;/strong&gt;, and your &lt;strong&gt;extensions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Make a folder and drop in a &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;code-server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;codercom/code-server:latest&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;code-server&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unless-stopped&lt;/span&gt;
    &lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1000:1000"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pick-something-better-than-this"&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./projects:/home/coder/projects&lt;/span&gt;   &lt;span class="c1"&gt;# your code&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./config:/home/coder/.config&lt;/span&gt;      &lt;span class="c1"&gt;# settings.json, keybindings&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./local:/home/coder/.local&lt;/span&gt;        &lt;span class="c1"&gt;# installed extensions&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--bind-addr=0.0.0.0:8080"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--auth=password"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--disable-telemetry"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/home/coder/projects"&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:8080"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; projects config &lt;span class="nb"&gt;local
&lt;/span&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Back to &lt;code&gt;localhost:8080&lt;/code&gt;. Install your extensions, tweak your theme, write some code. Now &lt;code&gt;docker compose restart&lt;/code&gt; — or reboot your machine entirely — and it all comes back exactly as you left it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The bug that ate 20 minutes of my life
&lt;/h2&gt;

&lt;p&gt;Here's the part most tutorials skip, and it's the part that will actually bite you.&lt;/p&gt;

&lt;p&gt;My first attempt used Docker &lt;strong&gt;named volumes&lt;/strong&gt; instead of folders. Looked cleaner. The container immediately went into a crash loop, and the logs said this, over and over:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;[error] EACCES: permission denied, mkdir '/home/coder/.config/code-server'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what's happening. code-server runs as the &lt;code&gt;coder&lt;/code&gt; user — &lt;strong&gt;UID 1000&lt;/strong&gt; — not as root. When Docker creates an empty named volume for a path that doesn't already exist in the image, that volume comes up owned by &lt;code&gt;root&lt;/code&gt;. So code-server boots, tries to create its config directory, gets slapped by the filesystem, and dies. Restart policy brings it back. It dies again. Forever.&lt;/p&gt;

&lt;p&gt;Two ways out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use bind mounts and pin the user&lt;/strong&gt; — that's the &lt;code&gt;user: "1000:1000"&lt;/code&gt; line in the compose file above. On most Linux systems your own account &lt;em&gt;is&lt;/em&gt; UID 1000, so the folders you create are already owned correctly and everything just works. Check with &lt;code&gt;id -u&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Or chown the volume first&lt;/strong&gt;, if you'd rather keep named volumes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; code_config:/data alpine &lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; 1000:1000 /data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I went with bind mounts. Beyond dodging the bug, I can &lt;code&gt;ls&lt;/code&gt; into &lt;code&gt;./config&lt;/code&gt; and see my actual &lt;code&gt;settings.json&lt;/code&gt; sitting there in plain sight, which is worth something on its own.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you actually get
&lt;/h2&gt;

&lt;p&gt;A few things surprised me:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Extensions work.&lt;/strong&gt; Themes, language support, formatters, GitLens — install them from the built-in marketplace like normal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Port forwarding is automatic.&lt;/strong&gt; Start a dev server inside the terminal on port 3000 and VS Code notices and offers to forward it. Your Vite app shows up in another tab.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It runs beautifully on a tablet.&lt;/strong&gt; This was the unexpected win. An iPad with a keyboard case is suddenly a genuinely usable dev machine. Not "usable in an emergency" — actually fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's a clean room.&lt;/strong&gt; Every project can have its own container with its own toolchain. No more version conflicts bleeding across projects, no more "which Python is this."&lt;/p&gt;




&lt;h2&gt;
  
  
  The honest limitations
&lt;/h2&gt;

&lt;p&gt;I'd rather you hear this from me than discover it at 1am.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Some Microsoft extensions will not work.&lt;/strong&gt; Pylance, C# Dev Kit, Remote Development, Live Share — these are proprietary, and their licenses restrict them to official Microsoft products. code-server pulls from the &lt;a href="https://open-vsx.org/" rel="noopener noreferrer"&gt;Open VSX&lt;/a&gt; registry instead. For Python you'll fall back to Jedi or basedpyright. They're fine. They're not Pylance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Browser keyboard shortcuts fight you.&lt;/strong&gt; &lt;code&gt;Ctrl+W&lt;/code&gt; closing your entire tab instead of the editor is a rite of passage. Installing it as a PWA fixes most of this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's as fast as the machine it runs on.&lt;/strong&gt; On localhost that's instant. Put it on a $5 VPS and you'll feel every keystroke.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where this actually earns its keep
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One environment, every device.&lt;/strong&gt; Desktop, laptop, tablet — same tab, same state, same everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Onboarding that takes a minute.&lt;/strong&gt; Hand someone a compose file instead of a three-page README.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teaching and workshops.&lt;/strong&gt; Everyone gets an identical environment. No more "it doesn't work on mine."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reviewing a PR from a borrowed laptop.&lt;/strong&gt; Just log in.&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Seriously — go run the one-liner at the top. It costs you thirty seconds and one &lt;code&gt;docker pull&lt;/code&gt;, and the moment you hit &lt;code&gt;Ctrl+`&lt;/code&gt; and a real shell opens inside a browser tab, you'll get why I bothered writing this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"demo1234"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  codercom/code-server:latest &lt;span class="nt"&gt;--bind-addr&lt;/span&gt; 0.0.0.0:8080 &lt;span class="nt"&gt;--auth&lt;/span&gt; password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;localhost:8080&lt;/code&gt;. Go.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you end up running this on a machine other people can reach, treat it seriously — that terminal is a full shell on your box. Put it behind a real password at minimum, and ideally behind HTTPS and a VPN or an auth proxy. That's a whole post of its own, and I'll write it if there's interest.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What would you use this for?&lt;/strong&gt; I'm curious whether the tablet thing lands for anyone else, or if that's just me.&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>docker</category>
      <category>productivity</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>We Finished Our AI Chess Coach: Meet Chaturanga ♟️</title>
      <dc:creator>Mothilal M</dc:creator>
      <pubDate>Sun, 07 Jun 2026 17:37:48 +0000</pubDate>
      <link>https://dev.to/mothilal/we-finished-our-ai-chess-coach-meet-chaturanga-3gel</link>
      <guid>https://dev.to/mothilal/we-finished-our-ai-chess-coach-meet-chaturanga-3gel</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/github-2026-05-21"&gt;GitHub Finish-Up-A-Thon Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Chaturanga&lt;/strong&gt; (चतुरङ्ग) is an AI-powered chess learning app for iOS &amp;amp; Android, built with Flutter. The idea is simple but stubborn: &lt;em&gt;playing&lt;/em&gt; chess and &lt;em&gt;understanding&lt;/em&gt; it should happen in the same place.&lt;/p&gt;

&lt;p&gt;You play real games against an on-device &lt;strong&gt;Stockfish&lt;/strong&gt; engine, and a grandmaster-style &lt;strong&gt;AI coach named "Acharya"&lt;/strong&gt; sits with you the whole time — you can ask "why that move?" or "what would a GM do here?" and get a clear, human explanation instead of a raw engine evaluation. After the game, a debrief shows your best move, your biggest blunder, and the one concept to take away.&lt;/p&gt;

&lt;p&gt;It's named after &lt;em&gt;chaturanga&lt;/em&gt;, the ancient Indian game that modern chess descends from — chess was born in India, and this app helps it teach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's actually in it:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;♟️ &lt;strong&gt;Play vs Stockfish&lt;/strong&gt; — on-device engine, adjustable skill (1–20), with a pure-Dart fallback engine on desktop.&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;AI coach (Acharya)&lt;/strong&gt; — context-aware (gets the live FEN + move history), supports &lt;strong&gt;OpenAI and Google Gemini&lt;/strong&gt;, streams replies token-by-token, and works with the built-in proxy &lt;em&gt;or&lt;/em&gt; your own API key (stored only in the device's secure storage).&lt;/li&gt;
&lt;li&gt;🔍 &lt;strong&gt;Post-game debrief&lt;/strong&gt; — evaluation chart, best/worst move detection, and a "My Mistakes" trainer that resurfaces your blunders to fix later.&lt;/li&gt;
&lt;li&gt;📚 &lt;strong&gt;Learn &amp;amp; Quizzes&lt;/strong&gt; — interactive lessons with arrows/highlights and tactical puzzles, with a streak + daily-challenge loop.&lt;/li&gt;
&lt;li&gt;🏆 &lt;strong&gt;Gamification&lt;/strong&gt; — XP and 7 progression tiers (Pawn → Grandmaster), daily streaks, and unlockables.&lt;/li&gt;
&lt;li&gt;🎨 &lt;strong&gt;Make it yours&lt;/strong&gt; — 16 piece styles, 4 board themes, sound effects, and smart local notifications.&lt;/li&gt;
&lt;li&gt;📦 &lt;strong&gt;Offline-first &amp;amp; private&lt;/strong&gt; — the engine runs fully offline and all progress is stored locally (Hive); there is no cloud account and no tracking.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tech:&lt;/strong&gt; Flutter · Riverpod · go_router · Stockfish (FFI) · bishop · squares · Hive · dio · flutter_secure_storage.&lt;/p&gt;

&lt;p&gt;🔗 &lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/Mothilal-M/Chaturanga" rel="noopener noreferrer"&gt;https://github.com/Mothilal-M/Chaturanga&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://drive.google.com/file/d/1MgDVRSBaVmaM3h-yf7VK43ELnAL8dAGw/view?usp=drive_link" rel="noopener noreferrer"&gt;https://drive.google.com/file/d/1MgDVRSBaVmaM3h-yf7VK43ELnAL8dAGw/view?usp=drive_link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxm6n1fsw0l3yayzbdqhr.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxm6n1fsw0l3yayzbdqhr.jpeg" alt=" " width="575" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fil87vh7m8rbalzpzz07i.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fil87vh7m8rbalzpzz07i.jpeg" alt=" " width="575" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphx4gtk4i4gnz0ra9dq7.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphx4gtk4i4gnz0ra9dq7.jpeg" alt=" " width="575" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpetn2os6lazg080aqs28.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpetn2os6lazg080aqs28.jpeg" alt=" " width="575" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Comeback Story
&lt;/h2&gt;

&lt;p&gt;This is exactly the kind of project this challenge was made for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt; Chaturanga was an ambitious side project I got &lt;em&gt;most&lt;/em&gt; of the way through and then stalled. The hard parts were actually done — the Stockfish integration, the AI coaching loop, the board UI, the lessons/quizzes engine — but it was stuck in "almost":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It still had &lt;strong&gt;half-removed backend plumbing&lt;/strong&gt; (leftover Firebase/Remote-Config references) from an architecture I'd abandoned.&lt;/li&gt;
&lt;li&gt;The Learn/Quizzes engines worked, but the &lt;strong&gt;content was third-party data I couldn't legally publish&lt;/strong&gt;, so the app couldn't go public.&lt;/li&gt;
&lt;li&gt;There was &lt;strong&gt;no real README, no license, no public release&lt;/strong&gt; — it lived only on my machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I stopped because the app was maybe 90% done and everything left was the un-fun 10% — licensing the lesson content, ripping out a backend I'd outgrown, and writing docs — so it quietly sat on my drive for months. This challenge was the deadline-shaped nudge I needed to finally clear those blockers and put it in front of people.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;After (what I finished for this challenge):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧹 &lt;strong&gt;Cleared the dead backend&lt;/strong&gt; — removed the Firebase/Remote-Config remnants so the codebase honestly reflects what it is: a private, local-first app.&lt;/li&gt;
&lt;li&gt;✍️ &lt;strong&gt;Replaced the unpublishable content with my own&lt;/strong&gt; — wrote an original starter set of lessons and tactical puzzles in the app's JSON format, so Learn &amp;amp; Quizzes work out of the box and the whole thing is safe to open-source.&lt;/li&gt;
&lt;li&gt;📖 &lt;strong&gt;Made it a real project&lt;/strong&gt; — proper README, MIT license, repo description/topics, and a clean public history.&lt;/li&gt;
&lt;li&gt;🚀 &lt;strong&gt;Shipped it&lt;/strong&gt; — Chaturanga is now public on GitHub for the first time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The "almost-done project gathering dust" finally became a finished, shippable app. That's the arc.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Experience with GitHub Copilot
&lt;/h2&gt;

&lt;p&gt;Copilot was the thing that got me over the "last 10%" hump — the unglamorous finishing work that's easy to procrastinate on.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo-wide context that paid off.&lt;/strong&gt; I added a &lt;a href="https://github.com/Mothilal-M/Chaturanga/blob/main/.github/copilot-instructions.md" rel="noopener noreferrer"&gt;&lt;code&gt;.github/copilot-instructions.md&lt;/code&gt;&lt;/a&gt; describing the architecture (Riverpod, go_router, Hive, local-only, the AI-coach flow). After that, Copilot's suggestions actually fit &lt;em&gt;my&lt;/em&gt; conventions instead of generic Flutter boilerplate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regenerating the content.&lt;/strong&gt; The biggest blocker was replacing the third-party lesson/quiz data. I used Copilot to scaffold new JSON in the exact schema the app expects — lesson trees (&lt;code&gt;root&lt;/code&gt; + &lt;code&gt;variations&lt;/code&gt;) and puzzles (&lt;code&gt;fen&lt;/code&gt; + &lt;code&gt;solutionUcis&lt;/code&gt;) — and to sanity-check positions. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The biggest blocker was replacing the third-party lesson/quiz data. I used Copilot Chat to scaffold new JSON in the exact schema the app expects — for example: "Using the lesson/quiz schema in copilot-instructions.md, generate a beginner back-rank-mate puzzle with a fen and solutionUcis." It got the structure right immediately, so I could focus on verifying the chess instead of the JSON.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cleanup &amp;amp; consistency.&lt;/strong&gt; Copilot helped me hunt down and remove the stale Firebase references and keep doc comments consistent across files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs at the finish line.&lt;/strong&gt; It sped up writing the README and this very post structure.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once Copilot had that instructions file, it stopped guessing and started writing code that fit the project — five minutes of setup paid for itself within the hour. The flip side: it happily produced chess puzzles with illegal or non-mating positions, so I still verified every fen by hand. A good reminder that it accelerates the work but doesn't replace your judgment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Team&lt;/strong&gt;
&lt;a class="mentioned-user" href="https://dev.to/shudiptotrafder"&gt;@shudiptotrafder&lt;/a&gt; and &lt;a class="mentioned-user" href="https://dev.to/mothilal"&gt;@mothilal&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Thanks for reading! If you want to try it or read the code, it's all here: &lt;strong&gt;&lt;a href="https://github.com/Mothilal-M/Chaturanga" rel="noopener noreferrer"&gt;https://github.com/Mothilal-M/Chaturanga&lt;/a&gt;&lt;/strong&gt; ⭐&lt;/p&gt;

&lt;p&gt;A note on the repo: this is a brand-new public repository. The original lessons and puzzles, I don't want to distribute the data, so I stripped that out and shipped a small original starter set in its place — Learn and Quizzes now run entirely on my own content, which is what made it safe to open-source.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
    </item>
    <item>
      <title>We Built a Typesense Dashboard with an AI Brain and It's Lowkey Insane 🧠⚡</title>
      <dc:creator>Mothilal M</dc:creator>
      <pubDate>Sat, 28 Feb 2026 16:56:37 +0000</pubDate>
      <link>https://dev.to/10xscale/we-built-a-typesense-dashboard-with-an-ai-brain-and-its-lowkey-insane-4m8j</link>
      <guid>https://dev.to/10xscale/we-built-a-typesense-dashboard-with-an-ai-brain-and-its-lowkey-insane-4m8j</guid>
      <description>&lt;h2&gt;
  
  
  TLDR for the scroll-addicted 📱
&lt;/h2&gt;

&lt;p&gt;We built &lt;strong&gt;Typesense UI&lt;/strong&gt; — an open-source, fully interactive dashboard for &lt;a href="https://typesense.org" rel="noopener noreferrer"&gt;Typesense&lt;/a&gt; with a built-in &lt;strong&gt;AI assistant powered by Gemini 2.0 Flash&lt;/strong&gt;. You can literally &lt;em&gt;talk&lt;/em&gt; to your search data in plain English.&lt;/p&gt;

&lt;p&gt;It's free. It's open source. It slaps.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/Mothilal-hire10x/typesense-ui" rel="noopener noreferrer"&gt;github.com/Mothilal-hire10x/typesense-ui&lt;/a&gt;&lt;br&gt;
👉 &lt;strong&gt;Run it instantly&lt;/strong&gt;: &lt;code&gt;npx typesense-ui&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Origin Story (a.k.a. why we rage-built this) 😤
&lt;/h2&gt;

&lt;p&gt;If you've ever used Typesense, you know it's an &lt;em&gt;absolute beast&lt;/em&gt; of a search engine — blazingly fast, typo-tolerant, and open source. But let's be real... managing collections and documents through cURL commands and JSON payloads? &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's giving 2015 energy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We wanted something that &lt;em&gt;looks&lt;/em&gt; like it belongs in 2026. Something with dark mode (obviously), smooth animations, and — here's the plot twist — an AI that actually understands our data and can query it for us.&lt;/p&gt;

&lt;p&gt;So we built it. And honestly? It ate and left no crumbs. 🍽️&lt;/p&gt;




&lt;h2&gt;
  
  
  What Makes This Different? (It's Not Just Another CRUD App)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🤖 AI Assistant That Actually Understands Your Data
&lt;/h3&gt;

&lt;p&gt;This is the main character energy of the whole project.&lt;/p&gt;

&lt;p&gt;The built-in AI chat (powered by &lt;strong&gt;Gemini 2.0 Flash&lt;/strong&gt;) doesn't just answer generic questions — it has &lt;strong&gt;full Typesense tool-calling capabilities&lt;/strong&gt;. It can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List and inspect your collections&lt;/li&gt;
&lt;li&gt;Search documents with complex filters&lt;/li&gt;
&lt;li&gt;Create, update, and delete documents&lt;/li&gt;
&lt;li&gt;Show results in beautifully formatted tables&lt;/li&gt;
&lt;li&gt;Ask for confirmation before destructive operations (it's responsible bestie fr fr)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just type something like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Show me all users who signed up last month sorted by name"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And it literally queries your Typesense instance and returns the results. In a table. With pagination. &lt;strong&gt;WHAT.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ⚡ The Landing Page Goes STUPID Hard
&lt;/h3&gt;

&lt;p&gt;Not gonna lie, we may have gone a little overboard with the landing page. It features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;3D scene&lt;/strong&gt; built with React Three Fiber &amp;amp; Three.js&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GSAP animations&lt;/strong&gt; with scroll-triggered reveals&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lenis smooth scrolling&lt;/strong&gt; (that butter-smooth feel)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Magnetic hover effects&lt;/strong&gt; on CTAs&lt;/li&gt;
&lt;li&gt;Fully responsive, dark-themed, cinematic vibes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's giving Apple product page energy and we're not apologizing for it.&lt;/p&gt;

&lt;h3&gt;
  
  
  🎨 The Dashboard Itself (Where the Real Work Happens)
&lt;/h3&gt;

&lt;p&gt;Once you connect to your Typesense server, you get:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&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;strong&gt;Collections Sidebar&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Browse all collections with doc counts, search/filter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Advanced Table View&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Paginated, sortable, with column picker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Live Search&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Instant full-text search across all fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dynamic Filters&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Auto-generated from schema — text, range, boolean&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Document Editor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Create, edit, delete docs with JSON editor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dark/Light Mode&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Because we respect the vibe check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Schema Inspector&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;View full collection schemas at a glance&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Everything is &lt;strong&gt;real-time&lt;/strong&gt;. Type a search query, toggle a filter, click a column header — instant response. No loading spinners holding your life hostage.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Get Started (It's Embarrassingly Easy)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1: The One-Liner 🏎️
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx typesense-ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. That's the tweet. It spins up a local server and opens the dashboard. Done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Clone &amp;amp; Vibe
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Mothilal-hire10x/typesense-ui.git
&lt;span class="nb"&gt;cd &lt;/span&gt;typesense-ui
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;http://localhost:3000&lt;/code&gt;, enter your Typesense server details, and you're locked in. 🔐&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 3: Install Globally
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; typesense-ui
typesense-ui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting Up the AI Assistant
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Click the ✨ sparkle button in the bottom-right&lt;/li&gt;
&lt;li&gt;Enter your Gemini API key (&lt;a href="https://aistudio.google.com/app/apikey" rel="noopener noreferrer"&gt;get one free here&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Start chatting with your data like it owes you money&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The AI in Action — Some Wild Prompts That Just Work
&lt;/h2&gt;

&lt;p&gt;Here are some things you can literally just &lt;em&gt;say&lt;/em&gt; to the AI:&lt;/p&gt;

&lt;p&gt;🗣️ &lt;em&gt;"What collections do I have?"&lt;/em&gt;&lt;br&gt;
→ Lists all collections with doc counts and schemas&lt;/p&gt;

&lt;p&gt;🗣️ &lt;em&gt;"Find products cheaper than $50 sorted by price"&lt;/em&gt;&lt;br&gt;
→ Searches with &lt;code&gt;filter_by&lt;/code&gt; and &lt;code&gt;sort_by&lt;/code&gt;, returns a formatted table&lt;/p&gt;

&lt;p&gt;🗣️ &lt;em&gt;"Delete the document with ID abc123 from the users collection"&lt;/em&gt;&lt;br&gt;
→ Shows a confirmation dialog first (safety first bestie)&lt;/p&gt;

&lt;p&gt;🗣️ &lt;em&gt;"What fields does the orders collection have?"&lt;/em&gt;&lt;br&gt;
→ Returns the full schema breakdown&lt;/p&gt;

&lt;p&gt;🗣️ &lt;em&gt;"Create a new document in products with name 'Widget' and price 29.99"&lt;/em&gt;&lt;br&gt;
→ Creates the doc after your confirmation&lt;/p&gt;

&lt;p&gt;The AI has &lt;strong&gt;function calling&lt;/strong&gt; — it doesn't just hallucinate answers, it actually executes real Typesense operations. It's like having a senior dev who never sleeps and never complains. We love to see it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Typesense Deserves Better Tooling
&lt;/h2&gt;

&lt;p&gt;Typesense is genuinely one of the best open-source search engines out there. It's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;Blazingly fast&lt;/strong&gt; (sub-millisecond searches)&lt;/li&gt;
&lt;li&gt;🔤 &lt;strong&gt;Typo-tolerant&lt;/strong&gt; out of the box&lt;/li&gt;
&lt;li&gt;🧠 &lt;strong&gt;Easy to setup&lt;/strong&gt; compared to Elasticsearch&lt;/li&gt;
&lt;li&gt;💰 &lt;strong&gt;Free &amp;amp; open source&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the ecosystem around it — especially for visual management — has been lacking. The official dashboard is decent, but we wanted something that feels &lt;em&gt;modern&lt;/em&gt;, &lt;em&gt;powerful&lt;/em&gt;, and has AI baked in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Typesense UI&lt;/strong&gt; is our attempt to give Typesense the visual experience it deserves.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next? 🔮
&lt;/h2&gt;

&lt;p&gt;Some things on the roadmap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;strong&gt;Multi-server support&lt;/strong&gt; — switch between Typesense instances&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Collection creation wizard&lt;/strong&gt; — visual schema builder&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Export/Import&lt;/strong&gt; — bulk operations with CSV/JSON&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Query playground&lt;/strong&gt; — test search queries with syntax highlighting&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;Analytics dashboard&lt;/strong&gt; — search metrics and insights&lt;/li&gt;
&lt;li&gt;[ ] &lt;strong&gt;More AI models&lt;/strong&gt; — OpenAI, Claude, local LLMs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If any of these get you hyped, drop a ⭐ on the repo and open an issue with your ideas!&lt;/p&gt;




&lt;h2&gt;
  
  
  Show Some Love ❤️
&lt;/h2&gt;

&lt;p&gt;If this project saved you time, made you smile, or just impressed you a little bit:&lt;/p&gt;

&lt;p&gt;⭐ &lt;strong&gt;Star the repo&lt;/strong&gt;: &lt;a href="https://github.com/Mothilal-hire10x/typesense-ui" rel="noopener noreferrer"&gt;github.com/Mothilal-hire10x/typesense-ui&lt;/a&gt;&lt;br&gt;
🐛 &lt;strong&gt;Report bugs&lt;/strong&gt;: &lt;a href="https://github.com/Mothilal-hire10x/typesense-ui/issues" rel="noopener noreferrer"&gt;Issues&lt;/a&gt;&lt;br&gt;
🤝 &lt;strong&gt;Contribute&lt;/strong&gt;: PRs are always welcome!&lt;br&gt;
📢 &lt;strong&gt;Share this post&lt;/strong&gt;: Help other devs discover it&lt;/p&gt;




&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Building this was genuinely one of the most fun projects we've worked on. The intersection of &lt;strong&gt;search technology&lt;/strong&gt;, &lt;strong&gt;modern frontend&lt;/strong&gt;, and &lt;strong&gt;AI&lt;/strong&gt; is such a vibe right now, and we think we're only scratching the surface.&lt;/p&gt;

&lt;p&gt;If you're using Typesense (or thinking about it), give &lt;strong&gt;Typesense UI&lt;/strong&gt; a spin. It's free, it's open source, and it might just change how you interact with your search data.&lt;/p&gt;

&lt;p&gt;Now go touch some grass. Or don't. We're not your mom. 🌱&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built with React, TypeScript, and an unhealthy amount of caffeine ☕&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow us for more open-source drops and dev content that doesn't put you to sleep.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>typesense</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stop Wiring Dependencies by Hand - Meet InjectQ, Python DI Done Right</title>
      <dc:creator>Mothilal M</dc:creator>
      <pubDate>Sun, 22 Feb 2026 12:36:40 +0000</pubDate>
      <link>https://dev.to/10xscale/stop-wiring-dependencies-by-hand-meet-injectq-python-di-done-right-50d3</link>
      <guid>https://dev.to/10xscale/stop-wiring-dependencies-by-hand-meet-injectq-python-di-done-right-50d3</guid>
      <description>&lt;p&gt;You've been there.&lt;/p&gt;

&lt;p&gt;A service that needs a database, which needs a config, which needs an env variable that someone hardcoded three months ago and nobody remembers where. You're passing objects down ten layers of constructors. Testing means faking half your app.&lt;/p&gt;

&lt;p&gt;It's messy. And it doesn't have to be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dependency Injection&lt;/strong&gt; is the fix - but most Python DI libraries feel like they were designed for a different language. Overly complex, decorator-heavy, or magical in ways that make debugging a nightmare.&lt;/p&gt;

&lt;p&gt;So we built &lt;strong&gt;InjectQ&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We wanted DI that feels like Python - not like a Java framework that got lost on its way to PyPI."&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What is InjectQ?
&lt;/h2&gt;

&lt;p&gt;InjectQ is a &lt;strong&gt;modern, lightweight Python dependency injection library&lt;/strong&gt; focused on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Clarity and simplicity&lt;/li&gt;
&lt;li&gt;✅ Type safety (works with mypy, pyright)&lt;/li&gt;
&lt;li&gt;✅ Async-first APIs&lt;/li&gt;
&lt;li&gt;✅ Seamless FastAPI &amp;amp; Taskiq integration&lt;/li&gt;
&lt;li&gt;✅ Production-grade performance (270ns per bind)&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;📦 &lt;a href="https://pypi.org/project/injectq/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐙 &lt;a href="https://github.com/10xHub/injectq" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📖 &lt;a href="https://10xhub.github.io/injectq/" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Install in one line
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;injectq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For framework integrations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;injectq[fastapi]   &lt;span class="c"&gt;# FastAPI support&lt;/span&gt;
pip &lt;span class="nb"&gt;install &lt;/span&gt;injectq[taskiq]    &lt;span class="c"&gt;# Taskiq support&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Quick Start - Zero config, maximum clarity
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;injectq&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InjectQ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;singleton&lt;/span&gt;

&lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;InjectQ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_instance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Bind a value — dict-style, no ceremony
&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello, World!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@singleton&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Service says: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@inject&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# → Service says: Hello, World!
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. &lt;code&gt;@singleton&lt;/code&gt; = one instance app-wide. &lt;code&gt;@inject&lt;/code&gt; = auto-resolve from type hints. No XML, no 500-line config, no magic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🔧 Dict-like API
&lt;/h3&gt;

&lt;p&gt;The simplest mental model possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;injectq&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InjectQ&lt;/span&gt;

&lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;InjectQ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_instance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Bind anything
&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config_value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Retrieve
&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🎯 Decorator + Type-based Injection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@inject&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Both auto-resolved from the container
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also supports &lt;code&gt;Inject[T]&lt;/code&gt; for inline type annotations that work with static type checkers.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔄 Scopes and Lifetimes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;injectq&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;scoped&lt;/span&gt;

&lt;span class="nd"&gt;@singleton&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;       &lt;span class="c1"&gt;# One instance, lives forever
&lt;/span&gt;
&lt;span class="nd"&gt;@transient&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Validator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;      &lt;span class="c1"&gt;# New instance every resolution
&lt;/span&gt;
&lt;span class="nd"&gt;@scoped&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;# One per request scope
&lt;/span&gt;
&lt;span class="c1"&gt;# Async scopes work too
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;ctx1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;ctx2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;ctx1&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;ctx2&lt;/span&gt;  &lt;span class="c1"&gt;# Same instance ✓
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🆕 Hybrid Factories - The Feature That Changes Everything
&lt;/h3&gt;

&lt;p&gt;This is new in &lt;strong&gt;v0.4&lt;/strong&gt; and it's genuinely great.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem:&lt;/strong&gt; You have a factory that needs a database (DI-managed) &lt;em&gt;and&lt;/em&gt; a user ID (runtime value). Old way is verbose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# ❌ Old way — manually resolve everything
&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;svc&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call_factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user123&lt;/span&gt;&lt;span class="sh"&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;New way with &lt;code&gt;invoke()&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_user_service&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind_factory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;create_user_service&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ✅ Auto-inject db and cache, you only pass what you know
&lt;/span&gt;&lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Async version
&lt;/span&gt;&lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ainvoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;async_service&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;InjectQ resolves what it knows from the container. You provide only the runtime-specific values. Clean.&lt;/p&gt;




&lt;h3&gt;
  
  
  🚀 FastAPI Integration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;injectq&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;InjectQ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;singleton&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;injectq.integrations.fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;setup_fastapi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;InjectFastAPI&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;       &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;InjectQ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_instance&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nf"&gt;setup_fastapi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@singleton&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/users/{user_id}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_service&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Annotated&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;InjectFastAPI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;)],&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;user_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🧪 Testing — Mocking Without the Pain
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;injectq.testing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;override_dependency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;test_container&lt;/span&gt;

&lt;span class="c1"&gt;# Override a specific dep for the duration of a block
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;override_dependency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;MockDatabase&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
    &lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# UserService gets MockDatabase here ✓
&lt;/span&gt;
&lt;span class="c1"&gt;# Fully isolated test container — no global state bleed
&lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;test_container&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MockDatabase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Clean slate for each test
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro tip:&lt;/strong&gt; Use &lt;code&gt;InjectQ.test_mode()&lt;/code&gt; with pytest fixtures to auto-reset your container between tests.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  🏗️ Modules and Providers
&lt;/h3&gt;

&lt;p&gt;For larger apps, organize your bindings into modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;injectq.modules&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SimpleModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ProviderModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;provider&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;binder&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;binder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="n"&gt;binder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Providers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ProviderModule&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@provider&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_notifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Notifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Notifier&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InjectQ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modules&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;AppModule&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;Providers&lt;/span&gt;&lt;span class="p"&gt;()])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  🛡️ Abstract Class Validation
&lt;/h3&gt;

&lt;p&gt;InjectQ validates at bind time, not at resolution time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;abc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abstractmethod&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;injectq.utils.exceptions&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BindingError&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_payment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# ❌ Raises BindingError immediately — no surprises at runtime
&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# ✅ Correct — bind the concrete implementation
&lt;/span&gt;&lt;span class="n"&gt;container&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PaymentProcessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CreditCardProcessor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fail fast. Debug less.&lt;/p&gt;




&lt;h2&gt;
  
  
  Performance Benchmarks
&lt;/h2&gt;

&lt;p&gt;InjectQ isn't just clean — it's &lt;strong&gt;fast&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Speed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Basic bind / get&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;270–780 nanoseconds&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dependency resolution&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~1 microsecond&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10-service web request simulation&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;142 microseconds&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1,000+ concurrent operations&lt;/td&gt;
&lt;td&gt;Sub-millisecond&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Thread-safe by default. Production-ready from day one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why InjectQ Over Alternatives?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;InjectQ&lt;/th&gt;
&lt;th&gt;dependency-injector&lt;/th&gt;
&lt;th&gt;injector&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Dict-like API&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FastAPI integration&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌ Manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid factories (invoke)&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async scope contexts&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;⚠️ Limited&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing utilities&lt;/td&gt;
&lt;td&gt;✅ Built-in&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌ Manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Taskiq integration&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Abstract class guard&lt;/td&gt;
&lt;td&gt;✅ Bind-time&lt;/td&gt;
&lt;td&gt;❌ Runtime&lt;/td&gt;
&lt;td&gt;❌ Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;If you're tired of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manually wiring dependencies&lt;/li&gt;
&lt;li&gt;Global state leaking into tests&lt;/li&gt;
&lt;li&gt;Framework integrations that require 200 lines of glue code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;InjectQ is for you.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;injectq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;📦 &lt;a href="https://pypi.org/project/injectq/" rel="noopener noreferrer"&gt;PyPI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐙 &lt;a href="https://github.com/10xHub/injectq" rel="noopener noreferrer"&gt;GitHub — star us if this helps!&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📖 &lt;a href="https://10xhub.github.io/injectq/" rel="noopener noreferrer"&gt;Full Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Built with ♥ by the 10xHub team. MIT Licensed. Contributions welcome.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have questions or feature requests? Drop them in the comments or open an issue on GitHub. We read everything.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>dependencyinjection</category>
      <category>opensource</category>
      <category>fastapi</category>
    </item>
  </channel>
</rss>
