<?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: Henk</title>
    <description>The latest articles on DEV Community by Henk (@excorporate).</description>
    <link>https://dev.to/excorporate</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%2F4036583%2F3ff7a9b7-0c90-4cf1-9c75-10cadcedc319.png</url>
      <title>DEV Community: Henk</title>
      <link>https://dev.to/excorporate</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/excorporate"/>
    <language>en</language>
    <item>
      <title>Building Court-Ready PDFs: What Legal Tech Taught Me About Deterministic Software</title>
      <dc:creator>Henk</dc:creator>
      <pubDate>Sun, 19 Jul 2026 14:12:36 +0000</pubDate>
      <link>https://dev.to/excorporate/building-court-ready-pdfs-what-legal-tech-taught-me-about-deterministic-software-a5l</link>
      <guid>https://dev.to/excorporate/building-court-ready-pdfs-what-legal-tech-taught-me-about-deterministic-software-a5l</guid>
      <description>&lt;p&gt;Most AI products are allowed to be a little unpredictable.&lt;/p&gt;

&lt;p&gt;A generated illustration can look different each time. A marketing draft can choose a different adjective. Even a coding assistant can offer several valid implementations.&lt;/p&gt;

&lt;p&gt;Court documents are different.&lt;/p&gt;

&lt;p&gt;A filing can contain a persuasive argument and still create problems because it uses the wrong margins, caption structure, line spacing, page numbering, or pleading-paper format.&lt;/p&gt;

&lt;p&gt;While building &lt;a href="https://www.pleadwise.com/" rel="noopener noreferrer"&gt;Pleadwise, a court-document formatting tool for self-represented litigants&lt;/a&gt;, I learned that legal technology needs a different architecture from the typical “send everything to an LLM” product.&lt;/p&gt;

&lt;p&gt;The most important lesson was simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use AI where variation is useful. Use deterministic software where precision is mandatory.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The document is structured data
&lt;/h2&gt;

&lt;p&gt;A court filing may look like a long block of text, but treating it as one string quickly becomes painful.&lt;/p&gt;

&lt;p&gt;It is better represented as a collection of fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;court&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;jurisdiction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;federal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;district&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;northern-district-of-california&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;case&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3:26-cv-00000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;plaintiff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Example Plaintiff&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;defendant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Example Defendant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;document&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="s2"&gt;motion&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Motion to Dismiss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="na"&gt;signature&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="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This separation makes validation possible before a PDF is generated.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;validateCaption&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;filing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;A case number is required.&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;filing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;plaintiff&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;filing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;defendant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Both parties must appear in the caption.&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Structured input also makes it easier to change renderers later. The same case data could eventually produce a PDF, editable source file, filing checklist, or accessible HTML preview.&lt;/p&gt;

&lt;p&gt;The renderer should not need to rediscover what every paragraph means.&lt;/p&gt;

&lt;h2&gt;
  
  
  Court rules belong in a rules engine
&lt;/h2&gt;

&lt;p&gt;Formatting rules vary between courts. A static Word template cannot reliably represent all those differences.&lt;/p&gt;

&lt;p&gt;A more scalable approach is to store formatting requirements as data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;courtProfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;example-district&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fontFamily&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Times New Roman&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;minimumFontSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lineSpacing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;marginTop&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;marginBottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;marginLeft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;marginRight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;requiresAttorneyBlock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;requiresCaseNumber&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The rendering layer can then consume the selected court profile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;courtProfile&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="nf"&gt;renderPdf&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filing&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="na"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;courtProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pageLayout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;courtProfile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives the application one source of truth for each supported court.&lt;/p&gt;

&lt;p&gt;It also creates an important operational benefit: when a court changes a requirement, the rule can be updated without rebuilding the entire document workflow.&lt;/p&gt;

&lt;p&gt;Of course, court rules are not ordinary configuration. They need sources, review dates, and change tracking. A production rule record should include metadata such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;sourceUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://court.example.gov/local-rules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;reviewedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-07-01&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;effectiveAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2026-06-15&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;reviewedBy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;human-reviewer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That provenance is as important as the rule itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI should not control the final layout
&lt;/h2&gt;

&lt;p&gt;AI can help organize user-provided facts, explain unfamiliar terminology, or create a first draft for review.&lt;/p&gt;

&lt;p&gt;It should not decide whether a document needs numbered pleading paper or whether a caption fits the selected court.&lt;/p&gt;

&lt;p&gt;Those are deterministic questions.&lt;/p&gt;

&lt;p&gt;In Pleadwise, guided drafting and source-linked case-law search are treated as separate tools. Generated legal content must still be reviewed, and citations must be checked against their original sources.&lt;/p&gt;

&lt;p&gt;The final formatting pipeline should behave more like a compiler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Structured input
      ↓
Validation
      ↓
Court-rule lookup
      ↓
Document assembly
      ↓
PDF compilation
      ↓
Preflight checks
      ↓
User review
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Given the same input and rule version, the system should produce the same result.&lt;/p&gt;

&lt;p&gt;That predictability makes testing and debugging dramatically easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a preflight checker
&lt;/h2&gt;

&lt;p&gt;A useful document generator should inspect its own output.&lt;/p&gt;

&lt;p&gt;Depending on the format, preflight checks could verify:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Required caption fields are present&lt;/li&gt;
&lt;li&gt;Page margins match the selected court profile&lt;/li&gt;
&lt;li&gt;Font sizes meet the configured minimum&lt;/li&gt;
&lt;li&gt;Page numbers appear in the expected location&lt;/li&gt;
&lt;li&gt;No placeholder text remains&lt;/li&gt;
&lt;li&gt;Signature information is included&lt;/li&gt;
&lt;li&gt;Content does not overflow the printable area&lt;/li&gt;
&lt;li&gt;A PDF was generated successfully and can be opened&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some checks can happen before rendering. Others require inspecting the generated artifact.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;preflight&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="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="na"&gt;missingFields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;checkRequiredFields&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="na"&gt;unresolvedTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;findTokens&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="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\{\{&lt;/span&gt;&lt;span class="sr"&gt;.+&lt;/span&gt;&lt;span class="se"&gt;?\}\}&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;layoutWarnings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;inspectRenderedPages&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="na"&gt;passed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In higher-stakes software, “the export completed” is not the same as “the output is ready.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Failure states deserve first-class design
&lt;/h2&gt;

&lt;p&gt;PDF compilation can fail. A user can close the browser midway through a workflow. A network request can time out after payment but before the document appears.&lt;/p&gt;

&lt;p&gt;These are not edge cases when the person using the product may have an approaching filing deadline.&lt;/p&gt;

&lt;p&gt;A resilient workflow should:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Save structured progress before starting generation.&lt;/li&gt;
&lt;li&gt;Make generation jobs safe to retry.&lt;/li&gt;
&lt;li&gt;Avoid charging twice for the same request.&lt;/li&gt;
&lt;li&gt;Return a document credit when generation fails.&lt;/li&gt;
&lt;li&gt;Show a useful error instead of a generic failure message.&lt;/li&gt;
&lt;li&gt;Keep enough diagnostic information to reproduce the problem without unnecessarily exposing sensitive document content.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The unhappy path is part of the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Privacy changes the architecture
&lt;/h2&gt;

&lt;p&gt;Legal documents can contain names, addresses, financial information, medical details, and allegations that users never intended to make broadly accessible.&lt;/p&gt;

&lt;p&gt;That means document storage cannot be treated like a folder of ordinary downloadable assets.&lt;/p&gt;

&lt;p&gt;Useful safeguards include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Private storage by default&lt;/li&gt;
&lt;li&gt;Short-lived signed download links&lt;/li&gt;
&lt;li&gt;Strict authorization checks&lt;/li&gt;
&lt;li&gt;Minimal retention&lt;/li&gt;
&lt;li&gt;Clear deletion behavior&lt;/li&gt;
&lt;li&gt;Limited logging of document contents&lt;/li&gt;
&lt;li&gt;Sending only necessary context to external AI providers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Security is not a feature that can be added after the document pipeline is finished. It shapes the pipeline.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would tell other developers
&lt;/h2&gt;

&lt;p&gt;If you are building software for law, finance, healthcare, compliance, or another rules-heavy field, start by separating three concerns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;User-provided facts&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Generated or suggested content&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authoritative formatting and validation rules&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do not let probabilistic output silently cross into the authoritative layer.&lt;/p&gt;

&lt;p&gt;AI is excellent at helping people move from a blank page to a structured draft. Deterministic software is excellent at applying known constraints repeatedly.&lt;/p&gt;

&lt;p&gt;The strongest products use both—and make the boundary visible.&lt;/p&gt;

&lt;p&gt;That is the approach behind &lt;a href="https://www.pleadwise.com/" rel="noopener noreferrer"&gt;Pleadwise&lt;/a&gt;: guided drafting where it helps, deterministic court formatting where consistency matters, and explicit user review before anything is filed.&lt;/p&gt;

&lt;p&gt;The interesting engineering challenge is not getting an AI model to produce more text.&lt;/p&gt;

&lt;p&gt;It is building a system people can inspect, correct, and trust.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: I’m building Pleadwise. It is a document-formatting and drafting tool, not a law firm, and it does not provide legal advice.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>llm</category>
      <category>softwareengineering</category>
    </item>
  </channel>
</rss>
