<?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: D Ivanitsky</title>
    <description>The latest articles on DEV Community by D Ivanitsky (@d_ivanitsky_1c82522ba2c52).</description>
    <link>https://dev.to/d_ivanitsky_1c82522ba2c52</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%2F4028603%2Fe3e51e6b-0a00-4868-8229-2206bc95c6e9.png</url>
      <title>DEV Community: D Ivanitsky</title>
      <link>https://dev.to/d_ivanitsky_1c82522ba2c52</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/d_ivanitsky_1c82522ba2c52"/>
    <language>en</language>
    <item>
      <title>json_shield: Engineering an AI-Ready Dart Package</title>
      <dc:creator>D Ivanitsky</dc:creator>
      <pubDate>Thu, 16 Jul 2026 08:37:03 +0000</pubDate>
      <link>https://dev.to/d_ivanitsky_1c82522ba2c52/jsonshield-engineering-an-ai-ready-dart-package-18ik</link>
      <guid>https://dev.to/d_ivanitsky_1c82522ba2c52/jsonshield-engineering-an-ai-ready-dart-package-18ik</guid>
      <description>&lt;p&gt;Code generation has shifted. The majority of code calling a new pub.dev package is no longer typed by a human reading the documentation; it is generated by LLMs (Copilot, Cursor, Claude) operating within an IDE. If an AI assistant hallucinates your API, the human developer will blame your package and remove it.&lt;/p&gt;

&lt;p&gt;Building a package in 2026 requires engineering for two distinct consumers: the compiler and the LLM context window. &lt;/p&gt;

&lt;p&gt;This post details the architecture of making &lt;code&gt;json_shield&lt;/code&gt; AI-ready. It deconstructs the four artifacts that reach a consumer's AI, the one artifact that steers a contributor's agent, and the deterministic rules for writing them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The LLM Consumer Architecture
&lt;/h2&gt;

&lt;p&gt;When a developer prompts an assistant to "use json_shield to parse this response", the LLM does not browse the repository. It relies on Retrieval-Augmented Generation (RAG) and its pre-trained weights. Because the package is new, pre-trained weights are zero. &lt;/p&gt;

&lt;p&gt;You control exactly four artifacts that the IDE's RAG pipeline will index and inject into the prompt context.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The README: Density over Marketing
&lt;/h3&gt;

&lt;p&gt;A README is no longer a sales pitch; it is the primary context injection for an LLM. &lt;br&gt;
LLMs prioritize code blocks and structural formatting over prose. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Rule:&lt;/strong&gt; Optimize for token density.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Remove adjectives:&lt;/strong&gt; "Fast", "easy", and "lightweight" consume tokens without providing structural constraints.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Provide exact input/output pairs:&lt;/strong&gt; An LLM infers the API contract from examples faster than from text descriptions. &lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Declare negative constraints explicitly:&lt;/strong&gt; State what the package &lt;em&gt;cannot&lt;/em&gt; do. LLMs hallucinate features when boundaries are undefined. &lt;code&gt;json_shield&lt;/code&gt;'s README explicitly states: "No HTTP, no retries, no code generation." This prevents the model from attempting to chain &lt;code&gt;guard.decode&lt;/code&gt; into a &lt;code&gt;Dio&lt;/code&gt; interceptor pipeline that doesn't exist.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2. The &lt;code&gt;example/&lt;/code&gt; Directory: The Integration Vector
&lt;/h3&gt;

&lt;p&gt;The IDE's language server indexes the &lt;code&gt;example/&lt;/code&gt; folder. When an AI agent searches the local workspace for usage patterns, this folder is the highest-weight reference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Rule:&lt;/strong&gt; The example must be a self-contained, compilable representation of the exact production use case.&lt;br&gt;
Do not write synthetic &lt;code&gt;Foo&lt;/code&gt;/&lt;code&gt;Bar&lt;/code&gt; examples. &lt;code&gt;json_shield&lt;/code&gt; provides a full mock HTTP response, a standard &lt;code&gt;quicktype&lt;/code&gt; generated model, and the exact try/catch block for aggregate error reporting. The AI copies the structural logic from here, not the README.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Doc Comments (&lt;code&gt;///&lt;/code&gt;): Inline RAG Injection
&lt;/h3&gt;

&lt;p&gt;When a user hovers over a method or an AI agent requests definitions, the Dart analyzer extracts the &lt;code&gt;///&lt;/code&gt; comments. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Rule:&lt;/strong&gt; Doc comments must dictate the contract and the failure mode.&lt;br&gt;
A comment like &lt;code&gt;/// Decodes the JSON&lt;/code&gt; is zero-value data. The LLM needs to know the type constraints and exceptions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// Decodes a single object from a JSON map.&lt;/span&gt;
&lt;span class="c1"&gt;/// Throws [DecodeException] if the [data] shape mismatches the expected input &lt;/span&gt;
&lt;span class="c1"&gt;/// before [factory] is invoked.&lt;/span&gt;
&lt;span class="c1"&gt;/// Set [guard.verbose = true] to print payloads to the console.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This forces the AI to wrap the call in a &lt;code&gt;try/catch&lt;/code&gt; specifically targeting &lt;code&gt;DecodeException&lt;/code&gt;, preventing generic &lt;code&gt;catch (e)&lt;/code&gt; block generation.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. &lt;code&gt;pubspec.yaml&lt;/code&gt; Description: The Search Heuristic
&lt;/h3&gt;

&lt;p&gt;The 255-character description is the primary string indexed by pub.dev's search and external web scrapers.&lt;br&gt;
Do not use it for branding. Use it as an array of technical keywords.&lt;br&gt;
&lt;code&gt;json_shield&lt;/code&gt; description: "A zero-dependency structural guard for fromJson factories. Turns anonymous map casting crashes into actionable, typed error reports with stack traces."&lt;/p&gt;
&lt;h2&gt;
  
  
  The Contributor's Agent
&lt;/h2&gt;

&lt;p&gt;When another developer forks your repository to add a feature, they will likely use an agentic IDE (like Cursor) to write the PR. Left unconstrained, the agent will introduce external dependencies or break your architectural rules because doing so is statistically common in its training data.&lt;/p&gt;
&lt;h3&gt;
  
  
  The &lt;code&gt;rules.md&lt;/code&gt; / &lt;code&gt;.cursorrules&lt;/code&gt; Artifact
&lt;/h3&gt;

&lt;p&gt;You must provide a machine-readable directive file in the root directory. This acts as a system prompt override for any agent modifying the codebase.&lt;/p&gt;

&lt;p&gt;For &lt;code&gt;json_shield&lt;/code&gt;, the constraints defined in Part 1 are codified as hard rules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Architecture Constraints
- Zero dependencies. Do not add packages to pubspec.yaml.
- Pure Dart. Do not use flutter or dart:ui imports.
- No code generation.
- All modifications must preserve the exact argument order in public APIs: (factory, json).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When an agent is prompted to "add an HTTP client to fetch the schema," this file forces it to reject the prompt, citing the zero-dependency architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;API design is no longer just about human ergonomics; it is about machine predictability. A package that is easy for an LLM to parse, index, and generate accurate code for will achieve adoption. A package that relies on human intuition to fill the gaps in its documentation will fail at the generation step. Define the rules, strip the context overhead, and write machine-readable context.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>json_shield: Turning Anonymous fromJson Crashes Into Actionable Reports</title>
      <dc:creator>D Ivanitsky</dc:creator>
      <pubDate>Tue, 14 Jul 2026 11:47:44 +0000</pubDate>
      <link>https://dev.to/d_ivanitsky_1c82522ba2c52/jsonshield-turning-anonymous-fromjson-crashes-into-actionable-reports-3n6</link>
      <guid>https://dev.to/d_ivanitsky_1c82522ba2c52/jsonshield-turning-anonymous-fromjson-crashes-into-actionable-reports-3n6</guid>
      <description>&lt;p&gt;I'm Dmytro Ivanitsky, a senior developer shipping Flutter since its alpha days. Across 15+ production projects — from eSIM apps to retail payment flows — one specific crash kept billing me hours. I finally packaged the structural fix into &lt;a href="https://pub.dev/packages/json_shield" rel="noopener noreferrer"&gt;json_shield&lt;/a&gt;. This post covers its core design decisions; the next one in the series covers making it AI-ready for the assistants that increasingly write the code calling it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pain
&lt;/h2&gt;

&lt;p&gt;Every Flutter developer knows this crash report line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type 'List&amp;lt;dynamic&amp;gt;' is not a subtype of type 'Map&amp;lt;String, dynamic&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And knows what it &lt;em&gt;doesn't&lt;/em&gt; say: which of your 30 endpoints broke, what the backend actually sent, or whether it's one corrupt row or the whole schema. Release stack traces are obfuscated; async gaps shred the logical chain. You end up reproducing production traffic locally to learn that the backend wrapped a plain array into a pagination envelope on Tuesday.&lt;/p&gt;

&lt;p&gt;The root cause is structural: &lt;code&gt;fromJson&lt;/code&gt; factories (quicktype, json_serializable, hand-written — all of them) are chains of &lt;code&gt;as&lt;/code&gt; casts. They're &lt;em&gt;supposed&lt;/em&gt; to be brittle; that's the contract check. What's missing is attribution when they fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the same break looks like guarded
&lt;/h2&gt;

&lt;p&gt;The backend renames &lt;code&gt;zip&lt;/code&gt; to &lt;code&gt;zipCode&lt;/code&gt; three levels deep, inside &lt;code&gt;order.customer.address&lt;/code&gt;. Unguarded, that's a bare &lt;code&gt;type 'Null' is not a subtype of type 'String'&lt;/code&gt; from &lt;em&gt;somewhere&lt;/em&gt;. Guarded:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&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;guard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;context:&lt;/span&gt; &lt;span class="s"&gt;'orders_byId_get'&lt;/span&gt;&lt;span class="p"&gt;);&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;DecodeException: [orders_byId_get] expected Order, got _Map&amp;lt;String, dynamic&amp;gt;. 
cause: type 'Null' is not a subtype of type 'String'

rawData: {
  "id": "ord_12345",
  "customer": {
    "name": "John Doe",
    "address": {
      "city": "New York",
      "zipCode": "10001"
    }
  }
}

causeTrace:
#0      new Address.fromJson (package:app/models/address.dart:24:36)
#1      new Customer.fromJson (package:app/models/customer.dart:18:24)
#2      new Order.fromJson (package:app/models/order.dart:12:22)
#3      guard.decode (package:json_shield/json_shield.dart:85:14)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One report line answers the triage questions in order: &lt;strong&gt;which endpoint&lt;/strong&gt; (&lt;code&gt;orders_byId_get&lt;/code&gt;), &lt;strong&gt;which contract&lt;/strong&gt; (&lt;code&gt;Order&lt;/code&gt; on a body that &lt;em&gt;is&lt;/em&gt; a map — so the shape is fine, a field inside broke), &lt;strong&gt;what kind of break&lt;/strong&gt; (a &lt;code&gt;String&lt;/code&gt; field came back &lt;code&gt;Null&lt;/code&gt;). The preserved &lt;code&gt;causeTrace&lt;/code&gt; points at the exact line inside &lt;code&gt;Address.fromJson&lt;/code&gt; — the nested model three levels down — and in a debug run with &lt;code&gt;guard.verbose = true&lt;/code&gt; the exception carries the raw payload — and the failure line above lands in the console by itself, without a breakpoint — the renamed key visible on sight. Ticket to the backend team written in one glance, no local reproduction of production traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design constraints
&lt;/h2&gt;

&lt;p&gt;The package is engineered specifically for rapid development. To guarantee high iteration velocity and zero integration friction, these four requirements were fixed before any code was written:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Generated models stay untouched.&lt;/strong&gt; Rapid development means schemas change often. While tools like &lt;code&gt;quicktype.io&lt;/code&gt; are highly prevalent in these workflows, the package supports any serializer exposing a &lt;code&gt;fromJson&lt;/code&gt; method. Because generated output is overwritten constantly, any manual additions die on the next run: no annotations, no base classes, no secondary code generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One operator at the call site.&lt;/strong&gt; Speed requires minimal setup. Not a custom HTTP client, not a configuration object, not a builder — just a single function you wrap around the &lt;code&gt;fromJson&lt;/code&gt; call you already have.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every failure carries its source.&lt;/strong&gt; Ambiguous crashes halt development; triage must be instantaneous. The endpoint name, expected type, original exception, and exact stack trace are packed into one typed object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero dependencies.&lt;/strong&gt; Version conflicts stall builds. The core must be completely isolated: pure Dart, working identically across Flutter, CLI, and server code.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The API
&lt;/h2&gt;

&lt;p&gt;The whole public surface:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:json_shield/json_shield.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Single object. T is inferred from the factory tear-off.&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;context:&lt;/span&gt; &lt;span class="s"&gt;'users_me_get'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Top-level array.&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decodeList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nl"&gt;context:&lt;/span&gt; &lt;span class="s"&gt;'orders_list'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Failure handling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;guard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;decodeList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;context:&lt;/span&gt; &lt;span class="s"&gt;'orders_list'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kd"&gt;on&lt;/span&gt; &lt;span class="n"&gt;DecodeListException&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// e.total = 100, e.errors = [(index: 17, error: ...), ...]&lt;/span&gt;
  &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "3/100 failed. #17: ...; #41: ...; #98: ..."&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kd"&gt;on&lt;/span&gt; &lt;span class="n"&gt;DecodeException&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Broken array shape (envelope, null body) or single-object failure.&lt;/span&gt;
  &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// e.context, e.expected, e.cause, e.causeTrace&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it: two methods, two exception types, one flag. One file, ~200 lines, zero dependencies. &lt;a href="https://pub.dev/packages/json_shield" rel="noopener noreferrer"&gt;pub.dev&lt;/a&gt; · &lt;a href="https://github.com/USERNAME/json_shield" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Decisions worth defending
&lt;/h2&gt;

&lt;p&gt;Four choices look wrong at first glance. Each is deliberate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Shape checks run &lt;em&gt;before&lt;/em&gt; your factory
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;null&lt;/code&gt; body, a &lt;code&gt;List&lt;/code&gt; where an object is expected, a primitive, a pagination envelope on an array endpoint — all rejected with &lt;code&gt;DecodeException(expected: ...)&lt;/code&gt; before &lt;code&gt;fromJson&lt;/code&gt; is ever invoked. Your factory only sees a &lt;code&gt;Map&amp;lt;String, dynamic&amp;gt;&lt;/code&gt;; the cryptic subtype error class of crashes is gone, replaced by a named expectation. If the factory itself throws — &lt;code&gt;age&lt;/code&gt; came back as a string — the exception is wrapped with the original cause and its stack trace preserved, not swallowed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lists are traversed fully, failures aggregated
&lt;/h3&gt;

&lt;p&gt;Fail-fast decoding reports element #0, you fix it, deploy, and meet element #41. &lt;code&gt;decodeList&lt;/code&gt; never stops: it decodes every element, collects every failure with its index, and throws one &lt;code&gt;DecodeListException&lt;/code&gt; carrying all of them. One run yields the complete map of a broken contract — and the pattern inside it. Identical cause on every index? The backend changed the schema. Three scattered indices? Dirty rows in their database. That distinction is a one-glance read in the aggregate and invisible in fail-fast.&lt;/p&gt;

&lt;p&gt;The deliberate flip side: it &lt;em&gt;still throws&lt;/em&gt; on a single bad element. Silently skipping broken rows and rendering the rest hides data loss from you and your users; if you want partial rendering, catch the aggregate and decide — the decoder won't decide for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Payloads are redacted by default
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;DecodeException.rawData&lt;/code&gt; — the actual JSON that failed — is &lt;code&gt;null&lt;/code&gt; and the console stays silent unless you flip &lt;code&gt;guard.verbose = true&lt;/code&gt; in a debug entry point; with the flag on, every failure is also printed the moment it happens, one line per broken element. Backwards from every logging library, deliberate here: response payloads contain user data, and exceptions flow into crash reporters. After years of wiring gateways into commerce apps, payloads carrying card and customer data are not a hypothetical. The failure mode of &lt;em&gt;forgetting the flag&lt;/em&gt; had to be "less diagnostic detail," never "PII in Sentry."&lt;/p&gt;

&lt;h3&gt;
  
  
  The API shrank during development
&lt;/h3&gt;

&lt;p&gt;Earlier iterations had &lt;code&gt;one&lt;/code&gt;/&lt;code&gt;many&lt;/code&gt; combinator functions, a unified reified-generics dispatcher, string-input variants, &lt;code&gt;tryDecode&lt;/code&gt; result types, and an encode side. All cut. Every additional entry point is another decision at the call site and another way to hold the tool wrong; the two survivors cover the actual production need — decoding a response you just received. The single least-obvious survivor is the argument order, &lt;code&gt;(fromJson, json)&lt;/code&gt;: factory first reads as "decode &lt;em&gt;as User&lt;/em&gt; this data" and keeps the tear-off — the thing that determines the type — in the position your eye checks first.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it refuses to do
&lt;/h2&gt;

&lt;p&gt;No HTTP, no retries, no schema validation, no code generation. A decoding guard that grows a network client stops being auditable in one sitting — and being auditable in one sitting is a feature: the entire implementation is a single file you can read before trusting it with production traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next in the series
&lt;/h2&gt;

&lt;p&gt;A growing share of the code calling any package is written by AI assistants that have never seen its repository. Part 2 covers what that changes for package authors: the four artifacts that reach a consumer's AI, the one that steers a contributor's agent, and how to write both so models stop hallucinating your API.&lt;/p&gt;

&lt;p&gt;json_shield: &lt;a href="https://pub.dev/packages/json_shield" rel="noopener noreferrer"&gt;pub.dev&lt;/a&gt; · &lt;a href="https://github.com/USERNAME/json_shield" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Issues and API roasting welcome — the surface is frozen pre-1.0, but frozen by discussion, not by ego.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>opensource</category>
      <category>errors</category>
    </item>
  </channel>
</rss>
