<?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: Christian Findlay</title>
    <description>The latest articles on DEV Community by Christian Findlay (@cfdevelop).</description>
    <link>https://dev.to/cfdevelop</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F935428%2F765aa836-e6aa-46b4-8ca7-c2c17e1fc974.jpeg</url>
      <title>DEV Community: Christian Findlay</title>
      <link>https://dev.to/cfdevelop</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/cfdevelop"/>
    <language>en</language>
    <item>
      <title>I built a Python AI agent and Pylance drove me to build a type checker and LSP</title>
      <dc:creator>Christian Findlay</dc:creator>
      <pubDate>Sat, 06 Jun 2026 07:47:31 +0000</pubDate>
      <link>https://dev.to/cfdevelop/i-built-a-python-ai-agent-and-pylance-drove-me-to-build-a-type-checker-and-lsp-41fb</link>
      <guid>https://dev.to/cfdevelop/i-built-a-python-ai-agent-and-pylance-drove-me-to-build-a-type-checker-and-lsp-41fb</guid>
      <description>&lt;p&gt;A while back I started building &lt;a href="https://github.com/Nimblesite/nimble_agent" rel="noopener noreferrer"&gt;Nimble Agent&lt;/a&gt; — a LangChain-based coding agent in Python. The idea was to fix the things that annoy me about other AI agents: smarter prompting, cheaper models, and &lt;em&gt;acceptance criteria&lt;/em&gt; so the agent can't just hand-wave "done" and stop.&lt;/p&gt;

&lt;p&gt;It's an AI agent that writes code. Which means the one thing I could not afford was my &lt;em&gt;own&lt;/em&gt; code lying to me about its types.&lt;/p&gt;

&lt;p&gt;So I tried the standard Pylance extension and Pyright as a type checker. I was seriously underwhelmed.&lt;/p&gt;

&lt;p&gt;Skip the hype. Install the extensions here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=Nimblesite.basilisk" rel="noopener noreferrer"&gt;VSCode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://open-vsx.org/extension/Nimblesite/basilisk" rel="noopener noreferrer"&gt;Open VSX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Nimblesite/basilisk.nvim" rel="noopener noreferrer"&gt;Neovim&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;More coming...&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pylance wall
&lt;/h2&gt;

&lt;p&gt;Python has a perfectly good type system, and almost nothing enforces it.&lt;/p&gt;

&lt;p&gt;Pylance defaults to gradual typing. Untyped code sails straight through. A function with no annotations? Fine. An implicit &lt;code&gt;Any&lt;/code&gt; swallowing an entire call chain? Fine. You opt &lt;em&gt;into&lt;/em&gt; strictness, rule by rule, setting by setting — and even then half of it slides. My annotations weren't contracts. They were comments that happened to compile. For a tool whose whole job is generating Python, "your types are vibes" is a non-starter.&lt;/p&gt;

&lt;p&gt;Then there's the part that actually made me angry: &lt;strong&gt;Pylance is proprietary and welded to Microsoft's official VS Code build.&lt;/strong&gt; I don't live in vanilla VS Code. I might bounce between Cursor, Windsurf, sometimes Zed. The moment you step outside the Microsoft walled garden, the good experience evaporates — different engine, missing features, or a license that says you're not allowed to be there at all.&lt;/p&gt;

&lt;p&gt;Moreover, I just couldn't figure out how to configure the type checker in vscode to analyze for all files. It would only show errors and warnings for the file I was editing even though I tried everything to configure it.&lt;/p&gt;

&lt;h2&gt;
  
  
  So I built Basilisk
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.basilisk-python.dev" rel="noopener noreferrer"&gt;Basilisk&lt;/a&gt; is an open-source Python language server, type checker, debugger, and profiler — written in Rust, shipped as a single binary, &lt;strong&gt;no Node runtime, no Python runtime, no Microsoft dependency.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The core stance is the whole pitch: &lt;strong&gt;strict by default, no permissive mode.&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;greet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error[BSK-E0001]: Missing parameter type annotation for `name`
error[BSK-E0002]: Missing return type annotation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's no &lt;code&gt;--basic&lt;/code&gt;, no &lt;code&gt;--permissive&lt;/code&gt;, no global "relax everything" knob. Rust doesn't ship a flag to disable the borrow checker; TypeScript's &lt;code&gt;strict: true&lt;/code&gt; is just &lt;em&gt;expected&lt;/em&gt;. Basilisk takes that stance for Python. Escape hatches exist — but the burden is on you to justify the exception, not to remember to turn the rule on. 151 diagnostics, all on, all the time.&lt;/p&gt;

&lt;p&gt;The IDE extension gives you all the tools to ignore what you need to to ignore to start using Basilisk with your untyped Python, but it also gives you lots of tools to add types gradually.&lt;/p&gt;

&lt;h2&gt;
  
  
  But "strict checker" isn't the actual goal
&lt;/h2&gt;

&lt;p&gt;The checker is the wedge. The real aim is the thing Pylance &lt;em&gt;almost&lt;/em&gt; is and won't let you have everywhere: &lt;strong&gt;one extension that's your complete Python dev experience.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Out of one Rust binary, Basilisk gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧠 Type checking + inference (strict by default, targeting 100% PEP conformance)&lt;/li&gt;
&lt;li&gt;🐛 Debugging (embedded debugpy, zero-config)&lt;/li&gt;
&lt;li&gt;🔥 Profiling (py-spy, inline in the editor)&lt;/li&gt;
&lt;li&gt;🧪 A test explorer (pytest/unittest with coverage overlay)&lt;/li&gt;
&lt;li&gt;🔧 Real refactors — extract, inline, move-to-file, scope-aware rename, change signature&lt;/li&gt;
&lt;li&gt;💡 Autofixes and formatting (delegated to Ruff — we don't reinvent the wheel)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And — the part that started this whole thing — &lt;strong&gt;the same experience everywhere:&lt;/strong&gt; VS Code, Cursor, and Windsurf (via Open VSX), plus Zed and Neovim. Because the &lt;em&gt;LSP&lt;/em&gt; drives the functionality, not the IDE. The editor just reacts to it. Install once, get the full thing, wherever you write Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it's at
&lt;/h2&gt;

&lt;p&gt;Basilisk is open source (MIT), built on the same parser that powers Ruff, with Salsa for sub-10ms incremental checks. I started it because I wanted my AI agent's Python to be as honest as TypeScript, in whatever editor I happened to have open. It turned into something a lot bigger.&lt;/p&gt;

&lt;p&gt;If you've ever wanted Python to just &lt;em&gt;say it out loud&lt;/em&gt; when your code isn't typed — &lt;a href="https://www.basilisk-python.dev/docs/quick-start/" rel="noopener noreferrer"&gt;give it a try&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Check out &lt;a href="https://www.nimblesite.co/tools/" rel="noopener noreferrer"&gt;Nimblesite's other tools&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>rust</category>
      <category>devtools</category>
    </item>
    <item>
      <title>Flutter: How To Mock Functions For Testing</title>
      <dc:creator>Christian Findlay</dc:creator>
      <pubDate>Sat, 19 Nov 2022 07:37:57 +0000</pubDate>
      <link>https://dev.to/cfdevelop/flutter-how-to-mock-functions-for-testing-2fpj</link>
      <guid>https://dev.to/cfdevelop/flutter-how-to-mock-functions-for-testing-2fpj</guid>
      <description>&lt;p&gt;This article explains how to mock top-level functions in Dart for Flutter widget tests. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.christianfindlay.com/blog/flutter-mock-functions-for-testing"&gt;https://www.christianfindlay.com/blog/flutter-mock-functions-for-testing&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Functions don't have to be second-class citizens in Dart, and you don't have to make a new class every time you want to mock a dependency. Pass your functions into your widgets with dependency injection and replace them with substitutes for testing. &lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>mocking</category>
      <category>functions</category>
    </item>
    <item>
      <title>Flutter Dependency Injection: ioc_container V1</title>
      <dc:creator>Christian Findlay</dc:creator>
      <pubDate>Wed, 26 Oct 2022 07:54:57 +0000</pubDate>
      <link>https://dev.to/cfdevelop/flutter-dependency-injection-ioccontainer-v1-4j6p</link>
      <guid>https://dev.to/cfdevelop/flutter-dependency-injection-ioccontainer-v1-4j6p</guid>
      <description>&lt;p&gt;Dart doesn't really have library specifically for dependency injection. There are libraries that you can use as a service locator but they have global declarations&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pub.dev/packages/ioc_container"&gt;ioc_container&lt;/a&gt; is full fledged, fast and simple library for dependency injection. Check out the benchmarks to compare the performance with other libraries. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://link.medium.com/EHKzqaV1qub"&gt;https://link.medium.com/EHKzqaV1qub&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Dart Immutable Collections</title>
      <dc:creator>Christian Findlay</dc:creator>
      <pubDate>Sun, 02 Oct 2022 06:58:22 +0000</pubDate>
      <link>https://dev.to/cfdevelop/dart-immutable-collections-4k2h</link>
      <guid>https://dev.to/cfdevelop/dart-immutable-collections-4k2h</guid>
      <description>&lt;p&gt;&lt;a href="https://link.medium.com/dhazVqi8Mtb"&gt;Link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Immutability is an important Dart concept. The immutable annotation marks a class as being immutable, but if the class has mutable properties, it is not really immutable. This is true in the case of collections. This article discusses why and how to use immutable collections on your Dart classes. &lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>immutability</category>
    </item>
  </channel>
</rss>
