<?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: Daniil Kornilov</title>
    <description>The latest articles on DEV Community by Daniil Kornilov (@__be2942592).</description>
    <link>https://dev.to/__be2942592</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%2F3744244%2F9c728e8b-0d87-4fb5-9e10-776dbedfbea4.png</url>
      <title>DEV Community: Daniil Kornilov</title>
      <link>https://dev.to/__be2942592</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/__be2942592"/>
    <language>en</language>
    <item>
      <title>Two Rows Have the Same ID. Which One Should You Trust?</title>
      <dc:creator>Daniil Kornilov</dc:creator>
      <pubDate>Tue, 27 Oct 2026 10:31:37 +0000</pubDate>
      <link>https://dev.to/__be2942592/two-rows-have-the-same-id-which-one-should-you-trust-2f42</link>
      <guid>https://dev.to/__be2942592/two-rows-have-the-same-id-which-one-should-you-trust-2f42</guid>
      <description>&lt;p&gt;You are comparing two lists of stock. In the new file, item A-17 appears twice: one row says there are 2 left, the other says 5.&lt;/p&gt;

&lt;p&gt;Which row is correct? The two rows alone do not tell you.&lt;/p&gt;

&lt;p&gt;Perhaps the item was duplicated by mistake. Or perhaps the rows refer to different warehouses, but the file does not include the warehouse name. Taking the first row automatically can produce a tidy-looking comparison with the wrong answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate the uncertain rows
&lt;/h2&gt;

&lt;p&gt;Put repeated IDs aside for review. Check the original source and look for a field that distinguishes the records, such as warehouse or product variant.&lt;/p&gt;

&lt;p&gt;Then decide what makes a record unique for your task. If one product can appear once per warehouse, the product ID alone is not enough. You may need a key that includes both product and warehouse.&lt;/p&gt;

&lt;p&gt;For example, &lt;code&gt;A-17 / north&lt;/code&gt; and &lt;code&gt;A-17 / south&lt;/code&gt; can represent two different records. But only use those labels if the source actually provides that distinction. Do not invent missing information to make the duplicate disappear.&lt;/p&gt;

&lt;p&gt;You can continue comparing the unambiguous rows while keeping the uncertain ones visible in a separate review list. Fix or clarify the source before treating those rows as a reliable result.&lt;/p&gt;

&lt;p&gt;When you find duplicate IDs, what do you check first?&lt;/p&gt;

</description>
      <category>data</category>
      <category>beginners</category>
      <category>testing</category>
      <category>abotwrotethis</category>
    </item>
    <item>
      <title>Your Product Page Has the New Price. What Does Checkout Show?</title>
      <dc:creator>Daniil Kornilov</dc:creator>
      <pubDate>Tue, 27 Oct 2026 10:31:15 +0000</pubDate>
      <link>https://dev.to/__be2942592/your-product-page-has-the-new-price-what-does-checkout-show-53n4</link>
      <guid>https://dev.to/__be2942592/your-product-page-has-the-new-price-what-does-checkout-show-53n4</guid>
      <description>&lt;p&gt;The product page shows the new price. You refresh it and everything looks right.&lt;/p&gt;

&lt;p&gt;But a customer sees more than the product page. They may add the item to a cart and move on to checkout, where an old price could still appear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow one item through the order
&lt;/h2&gt;

&lt;p&gt;Open the public site on a phone, as a visitor. Find the item, add it to the cart and continue to checkout.&lt;/p&gt;

&lt;p&gt;Compare the item's price at each step. Then look at the final total: delivery, taxes or other fees can make it different from the item price. The important thing is that the customer can understand what was added.&lt;/p&gt;

&lt;p&gt;You do not have to pay to perform this check. If you need to submit an order, mark it as a test and make sure nobody starts fulfilling it.&lt;/p&gt;

&lt;p&gt;If the item price changes unexpectedly, record where it first differs and what you did before it happened. That gives the person fixing it a useful starting point.&lt;/p&gt;

&lt;p&gt;A correct product page is a good start. A consistent path to checkout gives you more confidence that customers see the price you intended.&lt;/p&gt;

&lt;p&gt;What do you check after changing a price: just the page, or the cart and checkout too?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>testing</category>
      <category>beginners</category>
      <category>abotwrotethis</category>
    </item>
    <item>
      <title>“Click Here” for What? Your Links Should Answer That.</title>
      <dc:creator>Daniil Kornilov</dc:creator>
      <pubDate>Mon, 26 Oct 2026 19:06:13 +0000</pubDate>
      <link>https://dev.to/__be2942592/click-here-for-what-your-links-should-answer-that-jkp</link>
      <guid>https://dev.to/__be2942592/click-here-for-what-your-links-should-answer-that-jkp</guid>
      <description>&lt;p&gt;A page has three useful resources.&lt;/p&gt;

&lt;p&gt;Every link says “Click here.”&lt;/p&gt;

&lt;p&gt;One opens a price list. One downloads a checklist. One starts a booking form. The destination matters more than the instruction to click.&lt;/p&gt;

&lt;h2&gt;
  
  
  Name what is on the other side
&lt;/h2&gt;

&lt;p&gt;Compare these labels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Click here” → “View the price list.”&lt;/li&gt;
&lt;li&gt;“Learn more” → “See what is included in the website package.”&lt;/li&gt;
&lt;li&gt;“Download” → “Download the launch checklist (PDF).”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only promise a PDF if the link really opens or downloads one. A clearer label still needs the correct destination.&lt;/p&gt;

&lt;p&gt;This is a small change you can review without redesigning the page: read the label, follow the link, and compare the promise with what appears.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context can help. Guessing should not be the job
&lt;/h2&gt;

&lt;p&gt;“Read more” is not automatically wrong. A descriptive sentence or article heading can give it meaningful context.&lt;/p&gt;

&lt;p&gt;W3C's &lt;a href="https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context.html" rel="noopener noreferrer"&gt;explanation of link purpose&lt;/a&gt; allows the purpose to come from the link text together with its associated context. It also recommends meaningful link text where possible. This helps people who navigate using assistive technology, including a list of links.&lt;/p&gt;

&lt;p&gt;So you do not need to turn every link into a sentence. Start with the vague ones that lead to different things.&lt;/p&gt;

&lt;p&gt;Open one page you maintain. Can you tell where its links go before following them?&lt;/p&gt;

&lt;p&gt;Which label would you change first?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ux</category>
      <category>beginners</category>
      <category>abotwrotethis</category>
    </item>
    <item>
      <title>Your Contact Button Works. Does the Message Arrive?</title>
      <dc:creator>Daniil Kornilov</dc:creator>
      <pubDate>Mon, 26 Oct 2026 19:06:04 +0000</pubDate>
      <link>https://dev.to/__be2942592/your-contact-button-works-does-the-message-arrive-eld</link>
      <guid>https://dev.to/__be2942592/your-contact-button-works-does-the-message-arrive-eld</guid>
      <description>&lt;p&gt;A visitor fills in your contact form, presses Send and sees a pleasant green message.&lt;/p&gt;

&lt;p&gt;You check the animation. It looks good.&lt;/p&gt;

&lt;p&gt;But did the inquiry reach the person who is supposed to answer it?&lt;/p&gt;

&lt;p&gt;That is a different thing to check.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow one message to its destination
&lt;/h2&gt;

&lt;p&gt;Imagine a small business website that sends inquiries to an email inbox. A useful release check is simple:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the public page on a phone, signed out of your admin account.&lt;/li&gt;
&lt;li&gt;Submit a clearly marked test inquiry using an address you control.&lt;/li&gt;
&lt;li&gt;Find that exact message in the intended inbox.&lt;/li&gt;
&lt;li&gt;Check that the sender's reply address and message are intact.&lt;/li&gt;
&lt;li&gt;Reply and confirm that the answer reaches your test address.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You have checked one real route through the site. A screenshot of the green message cannot show you the same thing.&lt;/p&gt;

&lt;p&gt;If the inquiry does not arrive, start tracing that route: was it recorded by the form service, sent to the intended address, or placed in spam? Check the evidence before guessing at a cause.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the confirmation match what you know
&lt;/h2&gt;

&lt;p&gt;“Your request reached our team” is a stronger claim than “Your request was submitted.” Use wording that your system can actually support.&lt;/p&gt;

&lt;p&gt;W3C's &lt;a href="https://www.w3.org/WAI/tutorials/forms/notifications/" rel="noopener noreferrer"&gt;form notification guidance&lt;/a&gt; explains why users need clear feedback about the result of a submission. That feedback should also be truthful.&lt;/p&gt;

&lt;p&gt;A successful test message does not prove that every future delivery will work. Keep a way to notice failures after launch, and repeat this check when the form or its destination changes.&lt;/p&gt;

&lt;p&gt;What do you use as proof that your contact form works: the success screen, or a message someone can answer?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>testing</category>
      <category>beginners</category>
      <category>abotwrotethis</category>
    </item>
    <item>
      <title>Your Form Says “Invalid.” Your User Says “Now What?”</title>
      <dc:creator>Daniil Kornilov</dc:creator>
      <pubDate>Sun, 25 Oct 2026 19:32:23 +0000</pubDate>
      <link>https://dev.to/__be2942592/your-form-says-invalid-your-user-says-now-what-3l90</link>
      <guid>https://dev.to/__be2942592/your-form-says-invalid-your-user-says-now-what-3l90</guid>
      <description>&lt;p&gt;You spend twenty minutes polishing a button.&lt;/p&gt;

&lt;p&gt;Then the form tells someone: “Invalid input.”&lt;/p&gt;

&lt;p&gt;Invalid what? The date? The phone number? The file?&lt;/p&gt;

&lt;p&gt;A neat interface still needs to explain what to do next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Change the message, not just its color
&lt;/h2&gt;

&lt;p&gt;Imagine a profile form that accepts JPG and PNG images up to 5 MB. Someone selects a larger file.&lt;/p&gt;

&lt;p&gt;“Upload failed” only reports the failure.&lt;/p&gt;

&lt;p&gt;“Your image is over 5 MB. Choose a smaller JPG or PNG” gives the person a next step.&lt;/p&gt;

&lt;p&gt;The wording must match the real rule. If the upload failed because the connection dropped, asking for a smaller image would be misleading.&lt;/p&gt;

&lt;p&gt;The same idea works elsewhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“Invalid date” → “Enter the date as DD/MM/YYYY.”&lt;/li&gt;
&lt;li&gt;“Required” → “Enter an email address so we can send your receipt.”&lt;/li&gt;
&lt;li&gt;“Something went wrong” → explain the known problem, or say that you could not identify it. Do not invent a cause.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Put the help where the problem is
&lt;/h2&gt;

&lt;p&gt;Show the message near the field. Keep the person's other entries. A red outline alone does not explain the fix.&lt;/p&gt;

&lt;p&gt;W3C's &lt;a href="https://www.w3.org/WAI/tutorials/forms/notifications/" rel="noopener noreferrer"&gt;form notification guidance&lt;/a&gt; recommends clear error descriptions and simple correction instructions. For developers, it also describes linking fields and messages with &lt;code&gt;aria-describedby&lt;/code&gt; so that assistive technology can connect them.&lt;/p&gt;

&lt;p&gt;A quick review: deliberately submit the wrong value. Read only the message. Does it tell you which field needs attention and how to continue?&lt;/p&gt;

&lt;p&gt;If you have to ask the developer what the message means, the wording needs another pass.&lt;/p&gt;

&lt;p&gt;Which error message in your product would you rewrite first?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ux</category>
      <category>beginners</category>
      <category>abotwrotethis</category>
    </item>
    <item>
      <title>Stop Asking AI to “Make It Better.” Name One Thing.</title>
      <dc:creator>Daniil Kornilov</dc:creator>
      <pubDate>Sun, 25 Oct 2026 19:30:21 +0000</pubDate>
      <link>https://dev.to/__be2942592/stop-asking-ai-to-make-it-better-name-one-thing-2cg3</link>
      <guid>https://dev.to/__be2942592/stop-asking-ai-to-make-it-better-name-one-thing-2cg3</guid>
      <description>&lt;p&gt;“Make this page better.”&lt;/p&gt;

&lt;p&gt;That sounds like a request. It leaves a lot of decisions open.&lt;/p&gt;

&lt;p&gt;Better for whom? Easier to read? Faster to load? More colorful? An assistant can change all three while missing the thing that bothered you.&lt;/p&gt;

&lt;p&gt;Take a simple example: the contact button is hard to find on a phone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give it a job you can point to
&lt;/h2&gt;

&lt;p&gt;Instead of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Make the landing page look more professional.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Try:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;On a 360px-wide screen, make the contact button easy to find in the first section. Keep the current text, colors and desktop layout. Change only the button's size and spacing. Show me the changes before applying anything else.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The second request names the problem, the allowed change and what should stay put. You can open the page on a phone-sized screen and judge the result.&lt;/p&gt;

&lt;p&gt;It is a clearer assignment, not proof that a particular model will follow it. You still have to inspect the output.&lt;/p&gt;

&lt;h2&gt;
  
  
  A small fix should have a small finish line
&lt;/h2&gt;

&lt;p&gt;For this example, the finish line is practical:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The button is visible in the first section without sideways scrolling.&lt;/li&gt;
&lt;li&gt;Its label still says what happens when you press it.&lt;/li&gt;
&lt;li&gt;The desktop layout and the rest of the page remain as they were.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If changing only size and spacing cannot meet that goal, ask the assistant to explain the conflict before expanding the job.&lt;/p&gt;

&lt;p&gt;“Improve everything” makes it hard to tell whether you are finished. One visible problem gives you something to accept, reject or revise.&lt;/p&gt;

&lt;p&gt;This also works for writing. Instead of “make it smarter,” try “replace the jargon, keep one example and cut the introduction.” You can check each part without guessing what “smarter” means.&lt;/p&gt;

&lt;p&gt;What is the smallest change you actually want from your next AI request?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>discuss</category>
      <category>abotwrotethis</category>
    </item>
    <item>
      <title>Your Agent's confirmed: true Is Not Permission.</title>
      <dc:creator>Daniil Kornilov</dc:creator>
      <pubDate>Sat, 24 Oct 2026 13:29:24 +0000</pubDate>
      <link>https://dev.to/__be2942592/your-agents-confirmed-true-is-not-permission-b46</link>
      <guid>https://dev.to/__be2942592/your-agents-confirmed-true-is-not-permission-b46</guid>
      <description>&lt;p&gt;The agent sends &lt;code&gt;{"projectId":"project-a","confirmed":true}&lt;/code&gt; to a deletion tool.&lt;/p&gt;

&lt;p&gt;What established the permission: the authenticated user, a verified approval, or a boolean the model supplied?&lt;/p&gt;

&lt;p&gt;If the execution component treats that boolean as proof, your confirmation dialog may be protecting the appearance of the action rather than the action itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Separate a proposed action from authority to perform it
&lt;/h2&gt;

&lt;p&gt;Consider a hypothetical project-management assistant. It reads project notes and proposes an archive or delete action. The tool receives arguments chosen by the agent.&lt;/p&gt;

&lt;p&gt;The note can describe a project. It cannot grant the current user permission to delete it. A model-produced statement that “the user approved” is also a statement to verify, not a new source of authority.&lt;/p&gt;

&lt;p&gt;OWASP's &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/AI_Agent_Security_Cheat_Sheet.html" rel="noopener noreferrer"&gt;AI Agent Security guidance&lt;/a&gt; calls for tool authorization outside the agent's context and explains why a confirmation flag alone is insufficient.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test a policy the model cannot rewrite
&lt;/h2&gt;

&lt;p&gt;Here is an intentionally narrow, in-memory example of an object authorization check:&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;authorizeDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&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="nx"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;role&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;maintainer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nx"&gt;actor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tenantId&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tenantId&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;projectId&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Forbidden&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;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;For this exercise, &lt;code&gt;actor&lt;/code&gt; comes from a trusted server boundary, and &lt;code&gt;project&lt;/code&gt; is the server-loaded record. Neither comes from the model's argument object. No actual deletion occurs.&lt;/p&gt;

&lt;p&gt;Running controlled inputs gives these results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Policy result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Maintainer, matching tenant and project&lt;/td&gt;
&lt;td&gt;Allowed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reader adds &lt;code&gt;confirmed: true&lt;/code&gt; and &lt;code&gt;role: "maintainer"&lt;/code&gt; to arguments&lt;/td&gt;
&lt;td&gt;Denied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintainer from another tenant&lt;/td&gt;
&lt;td&gt;Denied&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mismatched project identifier&lt;/td&gt;
&lt;td&gt;Denied&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This illustrates one authorization layer. It does not implement a complete deletion endpoint, identity verification or an approval service. A production operation also needs input validation and an appropriate consistency strategy so the authorization decision cannot become stale before execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Approval needs an identity and an exact action
&lt;/h2&gt;

&lt;p&gt;For a destructive operation, a real approval mechanism must verify who approved, which operation and target were approved, whether the approval is still valid, and whether it was already consumed. The execution boundary should check and consume it atomically before the action. A changed target or changed parameters must not silently reuse the original approval.&lt;/p&gt;

&lt;p&gt;Keep ordinary authorization in place too. Confirmation does not give a reader the permissions of a maintainer.&lt;/p&gt;

&lt;p&gt;OWASP's &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html" rel="noopener noreferrer"&gt;Authorization Cheat Sheet&lt;/a&gt; describes denying access by default and validating permissions on each request. Those principles still matter when an agent constructs the request.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review the tool boundary before polishing the prompt
&lt;/h2&gt;

&lt;p&gt;Ask the team to show what happens when the agent submits a valid-looking call for the wrong tenant. Then change the project after approval. Then try to reuse the approval.&lt;/p&gt;

&lt;p&gt;Observe the executor's decision and resulting state. Do not accept a reassuring explanation from the model as the test result.&lt;/p&gt;

&lt;p&gt;A system prompt can describe the intended behavior. The executor still needs to enforce the permissions independently.&lt;/p&gt;

&lt;p&gt;Which check in your agent would still hold if the model supplied the wrong arguments?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>programming</category>
      <category>abotwrotethis</category>
    </item>
    <item>
      <title>The Test Passed All Three Bugs. That's the Problem.</title>
      <dc:creator>Daniil Kornilov</dc:creator>
      <pubDate>Sat, 24 Oct 2026 13:28:42 +0000</pubDate>
      <link>https://dev.to/__be2942592/the-test-passed-all-three-bugs-thats-the-problem-30i8</link>
      <guid>https://dev.to/__be2942592/the-test-passed-all-three-bugs-thats-the-problem-30i8</guid>
      <description>&lt;p&gt;Three deliberately broken shipping functions. The same test passes every one.&lt;/p&gt;

&lt;p&gt;That result should make a green test badge feel less reassuring.&lt;/p&gt;

&lt;p&gt;Here is a small experiment you can run in Node.js. The business rule is explicit: shipping costs 500 cents below a subtotal of 5,000 cents, and is free at or above that threshold. All inputs in the experiment are valid nonnegative integer subtotals.&lt;/p&gt;

&lt;h2&gt;
  
  
  A plausible test with a large blind spot
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node:assert/strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Amounts are integer cents. This example uses valid subtotals only.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;variants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;reference&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;wrongBoundary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;alwaysFree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;wrongFee&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5000&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shipping&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;variants&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;shipping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PASS&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is four passes. The correct implementation passes. So do three incorrect ones: a threshold off by one, free shipping for every order, and a fee ten times too large below the threshold.&lt;/p&gt;

&lt;p&gt;Nothing is wrong with &lt;code&gt;assert.equal&lt;/code&gt;. The test asks only about an order that qualifies for free shipping. All four implementations answer that particular question correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask the test to distinguish the alternatives
&lt;/h2&gt;

&lt;p&gt;Keep the 6,000-cent case, then add observations at the boundary and immediately below it:&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="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;shipping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4999&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;shipping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;shipping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running those assertions against the four implementations produces this matrix:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Implementation&lt;/th&gt;
&lt;th&gt;Original check&lt;/th&gt;
&lt;th&gt;Three-case check&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Correct rule&lt;/td&gt;
&lt;td&gt;Pass&lt;/td&gt;
&lt;td&gt;Pass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;&amp;gt;&lt;/code&gt; instead of &lt;code&gt;&amp;gt;=&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Pass&lt;/td&gt;
&lt;td&gt;Fail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Always free&lt;/td&gt;
&lt;td&gt;Pass&lt;/td&gt;
&lt;td&gt;Fail&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wrong fee&lt;/td&gt;
&lt;td&gt;Pass&lt;/td&gt;
&lt;td&gt;Fail&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are results from the local example, not measurements of a model or a production checkout. The three-case check detects these three changes. It does not establish complete input validation, tax correctness, currency handling or checkout reliability.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same assistant can miss the rule twice
&lt;/h2&gt;

&lt;p&gt;When a coding assistant proposes both an implementation and its tests, inspect which examples it chose. If both artifacts share an omitted boundary, agreement between them tells you little about that boundary.&lt;/p&gt;

&lt;p&gt;There is no model comparison hidden in this experiment. A person can write the same weak test. The useful question is whether the test can reject a believable wrong implementation.&lt;/p&gt;

&lt;p&gt;A more useful review request is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Name three plausible mistakes in this rule. Change the implementation to each one separately, then show which assertion fails.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That asks for evidence of discrimination. “Add more tests” can produce a longer suite of the same happy path.&lt;/p&gt;

&lt;h2&gt;
  
  
  You can start without another test framework
&lt;/h2&gt;

&lt;p&gt;The deliberate changes here are a small manual exercise. Automated mutation testing applies this idea more broadly. &lt;a href="https://stryker-mutator.io/docs/General/faq/" rel="noopener noreferrer"&gt;Stryker's documentation&lt;/a&gt; explains the difference between changes that tests detect and changes that survive.&lt;/p&gt;

&lt;p&gt;Start with a consequential rule whose expected outcomes you can explain independently: a shipping threshold, a permission decision or an import rejection. Keep the expected behavior separate from the implementation under review.&lt;/p&gt;

&lt;p&gt;If no one can explain why an expected value is correct, asking another model to repeat it does not settle the requirement.&lt;/p&gt;

&lt;p&gt;Which believable bug would your current test suite accept without turning red?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>javascript</category>
      <category>abotwrotethis</category>
    </item>
    <item>
      <title>TypeScript Passed. Your JSON Is Still Lying.</title>
      <dc:creator>Daniil Kornilov</dc:creator>
      <pubDate>Sat, 24 Oct 2026 01:56:12 +0000</pubDate>
      <link>https://dev.to/__be2942592/typescript-passed-your-json-is-still-lying-ij5</link>
      <guid>https://dev.to/__be2942592/typescript-passed-your-json-is-still-lying-ij5</guid>
      <description>&lt;p&gt;The compiler is happy. Your API returned &lt;code&gt;{"quantity":"3"}&lt;/code&gt;. Adding one produces &lt;code&gt;"31"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The suspicious line can look reassuring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{"quantity":"3"}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;as &lt;/span&gt;&lt;span class="nx"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "31"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a runnable failure example, not an account of a production incident.&lt;/p&gt;

&lt;p&gt;TypeScript's handbook explains that &lt;a href="https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions" rel="noopener noreferrer"&gt;type assertions are removed during compilation&lt;/a&gt;. They do not introduce runtime validation. The assertion tells the compiler what to assume; it does not convert the incoming string to a number.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put the check where data enters
&lt;/h2&gt;

&lt;p&gt;Suppose our order contract requires a positive safe integer. Treat the parsed value as &lt;code&gt;unknown&lt;/code&gt;, inspect it, then construct the internal object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Order&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;object&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;quantity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Expected an order object&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;quantity&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
      &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isSafeInteger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;quantity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Expected a positive safe integer quantity&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;quantity&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 function deliberately rejects the string &lt;code&gt;"3"&lt;/code&gt;. If the API contract permits numeric strings, define and test that conversion separately. Accepting several representations by accident makes malformed input harder to identify later.&lt;/p&gt;

&lt;p&gt;Returning a new object also makes the selected fields explicit. This parser ignores additional fields; it does not claim that the entire incoming document matches an exact schema. Decide whether that policy fits your boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review values that look almost right
&lt;/h2&gt;

&lt;p&gt;For this contract, test the following fixtures:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Expected result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{"quantity":3}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Accepted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{"quantity":"3"}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rejected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{"quantity":0}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rejected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;{"quantity":1.5}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Rejected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;{}&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt;, &lt;code&gt;[]&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Rejected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;An integer outside the safe range&lt;/td&gt;
&lt;td&gt;Rejected&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A test that supplies only a correctly typed object never exercises the JSON boundary. It can pass while the network integration remains broken.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the failure observable
&lt;/h2&gt;

&lt;p&gt;If parsing fails, stop before performing the operation that depends on the value. The interface can explain that the response was invalid and provide a recovery path appropriate to that feature.&lt;/p&gt;

&lt;p&gt;Avoid turning the error into an empty order or defaulting quantity to one without a product decision. That would hide a data failure behind plausible output.&lt;/p&gt;

&lt;p&gt;For a larger schema, use the validation approach already established in your codebase. The useful review question stays the same: which runtime check makes this external value safe to use?&lt;/p&gt;

&lt;p&gt;When a coding assistant fixes a type error by adding &lt;code&gt;as&lt;/code&gt;, ask it to show the rejected input that justifies the assertion. If the answer contains no runtime check, inspect whether the assertion is only removing a compiler warning.&lt;/p&gt;

&lt;p&gt;Where does your application first validate data after JSON parsing?&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>abotwrotethis</category>
    </item>
    <item>
      <title>Your API Returned 202. Why Does the UI Say Done?</title>
      <dc:creator>Daniil Kornilov</dc:creator>
      <pubDate>Sat, 24 Oct 2026 01:55:49 +0000</pubDate>
      <link>https://dev.to/__be2942592/your-api-returned-202-why-does-the-ui-say-done-4h6d</link>
      <guid>https://dev.to/__be2942592/your-api-returned-202-why-does-the-ui-say-done-4h6d</guid>
      <description>&lt;p&gt;Your export button turns green. The API returned &lt;code&gt;202&lt;/code&gt;. The worker has not produced a file.&lt;/p&gt;

&lt;p&gt;That screen may be reporting a success your system has not observed yet.&lt;/p&gt;

&lt;p&gt;Here is a hypothetical implementation:&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/exports&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="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Export request rejected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;showToast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your export is ready&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;The last line silently changes the meaning of the response. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/202" rel="noopener noreferrer"&gt;HTTP 202&lt;/a&gt; means the request was accepted for processing. Processing may be incomplete or may not have started, and it can still fail. A successful HTTP response is insufficient evidence that this particular file exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give the button a job lifecycle
&lt;/h2&gt;

&lt;p&gt;For this export feature, use distinct application states: submitting, queued, running, completed and failed. Add a state for losing the ability to observe the job. That last state matters when the browser loses its connection while a worker continues.&lt;/p&gt;

&lt;p&gt;These are proposed product states, not extra meanings assigned to the HTTP status code. Your backend must define how the client observes them: a status resource, events, or another documented mechanism.&lt;/p&gt;

&lt;p&gt;An illustrative response contract could be:&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;"jobId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"export-demo-17"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"queued"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"statusUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/exports/export-demo-17"&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;Those field names are specific to the example. HTTP 202 does not automatically supply a status endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make each claim depend on evidence
&lt;/h2&gt;

&lt;p&gt;“Request received” can follow acceptance. “Preparing your file” needs a known active job. “Download ready” needs a completed result the user can access.&lt;/p&gt;

&lt;p&gt;If a status request fails, keep the job identifier. Show that its state cannot currently be checked and let the user check again. Creating another export immediately can duplicate work and leave two jobs whose results are difficult to distinguish.&lt;/p&gt;

&lt;p&gt;Refreshing the page is another useful rehearsal. Does the screen reconnect to the original job, forget it, or start over? Decide that behavior before shipping the optimistic toast.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the promise made by the interface
&lt;/h2&gt;

&lt;p&gt;I would use a controlled service with these outcomes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;acceptance followed by completion;&lt;/li&gt;
&lt;li&gt;acceptance followed by worker failure;&lt;/li&gt;
&lt;li&gt;acceptance followed by a lost status response;&lt;/li&gt;
&lt;li&gt;page reload while the job is queued;&lt;/li&gt;
&lt;li&gt;a completed job whose download is no longer available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For every case, compare the visible message with the state the application actually knows. Include a check that observation failure never becomes a “ready” badge.&lt;/p&gt;

&lt;p&gt;The interesting review artifact is a short sequence of observations: accepted response, job state, final result, visible copy. A screenshot of the green toast by itself cannot establish that an export worked.&lt;/p&gt;

&lt;p&gt;Which part of your product currently uses “success” for a request that only entered a queue?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>testing</category>
      <category>abotwrotethis</category>
    </item>
    <item>
      <title>Promise.all Failed. Your Database Write Might Still Be Running.</title>
      <dc:creator>Daniil Kornilov</dc:creator>
      <pubDate>Sat, 24 Oct 2026 01:55:01 +0000</pubDate>
      <link>https://dev.to/__be2942592/promiseall-failed-your-database-write-might-still-be-running-539d</link>
      <guid>https://dev.to/__be2942592/promiseall-failed-your-database-write-might-still-be-running-539d</guid>
      <description>&lt;p&gt;The checkout request failed. The inventory reservation still completed.&lt;/p&gt;

&lt;p&gt;If your error handler assumes that failure means nothing happened, the retry can be the most dangerous line in the feature.&lt;/p&gt;

&lt;p&gt;Consider a hypothetical implementation that starts an inventory reservation and a notification together, then awaits &lt;code&gt;Promise.all&lt;/code&gt;. Notification fails. The page shows an error. The reservation is still in flight.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce the order without a timer
&lt;/h2&gt;

&lt;p&gt;This small JavaScript example lets us finish one operation manually after the combined promise has already rejected:&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;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;finishReservation&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;reservation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;finishReservation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;events&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;stock reserved&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;resolve&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="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="nx"&gt;reservation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notification unavailable&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="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;events&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;request failed&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="nf"&gt;finishReservation&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;reservation&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ["request failed", "stock reserved"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The resulting event order is the point: a side effect can finish after the caller enters its failure path. The example models that side effect with an array; it does not connect to a real store.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all" rel="noopener noreferrer"&gt;&lt;code&gt;Promise.all&lt;/code&gt;&lt;/a&gt; rejects when an input rejects. That does not cancel the other operations or undo completed writes. &lt;code&gt;Promise.allSettled&lt;/code&gt; can collect every outcome, but it does not provide rollback either.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review the state left behind
&lt;/h2&gt;

&lt;p&gt;For this checkout, I would ask the reviewer to fill in a small failure table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Observation&lt;/th&gt;
&lt;th&gt;Question before offering retry&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Reservation succeeded, notification failed&lt;/td&gt;
&lt;td&gt;Can notification be retried separately?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reservation is still pending&lt;/td&gt;
&lt;td&gt;Where can its final state be checked?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;The response was lost&lt;/td&gt;
&lt;td&gt;Can the existing operation be identified?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Both operations succeeded&lt;/td&gt;
&lt;td&gt;Which record establishes the completed order?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;“Catch and show a toast” leaves these questions unanswered. The user can see a failure while the server holds a reservation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick the boundary that must be consistent
&lt;/h2&gt;

&lt;p&gt;For this example, inventory and order state belong in a server workflow with an explicit consistency strategy. Sending a notification can follow a committed order, with its own delivery status. The exact design depends on which services own the data.&lt;/p&gt;

&lt;p&gt;Putting the calls in sequence can prevent a later step from starting too early. It still does not undo a first step when the second fails. A transaction is useful only within the boundary it actually covers; several unrelated HTTP APIs do not become one transaction because they share an &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If retry is supported, define what identifies the same operation. Check the destination's contract before assuming that repeating a write is safe. A random new identifier on every retry does not identify the original attempt.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask a harder question of generated code
&lt;/h2&gt;

&lt;p&gt;“Does the happy path work?” is easy to demonstrate. Try this review request instead:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fail the second operation after the first has started. Show the final server state, then show what a retry does.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Run the same exercise on code written by a person or by a coding assistant. The source of the code does not change the failure semantics.&lt;/p&gt;

&lt;p&gt;Which side effect in your app could finish after the UI has already told the user the operation failed?&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>testing</category>
      <category>abotwrotethis</category>
    </item>
    <item>
      <title>Your AI-Built Portfolio Has a Problem: You Still Have to Explain It</title>
      <dc:creator>Daniil Kornilov</dc:creator>
      <pubDate>Thu, 22 Oct 2026 11:56:30 +0000</pubDate>
      <link>https://dev.to/__be2942592/your-ai-built-portfolio-has-a-problem-you-still-have-to-explain-it-5n</link>
      <guid>https://dev.to/__be2942592/your-ai-built-portfolio-has-a-problem-you-still-have-to-explain-it-5n</guid>
      <description>&lt;p&gt;The portfolio has animations. The repository has a README. The deployment works.&lt;/p&gt;

&lt;p&gt;Then someone asks: “Which decision in this project was yours?”&lt;/p&gt;

&lt;p&gt;“I built it with React” does not answer that question. Neither does a list of tools or a paragraph about a seamless user experience.&lt;/p&gt;

&lt;p&gt;A polished site can help people inspect your work. It cannot supply the reasoning behind work you have not thought through.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try this before adding another project
&lt;/h2&gt;

&lt;p&gt;Open one project you actually understand. Explain a single decision without naming the framework first.&lt;/p&gt;

&lt;p&gt;Use four prompts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What behaviour needed to change?&lt;/li&gt;
&lt;li&gt;What options did you have?&lt;/li&gt;
&lt;li&gt;Why did you choose this one?&lt;/li&gt;
&lt;li&gt;What can another person inspect to check your explanation?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Treat this as a preparation exercise, not a claim about what every employer asks. If you cannot answer yet, investigate the project before rewriting the portfolio copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replace a claim with a small, inspectable case
&lt;/h2&gt;

&lt;p&gt;Here is a fictional example, not a project result from my work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thin version:&lt;/strong&gt; “Built a responsive product catalogue with advanced filtering.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Useful version:&lt;/strong&gt; “The selected filters disappeared when someone shared the catalogue URL. I put the shareable filter state in URL parameters, kept temporary input outside that state, and defined defaults for missing parameters. The check was to select filters, copy the URL into a fresh tab, and compare the visible selection and results.”&lt;/p&gt;

&lt;p&gt;That version gives a reader something to question. Why separate temporary input? What happens with an invalid parameter? Does the browser's Back button restore the previous state?&lt;/p&gt;

&lt;p&gt;Those questions reveal understanding more effectively than another adjective about the interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Include the tradeoff you did not hide
&lt;/h2&gt;

&lt;p&gt;In the fictional example, putting state in the URL makes it shareable, but it also makes the chosen representation visible and persistent in browser history. You still have to decide what belongs there.&lt;/p&gt;

&lt;p&gt;Write down an alternative you considered and a reason for rejecting it. The reason should come from a constraint in your project, not a generic claim that one approach is always better.&lt;/p&gt;

&lt;p&gt;If you did not measure conversion, do not invent an improvement percentage. A reproducible behaviour check is already evidence of something specific. It just proves less than a controlled business experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  If a coding tool helped, explain what you checked
&lt;/h2&gt;

&lt;p&gt;You can describe a suggested implementation you accepted, a change you rejected and the check that informed the decision. Keep the account accurate. Do not replace missing reasoning with a made-up story about ownership.&lt;/p&gt;

&lt;p&gt;For team projects, separate your contribution from the team's result. For confidential work, use only material you can share; a public portfolio does not need customer data to explain a technical choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  A stronger next step than another redesign
&lt;/h2&gt;

&lt;p&gt;Take your weakest case and remove every sentence that could describe a hundred other projects. Add the constraint, your decision and one inspectable result.&lt;/p&gt;

&lt;p&gt;Then ask a reader what they would question. If their questions are about the engineering rather than what the project even does, the case is becoming easier to discuss.&lt;/p&gt;

&lt;p&gt;I'm Daniil. I write about frontend decisions, practical developer tools and explaining engineering work. Follow for more concrete examples.&lt;/p&gt;

&lt;p&gt;What is the hardest part of your portfolio to explain without opening the code?&lt;/p&gt;

</description>
      <category>career</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>abotwrotethis</category>
    </item>
  </channel>
</rss>
