<?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: Mat Weiss</title>
    <description>The latest articles on DEV Community by Mat Weiss (@matweiss).</description>
    <link>https://dev.to/matweiss</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%2F994946%2F2107b19e-bba0-4076-94bd-dbd1e6e7f2a9.png</url>
      <title>DEV Community: Mat Weiss</title>
      <link>https://dev.to/matweiss</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/matweiss"/>
    <language>en</language>
    <item>
      <title>Code Intelligence Is Being Retrofitted. Ruuk Builds It In.</title>
      <dc:creator>Mat Weiss</dc:creator>
      <pubDate>Fri, 17 Apr 2026 12:01:48 +0000</pubDate>
      <link>https://dev.to/matweiss/code-intelligence-is-being-retrofitted-ruuk-builds-it-in-1nb</link>
      <guid>https://dev.to/matweiss/code-intelligence-is-being-retrofitted-ruuk-builds-it-in-1nb</guid>
      <description>&lt;p&gt;&lt;em&gt;A response to Thoughtworks Technology Radar Vol. 34, Blip 18: Code Intelligence as Agentic Tooling&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://www.thoughtworks.com/radar/techniques/summary/code-intelligence-as-agentic-tooling" rel="noopener noreferrer"&gt;Blip 18&lt;/a&gt; of the April 2026 Thoughtworks Radar names a real problem: AI coding agents are effectively blind to the meaning of the code they operate on. The Radar's answer is richer tooling — LSP integrations, OpenRewrite's Lossless Semantic Tree, JetBrains MCP servers.&lt;/p&gt;

&lt;p&gt;That's the practical path for mainstream languages. But it's still using the AST to &lt;em&gt;infer&lt;/em&gt; intent. What would it look like to use the AST to &lt;em&gt;see&lt;/em&gt; it?&lt;/p&gt;

&lt;p&gt;Ruuk — a language I'm designing — takes a different position: the constraints worth enforcing should be in the language itself.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the AST Cannot Tell You
&lt;/h2&gt;

&lt;p&gt;Take a typical enterprise operation: approving an order. In Java, an agent with full LSP access sees something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ApprovalResult&lt;/span&gt; &lt;span class="nf"&gt;approveOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderId&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;CSR&lt;/span&gt; &lt;span class="n"&gt;csr&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;orderStore&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// ... validation logic&lt;/span&gt;
    &lt;span class="c1"&gt;// ... state transition&lt;/span&gt;
    &lt;span class="c1"&gt;// ... outcome handling&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The agent knows the function name, its signature, its call sites, and the types it touches. What it doesn't know is more consequential:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;order&lt;/code&gt; must be in &lt;code&gt;Created&lt;/code&gt; state for this call to be valid. Calling it on a &lt;code&gt;Cancelled&lt;/code&gt; order is a logic error, not a type error — the compiler won't catch it, and neither will the agent.&lt;/li&gt;
&lt;li&gt;There are exactly two outcomes — &lt;code&gt;Approved&lt;/code&gt; and &lt;code&gt;Rejected&lt;/code&gt; — and callers must handle both. The agent has to read the implementation to find out.&lt;/li&gt;
&lt;li&gt;This is an instantaneous state transition, not a long-running process. That distinction matters for error handling and compensation strategy. The function signature doesn't say.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;orderStore&lt;/code&gt; is being mutated — it's an entity undergoing state change, not a read-only data source. The agent can't tell that from the call.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Modern Java closes some of these gaps. Sealed classes and pattern matching (Java 21+) give you exhaustive outcome handling. Some languages go further: TypeScript's discriminated unions and Rust's typestate patterns encode more intent into the type system. But even in those languages, the precondition that the order must be in &lt;code&gt;Created&lt;/code&gt; state remains a convention, not a compiler-checked constraint. The resource mutation role is still invisible. The state machine is still implicit. They moved the needle on &lt;em&gt;what can happen&lt;/em&gt;; they didn't touch &lt;em&gt;what must be true before&lt;/em&gt; or &lt;em&gt;what kind of change is occurring&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;An agent can reconstruct those properties for any well-written function by reading its implementation and tests. The problem is scale. Across a codebase of thousands of operations, inference compounds uncertainty. It fails hardest on the code that needs agents most: legacy systems, inconsistent patterns, missing tests. Structural declarations don't degrade. The thousandth operation is as machine-readable as the first.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Ruuk Exposes at Declaration Time
&lt;/h2&gt;

&lt;p&gt;In Ruuk, the same operation looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight fsharp"&gt;&lt;code&gt;&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Created&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Approved&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Shipped&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Delivered&lt;/span&gt;

&lt;span class="n"&gt;op&lt;/span&gt; &lt;span class="n"&gt;approveOrder&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Created&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;OrderStore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;performs&lt;/span&gt; &lt;span class="nn"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Created&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Approved&lt;/span&gt;
    &lt;span class="n"&gt;outcomes&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Approved&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Approved&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;Rejected&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything the Java version hid is in the declaration. The precondition is in the type: &lt;code&gt;Order&amp;lt;Created&amp;gt;&lt;/code&gt;. Passing an &lt;code&gt;Order&amp;lt;Cancelled&amp;gt;&lt;/code&gt; is a compile error. The state transition is explicit in the &lt;code&gt;performs&lt;/code&gt; clause. The outcomes are enumerated, and the compiler verifies that every call site handles both. The &lt;code&gt;subject&lt;/code&gt; role marks the order as the entity being changed; the &lt;code&gt;goal&lt;/code&gt; role marks the store as the mutation target, distinct from a read-only data source.&lt;/p&gt;

&lt;p&gt;The compiler checks these declarations against the implementation. If the function body transitions to a state that doesn't match the &lt;code&gt;performs&lt;/code&gt; clause, it won't compile. If you add an outcome variant without updating call sites, they won't compile. Declarations can't drift from reality because they're verified at compile time, then erased. Zero runtime cost.&lt;/p&gt;

&lt;p&gt;That's a meaningful difference from design-by-contract predecessors like Eiffel and JML, where contracts were primarily runtime-checked or relied on external verification tools. In Ruuk, the declaration &lt;em&gt;is&lt;/em&gt; the constraint, and the compiler enforces it statically.&lt;/p&gt;

&lt;p&gt;I designed it this way because the information always existed. Every team I've worked with on enterprise applications knew their preconditions, their state machines, their failure modes. They just had no place to put that knowledge where the compiler could use it.&lt;/p&gt;

&lt;p&gt;From this declaration alone, an agent can answer — without reading one line of implementation — every question the Java version left open. The Radar points to the AST and the richer Lossless Semantic Tree as the right representations for agent tooling. Those capture structure: how code is organized, what the syntactic relationships are. Ruuk's declarations go further — they capture &lt;em&gt;meaning&lt;/em&gt;: what must be true, what can happen, and what kind of change is occurring.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Could an Agent Do With This?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Impact analysis.&lt;/strong&gt; When &lt;code&gt;Order&lt;/code&gt; gains a new field, an agent can ask: which projections include this field? A Ruuk projection is a typed, compiler-checked subset of a resource's fields. &lt;code&gt;CustomerOrderView = Order only { id; customer; total }&lt;/code&gt; declares exactly which fields downstream consumers can see. Finding every projection affected by a schema change is a structural query, not a grep through the codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Outcome coverage.&lt;/strong&gt; Which operations have outcomes stubbed with &lt;code&gt;todo&lt;/code&gt;? In Ruuk, &lt;code&gt;todo&lt;/code&gt; is a compiler-tracked placeholder, not a comment. Which call sites are missing a handler for a specific variant? The answers are compiler-verified facts, not heuristic inference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;State machine queries.&lt;/strong&gt; A resource like &lt;code&gt;Order&lt;/code&gt; carries its current state in its type: &lt;code&gt;Order&amp;lt;Approved&amp;gt;&lt;/code&gt;, &lt;code&gt;Order&amp;lt;Shipped&amp;gt;&lt;/code&gt;. The compiler knows which operations are valid for which states. What transitions are reachable from &lt;code&gt;Order&amp;lt;Created&amp;gt;&lt;/code&gt;? What operations apply when the order is in transit? The answers come from the declared lifecycle, queryable and independent of implementation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Role-based refactoring.&lt;/strong&gt; An agent told to "add audit logging to every operation that mutates order state" doesn't need to read method bodies. It queries the declared &lt;code&gt;subject&lt;/code&gt; role across every operation, identifies the affected call sites, and makes the change. The semantic structure tells it exactly where to edit and what contract to preserve.&lt;/p&gt;

&lt;p&gt;In each case, the agent spends fewer tokens reconstructing context and gets closer to a correct edit on the first pass. That's a ceiling that better tooling can't raise if the language never captured the information.&lt;/p&gt;




&lt;p&gt;Blip 18 asks how to make agents smarter about existing code. That's the right question for existing codebases, and the Radar's answers are practical.&lt;/p&gt;

&lt;p&gt;Ruuk is exploring a different one: what happens when the language itself captures enough domain semantics that agents — and humans — can reason from declarations alone?&lt;/p&gt;

&lt;p&gt;That's a bet on language design, not tooling. Ruuk is in early development, and none of this has been tested at scale with real agents. But I believe the overhead pays for itself: what Ruuk asks you to declare is what you already know. The cost isn't in knowing your preconditions and state machines. It's in not having a compiler that checks them.&lt;/p&gt;




&lt;p&gt;The language design is documented on &lt;a href="https://github.com/ruuk-lang/ruuk" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, where I'm using Discussions to think through design decisions in the open. I also write about Ruuk's design rationale here on &lt;a href="https://dev.to/matweiss"&gt;dev.to&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>software</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
