<?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: Auka Wave</title>
    <description>The latest articles on DEV Community by Auka Wave (@aukawave8).</description>
    <link>https://dev.to/aukawave8</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%2F4232417%2F36155cde-d52b-4f71-80fb-4b65e726813f.jpg</url>
      <title>DEV Community: Auka Wave</title>
      <link>https://dev.to/aukawave8</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aukawave8"/>
    <language>en</language>
    <item>
      <title>From spreadsheet to template: the row that keeps a price table honest</title>
      <dc:creator>Auka Wave</dc:creator>
      <pubDate>Sat, 07 Nov 2026 18:47:00 +0000</pubDate>
      <link>https://dev.to/aukawave8/from-spreadsheet-to-template-the-row-that-keeps-a-price-table-honest-4b1o</link>
      <guid>https://dev.to/aukawave8/from-spreadsheet-to-template-the-row-that-keeps-a-price-table-honest-4b1o</guid>
      <description>&lt;p&gt;&lt;em&gt;A price table has to survive being dated, sourced, unit-checked and half-empty. Here is the row we ended up with, the one rule that carried the most weight, and three checks you can add to your own data product tomorrow.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI-assisted writing; the figures come from our own product files.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: a table with three ways to be wrong
&lt;/h2&gt;

&lt;p&gt;Every price table starts as a spreadsheet: one row per thing, one column per side, one number typed in from wherever you last read it. That spreadsheet is fine on day one and quietly broken by month three.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A unit that does not match the others.&lt;/strong&gt; In our table, the museum and park entrance line is CNY 60 on one side and USD 35 on the other — but the Chinese side is a ticket per person and the Western side is a pass per vehicle that admits everyone in the car. Their ratio is nonsense. There is also a rental deposit row where the quantity is not money: deposits are contract terms, so the figure is a number of months of rent. A column headed "price" that holds "months" is a unit error waiting to be read as a number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A figure that moved and nobody noticed.&lt;/strong&gt; Prices change; tables do not. Unless the row carries the date its source was read, a number from two years ago is indistinguishable from one from last Tuesday. Every row in our file carries a "last checked" date, and all 62 of ours carry the same one — 2026-11-05, the day the sources were read.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A cell filled in from memory.&lt;/strong&gt; This is the one that costs a table its credibility. Eight of our 62 lines have no published tariff on either side: a personal training session, a common over-the-counter medicine, a rental deposit, a domestic flight, a dry-cleaned jacket, a night of pet boarding, an average online order value. None is impossible to price; all are impossible to price from a source you could cite. The trap is the blank cell — it feels like an embarrassment, so somebody fills it in from memory, and a remembered number is then identical to a sourced one. Nothing in the table can tell them apart.&lt;/p&gt;

&lt;p&gt;None of those three is a research problem. They are schema problems, and you can design them away. Our table is 62 lines across 13 categories, drawn from 57 publishers and read on a single day; of those, 13 are filled on both sides, 41 on one side only, 8 on neither side.&lt;/p&gt;

&lt;h2&gt;
  
  
  The row
&lt;/h2&gt;

&lt;p&gt;A spreadsheet lets a cell hold anything. A database does not, once you give it the right properties. Here is the row we settled on:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# one row = one figure&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;         &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Household&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;electricity,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tier&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;      &lt;span class="c1"&gt;# the line this figure belongs to&lt;/span&gt;
&lt;span class="na"&gt;measure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;residential&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;electricity,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;first&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tier"&lt;/span&gt;  &lt;span class="c1"&gt;# what the number measures&lt;/span&gt;
&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;        &lt;span class="m"&gt;0.4883&lt;/span&gt;
&lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;         &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;per&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;kWh"&lt;/span&gt;                             &lt;span class="c1"&gt;# the basis the value is counted in&lt;/span&gt;
&lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;     &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CNY"&lt;/span&gt;
&lt;span class="na"&gt;source_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;   &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://..."&lt;/span&gt;                          &lt;span class="c1"&gt;# the page the number was read from&lt;/span&gt;
&lt;span class="na"&gt;source_date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-11-05"&lt;/span&gt;                          &lt;span class="c1"&gt;# the date that page was read&lt;/span&gt;
&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;       &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;verified"&lt;/span&gt;                            &lt;span class="c1"&gt;# verified | verified (one side) | verified (derived) | verified (indexed) | pending verification&lt;/span&gt;
&lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;         &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;                                    &lt;span class="c1"&gt;# why the number reads the way it does&lt;/span&gt;
&lt;span class="na"&gt;next_step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;                                    &lt;span class="c1"&gt;# what to open, and what to write down&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Four of those fields do the real work.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;unit&lt;/code&gt; is the one people forget. "Per kWh", "per ride", "per pound", "per dozen", "per lease" — as long as the basis is a separate property instead of a word buried in the item name, two rows that measure different things cannot be silently averaged, and a derived column knows when it must refuse to compute.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;currency&lt;/code&gt; is separate from &lt;code&gt;value&lt;/code&gt; for the same reason: some rows hold a quantity that is not money. A rental deposit is measured in months of rent, so that row has to be allowed to leave the currency empty rather than guess one.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;source_date&lt;/code&gt; is not &lt;code&gt;updated_at&lt;/code&gt;. It is the day the source was read, not the day somebody edited the row. Editing a note does not refresh a price, and a schema that treats the two as one field will quietly do that for you.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;status&lt;/code&gt; is the field almost every spreadsheet is missing, and it carries the most weight here. It is not a boolean: it says whether a figure is verified, verified on one side only, verified as an indexed or derived figure, or still pending — and "pending" is a legal value, not a failure state.&lt;/p&gt;

&lt;p&gt;The same row, when the figure does not exist in a comparable form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;         &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rental&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;deposit"&lt;/span&gt;
&lt;span class="na"&gt;measure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;deposit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;required,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;months&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rent"&lt;/span&gt;
&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;        &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;         &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;per&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lease"&lt;/span&gt;
&lt;span class="na"&gt;currency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;     &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="na"&gt;source_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;   &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="na"&gt;source_date&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;       &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pending&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;verification"&lt;/span&gt;
&lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;         &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deposits&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;are&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;contract&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;terms,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;not&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;published&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;tariffs."&lt;/span&gt;
&lt;span class="na"&gt;next_step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Multiply&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;one&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;month&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;of&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rent&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;by&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;reference&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;city&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;rent,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;or&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;read&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;deposit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;months&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;named&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;standard&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;residential&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;lease&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;requires."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is not a hole in the row. It is a row that knows what it is missing and what to do about it.&lt;/p&gt;

&lt;p&gt;The shipped template puts both sides in one row, so a comparison lives in a single place: &lt;code&gt;China Price CNY&lt;/code&gt;, &lt;code&gt;China Price USD&lt;/code&gt;, &lt;code&gt;West Price USD&lt;/code&gt;, a &lt;code&gt;Basis&lt;/code&gt;, and a source URL each. The currency therefore lives in the column name rather than in a field of its own, and everything else maps across: &lt;code&gt;Item&lt;/code&gt; for the name, &lt;code&gt;Basis&lt;/code&gt; for the unit, &lt;code&gt;Last Checked&lt;/code&gt; for the source date, &lt;code&gt;Source China&lt;/code&gt; and &lt;code&gt;Source West&lt;/code&gt; for the URLs, &lt;code&gt;Status&lt;/code&gt;, &lt;code&gt;Note&lt;/code&gt;, &lt;code&gt;Next Step&lt;/code&gt;. The one field with no place above is the derived one — "how many times dearer" the Western price is — and it is deliberately left empty in two of the 13 rows with both sides filled.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one rule: a cell you cannot source is information
&lt;/h2&gt;

&lt;p&gt;Everything above follows from one rule: &lt;strong&gt;a cell you cannot source is information, not a gap.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the product, the price cells stay empty, the status reads &lt;code&gt;Pending verification&lt;/code&gt;, the source type is &lt;code&gt;market-reference&lt;/code&gt; so pending rows filter out of the settled ones in one click, and &lt;code&gt;Next Step&lt;/code&gt; carries a written procedure, not a disclaimer. For the personal training row it names the route — quote a named chain's published single-session price and record the date you read it — and for the medicine row it names the national drug price registry on one side and the absence of any published retail price on the other.&lt;/p&gt;

&lt;p&gt;Why that is a feature for the buyer and not a defect: the queue is visible, and the buyer can shrink it. Filter to the pending rows, follow the next step, fill the figure in, paste the source, set the date, move the status on. Every line they fill is a line they can defend.&lt;/p&gt;

&lt;p&gt;Our own product notes put it more plainly: filling those cells with plausible-looking numbers teaches you to trust numbers nobody checked — this one teaches you to go and check.&lt;/p&gt;

&lt;p&gt;The honest limit: we cannot source anything for you, and we cannot decide whether two figures are comparable. We can make both decisions visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three checks you can add tomorrow
&lt;/h2&gt;

&lt;p&gt;If you build a small data product — a price table, a benchmark sheet, a rate card — three checks will do most of the work, and none of them needs code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. A field that must be non-empty.&lt;/strong&gt; If a row holds a value, it must hold a source URL and a source date. Not "the table should have sources somewhere" — a per-row rule, so one violation is visible and nameable. A price cell with an empty source cell beside it is the only thing we treat as a defect: that is the cell a reader takes as evidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. A currency and a unit that must agree.&lt;/strong&gt; The currency must match the publisher: a CNY figure from a Chinese publisher, a USD figure from a Western one. The unit must be the unit the source quotes — per kWh, per ride, per pound, per dozen — and a row may only carry a currency if its quantity is money. In our table 11 of the 13 rows with both sides filled carry a "how many times dearer" figure, and two deliberately do not: the museum and park line, per person against per vehicle, and the outpatient visit line, whose two benchmarks sit on deliberately different stated bases. Both are true numbers, and the ratio would be a category error, so the cell stays empty.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. A source date within N days of the last verified date.&lt;/strong&gt; Pick N, write it into the schema, and treat anything older as pending rather than as current. Our file reads every source on one day, 2026-11-05, so the table has one expiry date, and a buyer who re-checks on their own schedule gets a table with a visible shelf life instead of one that rots quietly.&lt;/p&gt;

&lt;p&gt;None of these are implementation — filters in Notion, conditional formatting in a spreadsheet. The point is not the tool but that the rule is written down somewhere the data has to pass.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we sell, and how it fits
&lt;/h2&gt;

&lt;p&gt;The table is the product, and it is deliberately not a frozen snapshot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;China Cost Guide — $14, price listed on aukawave8.gumroad.com, Nov 2026.&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://aukawave8.gumroad.com/l/china-cost-guide" rel="noopener noreferrer"&gt;https://aukawave8.gumroad.com/l/china-cost-guide&lt;/a&gt;&lt;br&gt;
62 cost lines across 13 categories, 57 sources in a separate index, every line carrying its unit, its source and the date it was read.&lt;/p&gt;

&lt;p&gt;Every Notion template we publish is built the same way: a written spec of every page, database and property, a paste-ready import block, and one CSV per database. Paste the block into a new Notion page, import the CSVs, and the structure is there in about three minutes — no paid plan, nothing hosted on our account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notion Budget Tracker Template — $16.&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://aukawave8.gumroad.com/l/budget-tracker" rel="noopener noreferrer"&gt;https://aukawave8.gumroad.com/l/budget-tracker&lt;/a&gt;&lt;br&gt;
Planned spend and actual spend in separate columns, categories with limits attached — the same separation of fields, applied to a month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notion Research Wiki Template — $18.&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://aukawave8.gumroad.com/l/research-wiki" rel="noopener noreferrer"&gt;https://aukawave8.gumroad.com/l/research-wiki&lt;/a&gt;&lt;br&gt;
Sources, atomic notes and claims with citations attached, so a claim keeps its source — the same rule, applied to writing.&lt;/p&gt;

&lt;p&gt;What we do not claim: the template cannot find a number for you, and it cannot decide whether two figures are comparable — only make those decisions visible.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you take one thing away
&lt;/h2&gt;

&lt;p&gt;A table is a schema with a reading order. If the schema has no way to say "not comparable" and "not checked yet", the table will fill both with numbers and you will never tell which is which. Give the row a unit, a currency, a source, a date and a status. Let a cell stay empty, and refuse to compute across two rows that are not measuring the same thing.&lt;/p&gt;

&lt;p&gt;The empty cell is not the failure. The filled one with nothing behind it is.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This piece was written with AI assistance.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>data</category>
      <category>datamodel</category>
      <category>notion</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Treat your prompts like dependencies: owner, version, test case, date</title>
      <dc:creator>Auka Wave</dc:creator>
      <pubDate>Sat, 07 Nov 2026 17:11:52 +0000</pubDate>
      <link>https://dev.to/aukawave8/treat-your-prompts-like-dependencies-owner-version-test-case-date-587d</link>
      <guid>https://dev.to/aukawave8/treat-your-prompts-like-dependencies-owner-version-test-case-date-587d</guid>
      <description>&lt;p&gt;&lt;em&gt;Most prompts in a repo were pasted out of a chat window and never versioned. Here is what a lockfile's discipline gives them — and three prompts in full.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: a prompt with no provenance
&lt;/h2&gt;

&lt;p&gt;Here is a scene you have probably watched. Someone scrolls up a chat window, copies a paragraph that told a model to do the thing, pastes it into a terminal, and it works. Nobody writes down what the prompt was; it survives as muscle memory and a scrollback buffer.&lt;/p&gt;

&lt;p&gt;Months later somebody pastes it again. The model changed. The library changed. The author has moved teams. The output is still plausible, so nobody reports that it is wrong — it quietly returns something else, and the difference is absorbed into the code.&lt;/p&gt;

&lt;p&gt;That is what makes prompts different from other artifacts in a repo. A broken script throws. A stale dependency changes behaviour and says nothing about what changed. A prompt with no owner, no version, no date and no test rots silently. The fix is not a more clever prompt but a more constrained one: a name, a version, a stored input, a pass condition.&lt;/p&gt;

&lt;p&gt;A prompt that produces part of your work is a dependency in the same sense as a library: it has an upstream (the model, which changes without notice and without a changelog you control), callers and a contract. Without a version you cannot tell whether a bad result came from a changed model, a changed prompt, or your own code. They rot quietly; the cure is bookkeeping, not cleverness.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "treat it like code" means concretely
&lt;/h2&gt;

&lt;p&gt;Four fields. That is the whole system.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;What it prevents&lt;/th&gt;
&lt;th&gt;How you check it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Owner&lt;/td&gt;
&lt;td&gt;The prompt outliving everyone who knows why&lt;/td&gt;
&lt;td&gt;A name in the file; nobody to answer means it is dead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Version&lt;/td&gt;
&lt;td&gt;Silent behaviour change on edit&lt;/td&gt;
&lt;td&gt;A number that changes on every edit, with that edit's date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test case&lt;/td&gt;
&lt;td&gt;A prompt that still runs but no longer does the job&lt;/td&gt;
&lt;td&gt;One stored input and a pass condition you can read&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Last verified&lt;/td&gt;
&lt;td&gt;A prompt that was fine against a model that no longer exists&lt;/td&gt;
&lt;td&gt;The date you last ran the stored input&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;One file per prompt, in a &lt;code&gt;prompts/&lt;/code&gt; directory: the body is the prompt text, the test file next to it holds one input and the condition the output must satisfy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;triage-failing-test&lt;/span&gt;
&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;team or person&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;last_verified&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;YYYY-MM-DD&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;models&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;&amp;lt;model ids you run&amp;gt;&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;test_case&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prompts/tests/triage-failing-test.md&lt;/span&gt;
&lt;span class="na"&gt;not_covered&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reads&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;only&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;the&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;CI&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;you&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;paste;&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;runs&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;nothing"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The loop is boring, which is the point. Paste the stored input, check the output against the pass condition. If it fails, decide: the prompt drifted, so fix it and bump the version, or the model drifted, so adapt it to the model you run — bump the version either way.&lt;/p&gt;

&lt;p&gt;The "not covered" line is not a disclaimer but a routing rule: it tells the next reader which file to open.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three prompts, written out in full
&lt;/h2&gt;

&lt;p&gt;A note before you copy these: the three prompts below were written with AI assistance. They are original — none is reproduced from our paid packs — and each ships with a test case, because the argument above only works if the prompts obey it.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Triage a failing CI test
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;For:&lt;/strong&gt; getting from a red build to one next experiment, without letting the model jump to a fix it cannot justify.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fill in:&lt;/strong&gt; the exact failing command, its complete output (not a screenshot), what changed recently, the runtime versions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are triaging one failing CI job. You cannot run code.

INPUT
- Command: &amp;lt;exact command that failed&amp;gt;
- Output: &amp;lt;complete stdout and stderr, unedited&amp;gt;
- Recent change: &amp;lt;what changed, or "none"&amp;gt;
- Runtime: &amp;lt;language, version&amp;gt;

OUTPUT — these four sections, in this order, nothing else:

1. HYPOTHESIS — one sentence: the most likely cause.

2. EVIDENCE — up to three bullets. Each quotes a line of the output above and says what it rules
   in or out. No bullet may cite a line that is not in it.

3. NEXT PROBE — one non-destructive command, runnable from the repository root (no deletes, no
   force pushes, no database writes, no cache clearing), that separates your hypothesis from the
   next most likely cause. Then: which result means which.

4. CONFIDENCE — high, medium or low, then the single fact that would change it.

RULES
- If the output is truncated, write "INSUFFICIENT OUTPUT" and ask for the rest.
- Do not propose a fix; a fix comes after the probe.
- Do not invent file paths, environment variables, stack frames or versions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A bad output:&lt;/strong&gt; a hypothesis with no quoted evidence, a "fix" that rewrites the function under test, a probe that clears a cache, or "high" confidence with nothing behind it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test case:&lt;/strong&gt; a failing test whose cause is a changed default value. The probe must separate that from other causes; if it is "add a print statement", the prompt was not obeyed.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Audit an API change for breaking callers
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;For:&lt;/strong&gt; deciding whether a change to a published interface is safe, and who has to be told before it ships.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fill in:&lt;/strong&gt; language and runtime version, the change (a diff, or old and new signature), who consumes the interface, whether it shipped.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are auditing one change to a published API for breaking changes.

INPUT
- Runtime: &amp;lt;language and version&amp;gt;
- The change: &amp;lt;diff, or old -&amp;gt; new&amp;gt;
- Consumers: &amp;lt;teams, packages, or "unknown"&amp;gt;
- Shipped: yes / no

OUTPUT — a table with these columns:

| Change | Breaking | Affected callers | Migration |

- "Change": what changed, as name and old -&amp;gt; new.
- "Breaking": yes, no, or unknown. Use "unknown" if the input shows too little.
- "Affected callers": name only callers you can prove from the input; otherwise write "not
  determinable" — do not guess.
- "Migration": the smallest change that keeps a caller working: the call site before -&amp;gt; after.

THEN
- ASSUMPTIONS: every assumption you made.
- QUESTIONS FOR THE OWNER: questions whose answers would change the table, or "none".
- FIRST CHANGE: the change to make first to reduce breakage, and why.

RULES
- Judge the change as specified, not as it might be later.
- If the calling code is not in the input, say so in the table instead of assuming call sites.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A bad output:&lt;/strong&gt; every row marked "breaking: no", callers invented from the shape of the API, a migration that quietly changes the contract, or a "Migration" cell that is really a rewrite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test case:&lt;/strong&gt; a change that keeps the type but alters a parameter's default value. The answer must be "breaking: yes" or "unknown" — "no" means the prompt was not obeyed.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Turn a paragraph into a testable contract
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;For:&lt;/strong&gt; converting a fuzzy feature description into something a reviewer can check line by line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fill in:&lt;/strong&gt; the description in one paragraph, the shape it must fit (a type, a schema, an API payload), the non-negotiable constraints.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are converting a feature description into a testable contract.

INPUT
- Description: &amp;lt;one paragraph&amp;gt;
- It must fit: &amp;lt;type, schema or payload shape&amp;gt;
- Not negotiable: &amp;lt;constraints, or "none"&amp;gt;

OUTPUT

1. AMBIGUOUS WORDS
   Every word two engineers could read differently ("fast", "recent", "valid", "default"). For
   each: the two readings and the question that resolves it. If none, write "none".

2. CONTRACT
   One code block: the type or schema, every field required unless the description gives a
   default. No extra properties unless it allows them.

3. ACCEPTANCE CRITERIA
   Five numbered criteria, each checkable by a test rather than by opinion, each naming an
   observable: a value, a status code, an error, a log line. None may say "should" or "properly".

4. THE TWO CASES MOST LIKELY MISSED
   Two edge cases, each an input and its expected result: one a boundary (empty, zero, a maximum,
   the limit), one a conflict between two rules.

RULES
- If the description does not say, do not decide it silently: record the gap in section 1, take
   the stricter reading in section 2, list it under assumptions.
- Do not add features or fields the description does not ask for.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;A bad output:&lt;/strong&gt; a schema where every field is optional, criteria that say the endpoint "responds quickly", or edge cases that restate the happy path.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test case:&lt;/strong&gt; a description containing the word "fast". If the ambiguous-words section comes back empty and the contract has no measurable bound, the prompt was not obeyed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we sell, and what we do not claim
&lt;/h2&gt;

&lt;p&gt;We sell prompt packs, built the same way as the three above: every prompt ships with a stated input, a stated output shape, and a note on what it does not cover. Nothing in a pack is a magic phrase, and no pack claims your output will match a sample.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI Prompts for Developers — $29.&lt;/strong&gt; Twenty copy-paste templates for code review, schemas and API design.&lt;br&gt;
&lt;a href="https://aukawave8.gumroad.com/l/developer-productivity-pack" rel="noopener noreferrer"&gt;https://aukawave8.gumroad.com/l/developer-productivity-pack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notion Code Snippets Template — $15.&lt;/strong&gt; A cheatsheet, a library, and a place for snippets you will reuse.&lt;br&gt;
&lt;a href="https://aukawave8.gumroad.com/l/code-snippets" rel="noopener noreferrer"&gt;https://aukawave8.gumroad.com/l/code-snippets&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Notion Research Wiki Template — $18.&lt;/strong&gt; Notes, sources and citations, so a claim keeps its source attached.&lt;br&gt;
&lt;a href="https://aukawave8.gumroad.com/l/research-wiki" rel="noopener noreferrer"&gt;https://aukawave8.gumroad.com/l/research-wiki&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI Prompts for Freelancers — $29.&lt;/strong&gt; Proposals and client work, for technical freelancers.&lt;br&gt;
&lt;a href="https://aukawave8.gumroad.com/l/freelancer-pro" rel="noopener noreferrer"&gt;https://aukawave8.gumroad.com/l/freelancer-pro&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What we do not claim: a prompt cannot know your system, cannot make the judgement call about your data, and cannot be verified by whoever did not write the test case. Every pack states its coverage, so you can tell when a prompt is out of its depth.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you take one thing away
&lt;/h2&gt;

&lt;p&gt;A prompt is a dependency, and an unversioned one that fails silently is the worst kind there is. Give it four fields — owner, version, test case, date — and the failure stops being silent. The date is the one everyone skips: a prompt verified four months ago against a changed model is unverified, whatever its filename.&lt;/p&gt;

&lt;p&gt;Take one prompt you already use. Write down its owner, its version, and one input you can paste into it. Run it. If you cannot say what a passing answer looks like, you do not have a prompt yet — you have a habit.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Note on authorship: the three prompts in this post were written with AI assistance. They are original, and none is taken from our paid packs. We have not attached sample outputs, because a prompt's output is model-dependent — each ships with the pass condition you should check against your model.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>promptengineering</category>
      <category>codereview</category>
      <category>testing</category>
    </item>
    <item>
      <title>A price without a source is just a number with a decimal point</title>
      <dc:creator>Auka Wave</dc:creator>
      <pubDate>Sat, 07 Nov 2026 16:26:57 +0000</pubDate>
      <link>https://dev.to/aukawave8/a-price-without-a-source-is-just-a-number-with-a-decimal-point-4n2h</link>
      <guid>https://dev.to/aukawave8/a-price-without-a-source-is-just-a-number-with-a-decimal-point-4n2h</guid>
      <description>&lt;p&gt;&lt;em&gt;Four pinned prices from China and the US, one we refused to publish, and why the same discipline applies to the number on your own invoice.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: a number with no provenance
&lt;/h2&gt;

&lt;p&gt;If you quote a price — to a client, for a product, for a day of consulting — the number on its own is not a fact. It is a claim with a decimal point. The moment you cannot say where it came from and when it was true, you cannot defend it: not to a client, and not to yourself three months later, when you are wondering whether you underpriced and by how much.&lt;/p&gt;

&lt;p&gt;Engineers already have this discipline. Every dependency comes with a version and a lockfile; every figure in a dashboard traces back to a query you can rerun. A price without a source and a date is a dependency you never pinned.&lt;/p&gt;

&lt;p&gt;So we did a small, unglamorous thing. We took four prices that all bear on one question — what does an ordinary day cost in mainland China versus the United States — and pinned each one to a source and a date. Then we went looking for a fifth number, established that it does not exist in comparable form, and left it out.&lt;/p&gt;

&lt;p&gt;That is the whole method. It is also the product.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four numbers we could source
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The burger.&lt;/strong&gt; About $3.47 in mainland China against $5.69 in the United States. Big Mac Index, The Economist, reported December 2025.&lt;br&gt;
&lt;a href="https://wap.eastmoney.com/a/202512163593148978.html" rel="noopener noreferrer"&gt;https://wap.eastmoney.com/a/202512163593148978.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The doctor visit.&lt;/strong&gt; About 50 yuan for one outpatient visit in China, about $186 in the United States. Published outpatient cost benchmarks, 2026.&lt;br&gt;
&lt;a href="https://aukawave8.gumroad.com/l/china-cost-guide" rel="noopener noreferrer"&gt;https://aukawave8.gumroad.com/l/china-cost-guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The movie ticket.&lt;/strong&gt; US average $16.30 in 2026.&lt;br&gt;
&lt;a href="https://www.cabletv.com/entertainment/cost-of-a-movie-ticket" rel="noopener noreferrer"&gt;https://www.cabletv.com/entertainment/cost-of-a-movie-ticket&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The rent.&lt;/strong&gt; New York one-bedroom fair market rent, $2,655 per month. HUD FY2026 Fair Market Rent schedule.&lt;br&gt;
&lt;a href="https://www.huduser.gov/portal/datasets/fmr/fmr2026/FY2026_FMR_Schedule.pdf" rel="noopener noreferrer"&gt;https://www.huduser.gov/portal/datasets/fmr/fmr2026/FY2026_FMR_Schedule.pdf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two of those four carry a trap in plain sight, and in both cases the trap is the unit.&lt;/p&gt;

&lt;p&gt;The rent figure is a Fair Market Rent: it includes a utility allowance, and it is not an advertised rent. The movie ticket is a national average, not the price of the film you actually want to see on a Friday night. Averages are honest numbers; they simply answer a different question than the one you asked.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a price gauge can and cannot tell you
&lt;/h2&gt;

&lt;p&gt;The burger is the most quoted and the most misused of the four. What it can do is give you an implied exchange rate: roughly 4 CNY per USD on the same index, reported December 2025. You can run rough conversions in your head from that, without looking anything up, which is genuinely useful if you invoice in dollars and think in local costs.&lt;/p&gt;

&lt;p&gt;What it cannot do is describe a life. Here is the honest caveat: a Big Mac is a manufactured good. It can be shipped, so its price answers to input costs and to trade. Rent is a local service. You cannot import a cheaper apartment from another country, so local wages, local supply and local rules set that price. Different markets, different mechanisms — and a ratio between two prices produced by different mechanisms tells you less than the two prices do on their own.&lt;/p&gt;

&lt;p&gt;The doctor visit is the same story with a sharper edge. A clinical appointment cannot be arbitraged across a border. The clinician, the building, the staff and the rules are all local, and the price is set where the service is delivered. So the gap between 50 yuan and $186 is a fact about two local systems, not a verdict about which system is better. Anyone quoting the ratio as a verdict is answering a question nobody asked.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number we would not publish
&lt;/h2&gt;

&lt;p&gt;The obvious fifth number is the Chinese one-bedroom rent, to sit next to New York's $2,655.&lt;/p&gt;

&lt;p&gt;No comparable published figure exists, so we did not publish one.&lt;/p&gt;

&lt;p&gt;The reason is a unit mismatch, and it is worth understanding rather than papering over. China's published rent data are indices — movements over time — not absolute monthly levels. The commercial indices that do carry levels are quoted in yuan per square metre per month, which is not the same unit as HUD's Fair Market Rent. Multiplying a per-square-metre figure by an assumed floor area would have produced a confident-looking number with nothing underneath it. We would rather publish four numbers that hold than five that include a guess dressed up as data.&lt;/p&gt;

&lt;p&gt;The same care applies to the medical pair. We have not verified whether the two published benchmarks cover an identical bundle of services — tests, imaging, follow-up. So read them as the price of one visit, not the price of a course of care.&lt;/p&gt;

&lt;p&gt;Both gaps are in the published table, marked as gaps rather than filled in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we published it at all
&lt;/h2&gt;

&lt;p&gt;Two reasons, and the second one is the commercial one.&lt;/p&gt;

&lt;p&gt;The first is that a table like this gets reused, cited and argued with. It is worth more with its sources attached than without them. The second is that we are a small studio that sells working files, and this was a working file: we built the sourced price table as a Notion template, one row per price, each row carrying its date, its source link, and a note on what the figure does and does not measure.&lt;/p&gt;

&lt;p&gt;If you are comparing costs across the two markets, or you are pricing your own work and want the underlying numbers on your desk instead of in a tab you cannot cite, it is here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;China Cost Guide&lt;/strong&gt; — $14, price listed on aukawave8.gumroad.com, Nov 2026.&lt;br&gt;
&lt;a href="https://aukawave8.gumroad.com/l/china-cost-guide" rel="noopener noreferrer"&gt;https://aukawave8.gumroad.com/l/china-cost-guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same principle runs through the rest of the shop. We publish copy-paste prompt packs for the other half of the work — code review, schemas and API design in the Developer Productivity Pack ($29, listed Nov 2026: &lt;a href="https://aukawave8.gumroad.com/l/developer-productivity-pack" rel="noopener noreferrer"&gt;https://aukawave8.gumroad.com/l/developer-productivity-pack&lt;/a&gt;), proposals and client work in Freelancer Pro ($29, listed Nov 2026: &lt;a href="https://aukawave8.gumroad.com/l/freelancer-pro" rel="noopener noreferrer"&gt;https://aukawave8.gumroad.com/l/freelancer-pro&lt;/a&gt;). The file handles the mechanical part; you handle the part that is yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you take one thing away
&lt;/h2&gt;

&lt;p&gt;A price is a claim. A price with a date, a source and a stated unit is evidence. And a number you cannot source is not a hole in your research — it is information about the market, and it belongs in the write-up as a gap rather than as an estimate.&lt;/p&gt;

&lt;p&gt;Pin your numbers. Say where they came from. And when a number does not exist, say that it does not exist, and leave the gap visible in the table where your readers can check it.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>webdev</category>
      <category>freelance</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
