<?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: Anuj Sadani</title>
    <description>The latest articles on DEV Community by Anuj Sadani (@anuj-sadani).</description>
    <link>https://dev.to/anuj-sadani</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%2F4202888%2F6844b643-7509-49b0-8798-eade0d8a416b.png</url>
      <title>DEV Community: Anuj Sadani</title>
      <link>https://dev.to/anuj-sadani</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/anuj-sadani"/>
    <language>en</language>
    <item>
      <title>Valid JSON Is Only Half the Output Contract</title>
      <dc:creator>Anuj Sadani</dc:creator>
      <pubDate>Sat, 24 Oct 2026 02:29:27 +0000</pubDate>
      <link>https://dev.to/anuj-sadani/valid-json-is-only-half-the-output-contract-5e6c</link>
      <guid>https://dev.to/anuj-sadani/valid-json-is-only-half-the-output-contract-5e6c</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flht4bknr46zlmkbcyyvg.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flht4bknr46zlmkbcyyvg.png" alt=" " width="640" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A response can be valid JSON and still give a downstream consumer the wrong shape. A Markdown handoff can read fluently while omitting the one section the next engineer needs.&lt;/p&gt;

&lt;p&gt;Those are concrete interface failures. They deserve checks that say which field or section broke the agreement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/asadani/output-contracts" rel="noopener noreferrer"&gt;Output Contracts&lt;/a&gt; is a small Python validator for explicit Markdown and JSON requirements. It does not call a model, score the writing, or silently repair the output.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write down the consumer requirement
&lt;/h2&gt;

&lt;p&gt;Imagine a work handoff that must contain these sections in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;li&gt;Evidence&lt;/li&gt;
&lt;li&gt;Unresolved questions&lt;/li&gt;
&lt;li&gt;Next steps&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A handoff that omits unresolved questions and moves evidence below the next steps might be readable, but it no longer follows the agreed interface. The bundled Markdown contract declares required headings, ordering, and a forbidden completion phrase.&lt;/p&gt;

&lt;p&gt;The last rule has a narrow meaning. Banning &lt;code&gt;Everything is complete.&lt;/code&gt; can enforce a wording constraint. It cannot determine whether the work is actually complete or whether another phrase makes an equally unsupported claim.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate the bad handoff
&lt;/h2&gt;

&lt;p&gt;Use Python 3.11 or newer. There are no third-party runtime dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/asadani/output-contracts.git
&lt;span class="nb"&gt;cd &lt;/span&gt;output-contracts
&lt;span class="nv"&gt;out_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

python &lt;span class="nt"&gt;-m&lt;/span&gt; output_contracts validate &lt;span class="se"&gt;\&lt;/span&gt;
  fixtures/handoff-contract.json fixtures/handoff-bad.md &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--format&lt;/span&gt; markdown &lt;span class="nt"&gt;--out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$out_dir&lt;/span&gt;&lt;span class="s2"&gt;/handoff-review.md"&lt;/span&gt;

python &lt;span class="nt"&gt;-m&lt;/span&gt; output_contracts inspect fixtures/handoff-contract.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validation exits with status &lt;code&gt;1&lt;/code&gt; because the fixture violates the contract. The report identifies the missing section, the ordering problem, and the forbidden phrase. &lt;code&gt;inspect&lt;/code&gt; validates the contract itself and prints its normalized summary.&lt;/p&gt;

&lt;p&gt;A clean validation returns &lt;code&gt;0&lt;/code&gt;. Invalid contracts, unreadable input, and command errors return &lt;code&gt;2&lt;/code&gt;, so a calling script can distinguish a rejected output from a broken validation operation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check values as well as syntax
&lt;/h2&gt;

&lt;p&gt;The JSON example requires an identifier, a constrained status, an integer count, and a non-empty items array. The bad fixture includes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"case-18"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"accepted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This parses as JSON. It still violates two declared requirements: &lt;code&gt;true&lt;/code&gt; does not satisfy the integer type, and an empty array does not satisfy the minimum item count.&lt;/p&gt;

&lt;p&gt;Run both versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python &lt;span class="nt"&gt;-m&lt;/span&gt; output_contracts validate &lt;span class="se"&gt;\&lt;/span&gt;
  fixtures/result-contract.json fixtures/result-bad.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--format&lt;/span&gt; json &lt;span class="nt"&gt;--out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$out_dir&lt;/span&gt;&lt;span class="s2"&gt;/bad-result-review.json"&lt;/span&gt;

python &lt;span class="nt"&gt;-m&lt;/span&gt; output_contracts validate &lt;span class="se"&gt;\&lt;/span&gt;
  fixtures/result-contract.json fixtures/result-good.json &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--format&lt;/span&gt; markdown &lt;span class="nt"&gt;--out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$out_dir&lt;/span&gt;&lt;span class="s2"&gt;/good-result-review.md"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The adverse example exits with &lt;code&gt;1&lt;/code&gt;; the good example exits with &lt;code&gt;0&lt;/code&gt;. Fields use JSON pointers, and null remains distinct from a missing field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the contract proportionate
&lt;/h2&gt;

&lt;p&gt;The current implementation is deliberately small. Markdown heading checks recognize ATX headings outside fenced code; they do not treat bold text or HTML as headings. Forbidden patterns scan the full output, including code blocks.&lt;/p&gt;

&lt;p&gt;That detail can create false positives. A tutorial quoting a forbidden phrase in an example may need a different contract from a handoff that must never contain it. Patterns come from trusted contract authors, and complex regular expressions are not sandboxed or guaranteed to run in linear time.&lt;/p&gt;

&lt;p&gt;For general JSON Schema validation, an established validator may fit better. This project is a compact option for a bounded workflow that needs both report-oriented Markdown checks and a small set of JSON field constraints.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate before the next operation
&lt;/h2&gt;

&lt;p&gt;Put structural validation at the point where another person or program consumes the output. Save the report and route violations for correction. Keep content review separate: a required Evidence section can still contain a false statement, and a correctly typed count can still be wrong.&lt;/p&gt;

&lt;p&gt;Start with one recurring failure that costs someone time. Add a rule for it, check useful and adverse examples, and watch for false positives before adding more constraints.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/asadani/output-contracts" rel="noopener noreferrer"&gt;MIT-licensed repository&lt;/a&gt; includes the fixture contracts, &lt;a href="https://github.com/asadani/output-contracts/blob/main/docs/SCHEMA.md" rel="noopener noreferrer"&gt;format reference&lt;/a&gt;, and validator. Its useful promise is precise feedback about declared rules.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI disclosure: This article was drafted with AI assistance using the project's code and fixtures. Passing these checks establishes structural conformance, not factual correctness.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>python</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Make Unresolved Decisions Visible Before a Specification Reaches Engineering</title>
      <dc:creator>Anuj Sadani</dc:creator>
      <pubDate>Fri, 23 Oct 2026 16:18:36 +0000</pubDate>
      <link>https://dev.to/anuj-sadani/make-unresolved-decisions-visible-before-a-specification-reaches-engineering-3io8</link>
      <guid>https://dev.to/anuj-sadani/make-unresolved-decisions-visible-before-a-specification-reaches-engineering-3io8</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyqhz5n0cvvl3evayvab7.png" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyqhz5n0cvvl3evayvab7.png" alt=" " width="640" height="256"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A specification can look complete while containing two limits for the same field and an unanswered question about who may approve an action. A reviewer might notice both, but the next revision can make it hard to tell which findings were discussed and which still apply.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/asadani/spec-sentry" rel="noopener noreferrer"&gt;Spec Sentry&lt;/a&gt; explores a narrow approach: annotate the facts that need mechanical checks, then keep review decisions in a separate ledger.&lt;/p&gt;

&lt;p&gt;This is most useful when a team is willing to make important uncertainty explicit. The checker does not promise to find every contradiction in ordinary prose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declare the parts that must agree
&lt;/h2&gt;

&lt;p&gt;The fixture contains these declarations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@quantity preview.max_size = 10 MB
@quantity preview.max_size = 10 GB
@decision sharing-policy | pending | Who may invite guests?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both quantities use the same key but declare conflicting values or units. The pending decision makes an unanswered product question visible instead of leaving an engineer to invent a default.&lt;/p&gt;

&lt;p&gt;Term references can also be explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The initial preview uses [[term:external collaborator]].
@define workspace: A named product collaboration area.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The undefined &lt;code&gt;external collaborator&lt;/code&gt; reference becomes a finding. A definition for a different term does not resolve it.&lt;/p&gt;

&lt;p&gt;These directives use a small documented grammar. They are easy to inspect because they declare the thing being checked instead of asking the checker to infer the meaning of every sentence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the fixture
&lt;/h2&gt;

&lt;p&gt;Use Python 3.11 or newer. The runtime needs no third-party dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/asadani/spec-sentry.git
&lt;span class="nb"&gt;cd &lt;/span&gt;spec-sentry
&lt;span class="nv"&gt;out_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

python &lt;span class="nt"&gt;-m&lt;/span&gt; spec_sentry check sharing-spec.md &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--root&lt;/span&gt; fixtures &lt;span class="nt"&gt;--out&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$out_dir&lt;/span&gt;&lt;span class="s2"&gt;/findings.json"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command exits with status &lt;code&gt;1&lt;/code&gt; because the example intentionally contains findings. Status &lt;code&gt;0&lt;/code&gt; means no configured findings were observed; status &lt;code&gt;2&lt;/code&gt; indicates invalid input or an operational error.&lt;/p&gt;

&lt;p&gt;The output locates the conflicting declarations, undefined marked term, and pending decision. It gives the reviewer source evidence to inspect rather than editing the document.&lt;/p&gt;

&lt;h2&gt;
  
  
  A reviewer decision belongs to a version
&lt;/h2&gt;

&lt;p&gt;A reviewer can accept, dismiss, or defer a finding with a note. The decision ledger is separate from the specification and records the fingerprint associated with the review.&lt;/p&gt;

&lt;p&gt;That separation matters after an edit. A dismissal against an older version should not automatically hide a finding in new text. When the relevant evidence changes, the prior disposition becomes stale and needs another look.&lt;/p&gt;

&lt;p&gt;The report excludes only fresh dismissed findings from its unresolved result. Accepting a finding records a review decision; it does not erase the underlying issue. This keeps discussion history distinct from a claim that the specification is now correct.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/asadani/spec-sentry#quickstart" rel="noopener noreferrer"&gt;full walkthrough&lt;/a&gt; demonstrates the check, decision, and report lifecycle with a temporary copy of the fixture.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pay attention to the cost of annotations
&lt;/h2&gt;

&lt;p&gt;Explicit syntax trades broad coverage for reproducible checks. If a limit appears only in prose without a declared quantity, the tool cannot assume that it belongs to a matching key. Markdown headings do not create implicit scopes, and units are compared as written rather than converted.&lt;/p&gt;

&lt;p&gt;Those limits affect how a team should use the tool. Annotate the few quantities, terms, and decisions that repeatedly create ambiguity. Do not require a new notation for every sentence unless it produces enough useful findings to justify the authoring burden.&lt;/p&gt;

&lt;p&gt;The same principle applies to review ledgers. A ledger that requires more effort than it saves will become another stale artifact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it before a planning handoff
&lt;/h2&gt;

&lt;p&gt;Take one small feature spec with a few consequential limits and unresolved questions. Add the explicit declarations, run the check, and review the findings with the engineer who will implement the feature.&lt;/p&gt;

&lt;p&gt;Measure whether the check surfaces something actionable and whether the recorded decisions survive the next revision without concealing new uncertainty. That is a more useful test than counting annotations or declaring the document approved because the report is clean.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/asadani/spec-sentry" rel="noopener noreferrer"&gt;MIT-licensed repository&lt;/a&gt; includes the grammar, ledger format, fixtures, and checks. It offers a bounded way to keep selected questions visible through review.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI disclosure: This article was drafted with AI assistance using the project's implementation and synthetic specification. It does not report a measured reduction in specification defects.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>documentation</category>
      <category>testing</category>
      <category>python</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
