<?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</title>
    <description>The most recent home feed on DEV Community.</description>
    <link>https://dev.to</link>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed"/>
    <language>en</language>
    <item>
      <title>AI Literacy Will Be More Valuable Than Coding for Most Professionals</title>
      <dc:creator>Jaideep Parashar</dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:52:29 +0000</pubDate>
      <link>https://dev.to/jaideepparashar/ai-literacy-will-be-more-valuable-than-coding-for-most-professionals-2mo0</link>
      <guid>https://dev.to/jaideepparashar/ai-literacy-will-be-more-valuable-than-coding-for-most-professionals-2mo0</guid>
      <description>&lt;p&gt;For the last two decades, learning to code was considered one of the most valuable skills anyone could acquire.&lt;/p&gt;

&lt;p&gt;Today, a new skill is emerging that may become even more important for millions of professionals:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI Literacy.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This statement often creates controversy, especially among developers. But after years of studying technology adoption, business systems, and artificial intelligence, I believe we are entering an era where understanding how to work with AI will create more opportunities than learning programming for most people.&lt;/p&gt;

&lt;p&gt;Notice the phrase for most people.&lt;/p&gt;

&lt;p&gt;I'm not saying coding is becoming irrelevant. Software developers will continue to play a critical role in building the future.&lt;/p&gt;

&lt;p&gt;What is changing is the relationship between humans and technology.&lt;/p&gt;

&lt;p&gt;Let's explore why.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What Is AI Literacy?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI literacy is the ability to understand, evaluate, and effectively use artificial intelligence systems.&lt;/p&gt;

&lt;p&gt;It includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Knowing what AI can and cannot do&lt;/li&gt;
&lt;li&gt;Writing effective prompts&lt;/li&gt;
&lt;li&gt;Verifying AI-generated outputs&lt;/li&gt;
&lt;li&gt;Integrating AI into workflows&lt;/li&gt;
&lt;li&gt;Understanding AI limitations and risks&lt;/li&gt;
&lt;li&gt;Using AI responsibly and ethically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In simple terms:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coding teaches computers how to work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI literacy teaches humans how to work with intelligent systems.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That distinction is becoming increasingly important.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Shift We Are Witnessing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Historically, if you wanted technology to perform a task, you needed someone who could write code.&lt;/p&gt;

&lt;p&gt;Today, things are different.&lt;/p&gt;

&lt;p&gt;A marketing manager can generate campaign ideas.&lt;/p&gt;

&lt;p&gt;A researcher can summarize hundreds of pages.&lt;/p&gt;

&lt;p&gt;A teacher can create lesson plans.&lt;/p&gt;

&lt;p&gt;A business owner can build prototypes.&lt;/p&gt;

&lt;p&gt;A writer can generate drafts.&lt;/p&gt;

&lt;p&gt;All of this can happen without writing traditional code.&lt;/p&gt;

&lt;p&gt;The barrier between an idea and execution is shrinking rapidly.&lt;/p&gt;

&lt;p&gt;The new challenge is no longer:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Can I code this?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The new challenge is:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Can I think clearly enough to guide AI effectively?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Real Skill Is Thinking&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One misconception about AI is that it eliminates the need for human intelligence.&lt;/p&gt;

&lt;p&gt;The opposite is happening.&lt;/p&gt;

&lt;p&gt;AI rewards clarity.&lt;/p&gt;

&lt;p&gt;If your instructions are vague, the output is vague.&lt;/p&gt;

&lt;p&gt;If your objectives are unclear, the results are inconsistent.&lt;/p&gt;

&lt;p&gt;If your reasoning is weak, AI simply amplifies that weakness.&lt;/p&gt;

&lt;p&gt;Consider these two prompts:&lt;/p&gt;

&lt;p&gt;Prompt 1&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Write a business plan.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Prompt 2&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Act as a startup consultant.&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Create a one-page business plan for an AI education company targeting working professionals. Include: - Problem - Solution - Revenue Model - Marketing Strategy - Risks&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The difference is not coding.&lt;/p&gt;

&lt;p&gt;The difference is thinking.&lt;/p&gt;

&lt;p&gt;And that is what AI literacy develops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why This Matters for Non-Technical Professionals&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many people still believe AI is primarily for engineers and developers.&lt;/p&gt;

&lt;p&gt;That mindset is becoming outdated.&lt;/p&gt;

&lt;p&gt;Every profession is becoming AI-assisted.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HR professionals using AI for recruitment&lt;/li&gt;
&lt;li&gt;Lawyers using AI for document analysis&lt;/li&gt;
&lt;li&gt;Doctors using AI-assisted diagnostics&lt;/li&gt;
&lt;li&gt;Designers using AI-generated concepts&lt;/li&gt;
&lt;li&gt;Entrepreneurs using AI for market research&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The professionals who thrive will not necessarily be the ones who understand the most code.&lt;/p&gt;

&lt;p&gt;They will be the ones who understand how to combine human judgment with AI capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Simple Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's say you want Python code that analyzes a CSV file.&lt;/p&gt;

&lt;p&gt;Instead of writing everything from scratch, you could ask AI:&lt;/p&gt;

&lt;p&gt;import pandas as pd&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read_csv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sales.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if you don't fully understand Python, AI can help generate a starting point.&lt;/p&gt;

&lt;p&gt;However, you still need to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What question you're trying to answer&lt;/li&gt;
&lt;li&gt;Whether the output makes sense&lt;/li&gt;
&lt;li&gt;How the results affect decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That requires AI literacy.&lt;/p&gt;

&lt;p&gt;Not just technical knowledge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Future Belongs to AI Collaborators&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most successful professionals of the next decade will likely fall into three categories:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. AI Builders&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;People who create AI systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. AI Integrators&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;People who embed AI into business processes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. AI Collaborators&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;People who use AI to increase their effectiveness.&lt;/p&gt;

&lt;p&gt;Most professionals will belong to the third category.&lt;/p&gt;

&lt;p&gt;And that is perfectly fine.&lt;/p&gt;

&lt;p&gt;Not everyone needs to become a machine learning engineer.&lt;/p&gt;

&lt;p&gt;But everyone should learn how to work intelligently with AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My Perspective&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the biggest mistakes organizations make is treating AI as a technology project.&lt;/p&gt;

&lt;p&gt;AI is fundamentally a human capability project.&lt;/p&gt;

&lt;p&gt;The tools will continue to evolve.&lt;/p&gt;

&lt;p&gt;Models will improve.&lt;/p&gt;

&lt;p&gt;Interfaces will change.&lt;/p&gt;

&lt;p&gt;What will remain valuable is the ability to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Think clearly&lt;/li&gt;
&lt;li&gt;Ask better questions&lt;/li&gt;
&lt;li&gt;Evaluate information&lt;/li&gt;
&lt;li&gt;Make sound decisions&lt;/li&gt;
&lt;li&gt;Collaborate with intelligent systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are not technical skills.&lt;/p&gt;

&lt;p&gt;They are human skills.&lt;/p&gt;

&lt;p&gt;And AI literacy strengthens them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Final Thoughts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Coding remains an extraordinary skill and will continue to be essential for building the digital world.&lt;/p&gt;

&lt;p&gt;But for the majority of professionals, the greater opportunity may lie elsewhere.&lt;/p&gt;

&lt;p&gt;The future is not about competing with AI.&lt;/p&gt;

&lt;p&gt;The future is about learning how to collaborate with it.&lt;/p&gt;

&lt;p&gt;Those who develop AI literacy today will be better prepared for the opportunities, challenges, and transformations of tomorrow.&lt;/p&gt;

&lt;p&gt;In the AI era, the ultimate advantage may not be writing better code.&lt;/p&gt;

&lt;p&gt;It may be learning how to think better alongside intelligent machines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What do you think?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Do you believe AI literacy will become a core professional skill in the next five years? Why or why not? Share your thoughts below.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Date math notes for a court deadline calculator</title>
      <dc:creator>Xian X</dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:47:07 +0000</pubDate>
      <link>https://dev.to/xian_x_9469bb3e1b9a2f6ed0/date-math-notes-for-a-court-deadline-calculator-2ljk</link>
      <guid>https://dev.to/xian_x_9469bb3e1b9a2f6ed0/date-math-notes-for-a-court-deadline-calculator-2ljk</guid>
      <description>&lt;p&gt;Date calculators look simple until the rules around the date become the main product. I have been using a small court-day calculator as a reminder to keep the implementation boring, explicit, and easy to audit.&lt;/p&gt;

&lt;p&gt;The most important design decision is to separate ordinary calendar math from jurisdiction-specific legal rules. A tool can help someone count days, compare deadlines, and avoid manual arithmetic mistakes, but it should not pretend to replace local court rules or legal advice. That boundary needs to be visible in both the interface and the page copy.&lt;/p&gt;

&lt;p&gt;For the calculator workflow, I keep a few implementation notes close to the code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Normalize all input dates before doing any counting.&lt;/li&gt;
&lt;li&gt;Show the start date and end date in the result, not only the final number.&lt;/li&gt;
&lt;li&gt;Make weekend and holiday assumptions explicit.&lt;/li&gt;
&lt;li&gt;Keep a plain-language explanation beside the result so users can catch obvious mistakes.&lt;/li&gt;
&lt;li&gt;Avoid hidden defaults when a date rule could change the answer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The small calculator page I am using for these checks is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://courtdayscalc.com/" rel="noopener noreferrer"&gt;https://courtdayscalc.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A useful test case is to compare a short same-week range, a range that crosses a weekend, and a range that crosses a month boundary. Those three cases catch many accidental off-by-one errors before the tool reaches real users.&lt;/p&gt;

&lt;p&gt;For any legal-adjacent utility, clarity matters more than cleverness. The safest pattern is to make the arithmetic transparent, expose assumptions, and tell users when they need to verify a rule outside the tool.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>productivity</category>
      <category>programming</category>
      <category>software</category>
    </item>
    <item>
      <title>Introduction to TypeScript. JavaScript primitive data types</title>
      <dc:creator>Julia Shlykova</dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:44:53 +0000</pubDate>
      <link>https://dev.to/jsha/introduction-to-typescript-javascript-primitive-data-types-49ok</link>
      <guid>https://dev.to/jsha/introduction-to-typescript-javascript-primitive-data-types-49ok</guid>
      <description>&lt;h2&gt;
  
  
  Implicit and explicit data type assignment
&lt;/h2&gt;

&lt;p&gt;Now, with TypeScript we have the ability to explicitly set data type for a value like this:&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="c1"&gt;//script.ts &lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&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;str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, when we assign a value, even without explicitly declaring data type, TypeScript can implicitly set data type. This technique is called &lt;strong&gt;inferred typing&lt;/strong&gt;. The following code is still valid:&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="c1"&gt;//script.ts &lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// type is "string"&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;str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;TypeScript will still show an error if we try to assign a new value that is not of the previous data type.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//script.ts &lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;str&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;// Type 'number' is not assignable to type 'string'.&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;str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, in what cases would we need to explicitly set data type?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if we not immediately assign a value to a variable when we declare it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&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;str&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;for function input and output:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&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;ul&gt;
&lt;li&gt;inside object types (interfaces);&lt;/li&gt;
&lt;li&gt;inside classes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Duck inferred typing
&lt;/h3&gt;

&lt;p&gt;In more complex variable types if they are not explicitely set, TypeScript uses so-called "duck typing" method:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If it looks like a duck, and quacks like a duck, then it probably is a duck"&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&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;Mary&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;greet&lt;/span&gt;&lt;span class="p"&gt;()&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="s2"&gt;`Hello, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;this&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="s2"&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="nx"&gt;obj1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ann&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Ok&lt;/span&gt;

&lt;span class="nx"&gt;obj1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Property 'age' does not exist on type '{ id: number; name: string; greet(): void; }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TypeScript defined implicityle the type as &lt;code&gt;{ id: number; name: string; greet(): void; }&lt;/code&gt; and doesn't allow to assign new properties.&lt;/p&gt;





&lt;h2&gt;
  
  
  Primitive JavaScript types
&lt;/h2&gt;

&lt;p&gt;Each primitive type in JavaScript has a corresponding type in TypeScript:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;number&lt;/strong&gt; - floating-point numbers:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;a&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="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;123.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mh"&gt;0x7b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//hexadecimal notation&lt;/span&gt;
&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mb"&gt;0b1111011&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//binary notation&lt;/span&gt;
&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.123e3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// exponential notation&lt;/span&gt;
&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;Infinity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;bigint&lt;/strong&gt; - used for large integers (usually higher than &lt;code&gt;2^53-1&lt;/code&gt; or lower than &lt;code&gt;-(2^53-1)&lt;/code&gt;):
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;bigint&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9007199254740992&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BigInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;9007199254740992&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;ul&gt;
&lt;li&gt;
&lt;strong&gt;string&lt;/strong&gt; - sequence of characters:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;apple&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; bears`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;boolean&lt;/strong&gt; - true of false:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Boolean&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="c1"&gt;//false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;symbol&lt;/strong&gt; - special type that makes value unique:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Symbol&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;key&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;h3&gt;
  
  
  Null and undefined
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;null&lt;/strong&gt; - indicates value, that's empty or non-existent and we don't expect it to change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;undefined&lt;/strong&gt; - works as a placeholder for a value of a variable, that was declared but has not yet been assigned to other value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These two values in TypeScript have &lt;code&gt;any&lt;/code&gt; data type. It means that we can change the value of the variable to any value.&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;let&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// data type is any&lt;/span&gt;

&lt;span class="nx"&gt;a&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="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;str&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;If we explicitly set data type as &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;, the variable won't have the ability to change its value:&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;let&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;a&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;// Type '1' is not assignable to type 'undefined'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Introduction to TypeScript. Special TypeScript data types</title>
      <dc:creator>Julia Shlykova</dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:44:36 +0000</pubDate>
      <link>https://dev.to/jsha/introduction-to-typescript-special-typescript-data-types-2m67</link>
      <guid>https://dev.to/jsha/introduction-to-typescript-special-typescript-data-types-2m67</guid>
      <description>&lt;h2&gt;
  
  
  Arrays and tuples
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;array&lt;/strong&gt; - collection of items (similar to JavaScript). In TypeScript we can specify what value types we expect for items to be:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;arr&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="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="nx"&gt;arr&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="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="nx"&gt;arr&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="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Type 'string' is not assignable to type 'number'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;we can declare nested arrays like this: &lt;code&gt;let arr: string[][] = [["string1"], ["string2"], []]&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;tuples&lt;/strong&gt; - fixed length array that has predefined types for each position:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;tup&lt;/span&gt;&lt;span class="p"&gt;:&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nx"&gt;tup&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="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;tup&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;//Type 'number' is not assignable to type 'string'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also can use nested tuples:&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;const&lt;/span&gt; &lt;span class="nx"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;:&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="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]][]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&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="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;0&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&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;1&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nx"&gt;coords&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;







&lt;h2&gt;
  
  
  Literal types
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;literal types&lt;/strong&gt; - constraints on types (string, number or boolean), specifying exactly which values the variable is allowed to take:&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;let&lt;/span&gt; &lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;offline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;online&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ofline&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Type '"ofline"' is not assignable to type '"offline" | "online"'. Did you mean '"offline"'?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Object literal
&lt;/h3&gt;

&lt;p&gt;We can also specify what properties can our object have using &lt;strong&gt;object literals&lt;/strong&gt;:&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;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;a&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="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;obj.b = 2; //Type 'number' is not assignable to type 'string'&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We used optional parameter (&lt;code&gt;?:&lt;/code&gt;), that allows us to omit value assignment to the property.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  enum type
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;enum&lt;/strong&gt; (from &lt;em&gt;enumeration&lt;/em&gt;) - a collection of named constants linked with an integer (by default enumeration starts from 0):&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="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Color&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Red&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Blue&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;colorName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="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;colorName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Colors&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Red&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Blue&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;colorName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Colors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&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;colorName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Green&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  String enums
&lt;/h3&gt;

&lt;p&gt;String enums allow us to create a set of constants mapped to strings:&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="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Notification&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Warning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This is a warning&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This is a success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Danger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This is a danger&lt;/span&gt;&lt;span class="dl"&gt;"&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;Notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Danger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// This is a danger&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  enum as data type of a variable
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Offline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OFFLINE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Online&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ONLINE&lt;/span&gt;&lt;span class="dl"&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;userStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;userStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Offline&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;userStatus&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// OFFLINE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  enum vs object
&lt;/h3&gt;

&lt;p&gt;You may ask yourself, why would we use enum, if we can achieve the same behavior with object?&lt;/p&gt;

&lt;p&gt;Let's look at the last example: we have &lt;code&gt;userStatus&lt;/code&gt; type as enum, but in the end of the day it's just a string and let's try to rewrite it with 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;const&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;Offline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OFFLINE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;Online&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ONLINE&lt;/span&gt;&lt;span class="dl"&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;userStatus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Offline&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;userStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;plain text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// OK&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;We can assign another value to &lt;code&gt;userStatus&lt;/code&gt;!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Actually the compiler transforms &lt;code&gt;enum&lt;/code&gt; to javascript the object. We can even access its properties:&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="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Offline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OFFLINE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Online&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ONLINE&lt;/span&gt;&lt;span class="dl"&gt;'&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;getAttr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;Offline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Offline&lt;/span&gt;&lt;span class="p"&gt;;&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="nf"&gt;getAttr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// OFFLINE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Actually, we can make &lt;code&gt;enums&lt;/code&gt; disappear at the compile time using &lt;code&gt;const&lt;/code&gt;. They won't be compiled to the object and the variables assigned to their keys will be assigned to literals:&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;const&lt;/span&gt; &lt;span class="kr"&gt;enum&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Offline&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;OFFLINE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Online&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ONLINE&lt;/span&gt;&lt;span class="dl"&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;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Offline&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;getAttr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;Offline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Offline&lt;/span&gt;&lt;span class="p"&gt;;&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="nf"&gt;getAttr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment or type query.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;







&lt;h2&gt;
  
  
  Any, Unknown and Type Casts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;any&lt;/strong&gt; - this type tells typescript compiler to ignore the variable while type checking. It means that we can assign any value to the variable and call any method on it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;x&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="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="nf"&gt;x&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// compiler allows it, since it doesn't know what type of the variable it is.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;any&lt;/code&gt; is used in a difficult situation, when we are not able to predict the type. Usually in the case of unceratainty you should use &lt;code&gt;unknown&lt;/code&gt; type.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;unknown&lt;/strong&gt; - we don't know what the type is going to be but before performing any operations with the variable we have to check its type:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&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;a&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="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;+=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&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;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another way to work with &lt;code&gt;unknown&lt;/code&gt; type is to use &lt;strong&gt;type casting&lt;/strong&gt;:&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;let&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// We tell the compiler to treat the variable as its type is number:&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&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;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>beginners</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Create condition using JooqTemplate</title>
      <dc:creator>ts5432</dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:43:06 +0000</pubDate>
      <link>https://dev.to/javaer/create-condition-using-jooqtemplate-76d</link>
      <guid>https://dev.to/javaer/create-condition-using-jooqtemplate-76d</guid>
      <description>&lt;h2&gt;
  
  
  1. condition
&lt;/h2&gt;

&lt;p&gt;Create a Condition from a Field, an operator, and a value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Condition&lt;/span&gt; &lt;span class="nf"&gt;condition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Name&lt;/span&gt; &lt;span class="n"&gt;fieldName&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;operator&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. conditions
&lt;/h2&gt;

&lt;p&gt;Parse varargs into List. Arguments are grouped in pairs (fieldName, value). An operator suffix can be appended to the field name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Condition&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;conditions&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;condtionKvs&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Basic usage: pairs of (fieldName, value)&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Condition&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;conds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;conditions&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"John"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;                 &lt;span class="c1"&gt;// name = 'John'&lt;/span&gt;
    &lt;span class="s"&gt;"birthday≥"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;beginDate&lt;/span&gt;        &lt;span class="c1"&gt;// birthday ≥ '2020-01-01'&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Mixing Condition objects&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Condition&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;conds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;conditions&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="no"&gt;F&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"create_time"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;isNotNull&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;  &lt;span class="c1"&gt;// passing a Condition directly&lt;/span&gt;
    &lt;span class="s"&gt;"birthday:desc"&lt;/span&gt;               &lt;span class="c1"&gt;// OrderBy: birthday DESC (ignored for conditions)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Using conditions with query&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_table"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;jt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;conditions&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name%"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"birthday&amp;gt;="&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;beginDate&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;(none) or ==&lt;/td&gt;
&lt;td&gt;Equal (eq / isNull)&lt;/td&gt;
&lt;td&gt;"name", value → name = ?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;%&lt;/td&gt;
&lt;td&gt;Contains (LIKE)&lt;/td&gt;
&lt;td&gt;"name%", "Jo" → name LIKE '%Jo%'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;%L&lt;/td&gt;
&lt;td&gt;Starts with&lt;/td&gt;
&lt;td&gt;"name%L", "Jo" → name LIKE 'Jo%'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;%R&lt;/td&gt;
&lt;td&gt;Ends with&lt;/td&gt;
&lt;td&gt;"name%R", "hn" → name LIKE '%hn'&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt;&lt;/td&gt;
&lt;td&gt;Greater than&lt;/td&gt;
&lt;td&gt;"birthday&amp;gt;", date → birthday &amp;gt; ?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;gt;=&lt;/td&gt;
&lt;td&gt;Greater than or equal&lt;/td&gt;
&lt;td&gt;"birthday&amp;gt;=", date → birthday &amp;gt;= ?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;&lt;/td&gt;
&lt;td&gt;Less than&lt;/td&gt;
&lt;td&gt;"age&amp;lt;", 18 → age &amp;lt; ?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;=&lt;/td&gt;
&lt;td&gt;Less than or equal&lt;/td&gt;
&lt;td&gt;"age&amp;lt;=", 18 → age &amp;lt;= ?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&amp;lt;&amp;gt;&lt;/td&gt;
&lt;td&gt;Not equal&lt;/td&gt;
&lt;td&gt;"name&amp;lt;&amp;gt;", "admin" → name &amp;lt;&amp;gt; ?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;in&lt;/td&gt;
&lt;td&gt;IN query&lt;/td&gt;
&lt;td&gt;"id", list (Collection/array value) → id IN (?,?,?)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;notin&lt;/td&gt;
&lt;td&gt;NOT IN query&lt;/td&gt;
&lt;td&gt;"id:notin", list → id NOT IN (?,?,?)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isnull&lt;/td&gt;
&lt;td&gt;IS NULL (unary, no value argument)&lt;/td&gt;
&lt;td&gt;"name:isnull" → name IS NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isnotnull&lt;/td&gt;
&lt;td&gt;IS NOT NULL (unary, no value argument)&lt;/td&gt;
&lt;td&gt;"name:isnotnull" → name IS NOT NULL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  3. Order By
&lt;/h2&gt;

&lt;p&gt;In varargs, can append sort operators to field names to specify ordering. can also pass OrderField objects directly.&lt;br&gt;
example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Mix ordering in varargs&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;queryv&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_table"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"birthday:desc"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;// ORDER BY birthday DESC&lt;/span&gt;
    &lt;span class="no"&gt;F&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;asc&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;nullsLast&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// ORDER BY name ASC NULLS LAST&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Pass order list to query()&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user_table"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;conditions&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;asList&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;F&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"birthday"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;F&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;asc&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Sort Operator&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;fieldName:asc&lt;/td&gt;
&lt;td&gt;ASC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fieldName:desc&lt;/td&gt;
&lt;td&gt;DESC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fieldName:ascnf&lt;/td&gt;
&lt;td&gt;ASC NULLS FIRST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fieldName:ascnl&lt;/td&gt;
&lt;td&gt;ASC NULLS LAST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fieldName:descnf&lt;/td&gt;
&lt;td&gt;DESC NULLS FIRST&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fieldName:descnl&lt;/td&gt;
&lt;td&gt;DESC NULLS LAST&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>database</category>
      <category>java</category>
      <category>sql</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Notes from maintaining a small game reference page</title>
      <dc:creator>Xian X</dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:42:23 +0000</pubDate>
      <link>https://dev.to/xian_x_9469bb3e1b9a2f6ed0/notes-from-maintaining-a-small-game-reference-page-5edp</link>
      <guid>https://dev.to/xian_x_9469bb3e1b9a2f6ed0/notes-from-maintaining-a-small-game-reference-page-5edp</guid>
      <description>&lt;p&gt;I have been keeping notes on a small fan reference page and wanted to write down the checks that made it less fragile. The main lesson is that a reference page should not try to look bigger than the information it can verify. A short page with clear scope is more useful than a large page padded with guesses.&lt;/p&gt;

&lt;p&gt;For game notes, I now separate three things before publishing: confirmed mechanics, change-prone observations, and open questions. Confirmed items can sit in the main table. Change-prone items need a date or version note. Open questions should stay out of the main answer until there is better evidence.&lt;/p&gt;

&lt;p&gt;That process also makes updates faster. When a player reports a change, I can compare it against the current notes instead of rewriting the whole page. It is a small workflow, but it prevents the common wiki problem where old information stays visible because nobody remembers why it was added.&lt;/p&gt;

&lt;p&gt;The current reference page I am using for this workflow is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://growagardens2.com/" rel="noopener noreferrer"&gt;https://growagardens2.com/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A simple checklist I use before updating a page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can this claim be checked from more than one source or direct play?&lt;/li&gt;
&lt;li&gt;Does the page show the limits of what is known?&lt;/li&gt;
&lt;li&gt;Are dates or version notes included where the information may change?&lt;/li&gt;
&lt;li&gt;Is the answer easy to scan on mobile?&lt;/li&gt;
&lt;li&gt;Would a new reader understand the next action without reading the whole page?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Small reference projects do not need heavy infrastructure. They need careful wording, repeatable checks, and a willingness to leave uncertain details unpublished until they are verified.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>gamedev</category>
      <category>documentation</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The Skills File Pattern</title>
      <dc:creator>kavyarani7</dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:33:48 +0000</pubDate>
      <link>https://dev.to/kavyarani7/the-skills-file-pattern-2oam</link>
      <guid>https://dev.to/kavyarani7/the-skills-file-pattern-2oam</guid>
      <description>&lt;p&gt;&lt;strong&gt;SectorFlow Engineering Series&lt;/strong&gt;   ·   Part 2 of 3   ·   Read Part 1 first: &lt;a href="https://dev.to/kavyarani7/token-efficiency-in-claude-code-2kpi"&gt;Token Efficiency in Claude Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How we stopped appending to CLAUDE.md and started importing only what we need.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;June 2026  ·  SectorFlow Engineering&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In this series&lt;/strong&gt;    Part 1: &lt;a href="https://dev.to/kavyarani7/token-efficiency-in-claude-code-2kpi"&gt;Token Efficiency in Claude Code&lt;/a&gt; (start here).    &lt;a href="https://dev.to/kavyarani7/picking-models-and-tools-gmn"&gt;Part 3: Picking Models and Tools&lt;/a&gt; — the MCPs we tried, refused, and why.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The append loop
&lt;/h2&gt;

&lt;p&gt;If you use Claude Code for a while you'll probably land where we did: a &lt;code&gt;CLAUDE.md&lt;/code&gt; that got too big and too self-contradictory to trust.&lt;/p&gt;

&lt;p&gt;It starts fine. You write down the basics — what the app does, where the server lives, what the API looks like. Then something breaks. Claude picks the wrong model string, or makes a chart without the right color, or ignores a cache rule you mentioned in passing three weeks ago. So you write the rule down and append it. Reasonable.&lt;/p&gt;

&lt;p&gt;And it works for a while. Then the file's big, the week-two rules are sitting on top of the week-six rules, and when two of them disagree — they will, because requirements move and old notes go stale — the model either reconciles them into mush or just picks one. You spend an hour debugging output before you realize the rule you thought was active got overridden by something you wrote months ago and forgot about.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The bigger issue: the whole file loads every session, no matter what you're doing. A color fix on the frontend doesn't need the deployment runbook. An API route change doesn't need the chart color rules. Every line that isn't relevant is costing tokens and adding noise.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We thought about how to fix this without just deleting the stuff we'd need again later, and ended up with what we call the skills file pattern. Split the context into separate files by purpose, import only what the task needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The structure
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; stops being the rulebook and becomes a table of contents. The actual rules move into separate files under &lt;code&gt;.claude/&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;What it owns&lt;/th&gt;
&lt;th&gt;When it loads&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;core.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Absolute constraints: model strings, API key rules, cache TTLs, data contracts&lt;/td&gt;
&lt;td&gt;Every session — the foundational constraints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;design.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Color tokens, chart types, component patterns, loading-state conventions&lt;/td&gt;
&lt;td&gt;UI and frontend tasks only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;infrastructure.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Hosting config, environment variables, deployment rules, automator wiring&lt;/td&gt;
&lt;td&gt;Infrastructure and CI tasks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;workflow.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ticket format, commit rules, token rules, division of labor&lt;/td&gt;
&lt;td&gt;Every coding session&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;architecture.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;ADRs, scaling thresholds, rejected technical decisions&lt;/td&gt;
&lt;td&gt;Architecture reviews and new integrations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The import syntax in &lt;code&gt;CLAUDE.md&lt;/code&gt; is nothing special:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@sector-dashboard/.claude/core.md
@sector-dashboard/.claude/design.md
@sector-dashboard/.claude/workflow.md
(infrastructure.md and architecture.md load only when the task references them)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So a UI task loads core + design + workflow. An infra task loads core + infrastructure + workflow. &lt;code&gt;architecture.md&lt;/code&gt; only shows up if someone actually asks about a scaling decision or an ADR. The context stays small and stays on topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  What goes where
&lt;/h2&gt;

&lt;h3&gt;
  
  
  core.md — the constitution
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;core.md&lt;/code&gt; is the one file that loads every single session, so everything in it has to be true and currently enforced. We treat it like a constitution. Stable, authoritative, never wishful.&lt;/p&gt;

&lt;p&gt;Two kinds of things go in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Facts we've verified — model IDs we've confirmed work, endpoint shapes, cache key names, exact token limits per endpoint.&lt;/li&gt;
&lt;li&gt;Hard constraints — rules you don't break without a deliberate, written-down decision (where the API key goes, no swapping models, TTL values).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Don't put aspirations in core.md. "We should try to…" or "ideally the model would…" — that's workflow.md material. core.md is only for things that are true right now. The moment you put a hope in there, reality eventually contradicts it and now you've got a lie sitting in your most-trusted file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  design.md — the visual contract
&lt;/h3&gt;

&lt;p&gt;Every hex value, every chart-type assignment, every loading-state pattern goes in &lt;code&gt;design.md&lt;/code&gt;. Three reasons it pays off:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new engineer, or a fresh Claude session, doesn't have to reverse-engineer the visual conventions out of the component code.&lt;/li&gt;
&lt;li&gt;When we changed the color logic on the 52-week range bar, we edited one table in &lt;code&gt;design.md&lt;/code&gt;. No component files had to change just to record the rule.&lt;/li&gt;
&lt;li&gt;We can reject a PR where Claude hardcoded a hex instead of using the documented token, because the rule is written down.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;design.md&lt;/code&gt; is also where we list what not to touch. &lt;code&gt;TradingViewChart.jsx&lt;/code&gt; is wrapper-only. &lt;code&gt;signals.js&lt;/code&gt; has its own color system and must not get the new &lt;code&gt;--sf-&lt;/code&gt; variables. Those "don't" rules matter as much as the conventions do.&lt;/p&gt;

&lt;h3&gt;
  
  
  workflow.md — who does what
&lt;/h3&gt;

&lt;p&gt;This is the file that matters most day to day. It spells out what Claude does and what the engineer does, in enough detail that there's no room to wonder. Part 1 has the full reasoning, but the short version is: if the engineer can do it in under two minutes in a terminal, it shouldn't go to Claude through an MCP.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;workflow.md&lt;/code&gt; also holds the task format we make engineers use when they hand work to Claude:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Required field&lt;/th&gt;
&lt;th&gt;Why it exists&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Branch: {name}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stops Claude asking or guessing — one less tool call&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Files to CREATE: [list]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Scopes creation, stops new files getting added speculatively&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Files to MODIFY: [list + one-liner]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Keeps Claude from reading unrelated files for context&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;What to build: [criteria]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The single source of truth for when it's done&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Do NOT read: [not listed]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;An explicit boundary — absence isn't permission&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If any of those fields is missing, Claude asks before it touches a file. That kills the usual failure mode where it reads three or four files "to understand the codebase" before writing a single line.&lt;/p&gt;

&lt;h3&gt;
  
  
  architecture.md — decisions and why
&lt;/h3&gt;

&lt;p&gt;Architecture decisions go in as ADRs — the decision, what led to it, what it costs us. We record the rejected ones too, and those turn out to be the more useful part.&lt;/p&gt;

&lt;p&gt;When some future session asks "why not GraphQL?" or "should we add WebSockets for live data?", the answer is already sitting there: considered, rejected, here's why. Claude doesn't reopen it and neither do we.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule that keeps it from rotting
&lt;/h2&gt;

&lt;p&gt;One rule keeps the split files from turning into the same contradictory mess as the old monolith:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One file owns one domain. If a rule could live in two files, it goes in the more constrained one — usually core.md. And if you can't tell which file a rule belongs in, it's probably too vague to be a rule.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In practice: color tokens go in &lt;code&gt;design.md&lt;/code&gt;, not &lt;code&gt;core.md&lt;/code&gt;. Cache TTLs go in &lt;code&gt;core.md&lt;/code&gt;, not &lt;code&gt;architecture.md&lt;/code&gt;. Division of labor is &lt;code&gt;workflow.md&lt;/code&gt;, not &lt;code&gt;core.md&lt;/code&gt;. Once the domains are clear the contradictions jump out — if the same thing shows up in two files, one of them is wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changed after
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Startup token cost down about 60% on a typical task.&lt;/li&gt;
&lt;li&gt;Contradictions are findable now — the same topic in two files gets flagged.&lt;/li&gt;
&lt;li&gt;New rules have an obvious home, and figuring out the home forces you to be clear about what kind of rule it even is.&lt;/li&gt;
&lt;li&gt;You can review the files on their own — a frontend change goes up against &lt;code&gt;design.md&lt;/code&gt; and nothing else.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's not a complicated setup. Six files and a manifest. The work is in holding the one-domain line: the moment a file starts feeling like it's about two things, split it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Continue reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/kavyarani7/picking-models-and-tools-gmn"&gt;Part 3: Picking Models and Tools&lt;/a&gt; — the MCPs we tried, refused, and why.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>devtools</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Why AI-Generated UIs Look 'Off' — and the One Principle That Fixes It</title>
      <dc:creator>@kiwibreaksme</dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:33:17 +0000</pubDate>
      <link>https://dev.to/kiwibreaksme/why-ai-generated-uis-look-off-and-the-one-principle-that-fixes-it-4j20</link>
      <guid>https://dev.to/kiwibreaksme/why-ai-generated-uis-look-off-and-the-one-principle-that-fixes-it-4j20</guid>
      <description>&lt;p&gt;You've seen it. You ask an AI to build a dashboard, and it returns something that's… fine? Every component is individually competent. The button is a button. The card is a card. And yet the whole thing reads as &lt;em&gt;generated&lt;/em&gt; — like a stock photo of a UI.&lt;/p&gt;

&lt;p&gt;For a long time I assumed the fix was "better components" or "more taste in the prompt." It isn't. After digging through the actual design literature — Refactoring UI, Material Design 3, Apple's HIG, IBM Carbon, WCAG, the Financial Times' visual vocabulary — the real culprit has a name:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Incoherence.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The parts don't agree with each other. And once you can see it, you can't unsee it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I've written before about &lt;a href="https://dev.to/kiwibreaksme/your-vibe-coded-app-looks-ugly-heres-what-i-did-about-it-2nb4"&gt;why your vibe-coded app looks ugly and what I did about it&lt;/a&gt;. This post goes underneath that — the actual mechanism behind the "off" feeling, and the one principle that fixes it.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The tell: parts that don't agree
&lt;/h2&gt;

&lt;p&gt;Here's a UI with nothing "wrong" in it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A card with &lt;strong&gt;sharp&lt;/strong&gt; 2px corners&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pill-shaped&lt;/strong&gt; buttons inside it&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;blue&lt;/strong&gt; primary action next to a &lt;strong&gt;purple&lt;/strong&gt; "upgrade" link&lt;/li&gt;
&lt;li&gt;Icons: two from Lucide (outline), one filled emoji&lt;/li&gt;
&lt;li&gt;One shadow lit from the top-left, another from straight below&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every one of those choices is defensible in isolation. Together, they signal "assembled from parts," because a human designer would never make all of them on the same screen. A designer picks &lt;em&gt;one&lt;/em&gt; corner style, &lt;em&gt;one&lt;/em&gt; accent, &lt;em&gt;one&lt;/em&gt; icon set, &lt;em&gt;one&lt;/em&gt; light source — and repeats those decisions everywhere.&lt;/p&gt;

&lt;p&gt;That repetition is what we read as "designed by one mind." Goran Paun (UX Collective) calls it visual coherence; Steve Schoger's whole &lt;em&gt;Refactoring UI&lt;/em&gt; is, in a sense, a book about not introducing gratuitous variation. The principle is old. What's new is that &lt;strong&gt;AI breaks it by default&lt;/strong&gt;, and understanding &lt;em&gt;why&lt;/em&gt; tells you how to fix it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why AI specifically struggles with this
&lt;/h2&gt;

&lt;p&gt;A language model generates UI &lt;strong&gt;locally&lt;/strong&gt;. It writes the card, then the button, then the modal — each as a fresh, plausible snippet. It has no running memory that "the buttons 40 lines up were pill-shaped, so these must be too." Each component regresses to the mean of its training data, and the mean of "a button" and the mean of "a card" were never coordinated.&lt;/p&gt;

&lt;p&gt;Humans don't work this way. We carry a tiny set of decisions in our head — &lt;em&gt;this product is soft-cornered, blue-accented, 8px-grid&lt;/em&gt; — and every new element inherits them. The decisions are sticky.&lt;/p&gt;

&lt;p&gt;So the fix isn't "make the model more tasteful." It's: &lt;strong&gt;write the sticky decisions down, and tell the model to copy them instead of inventing new ones.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The meta-law: one choice per axis
&lt;/h2&gt;

&lt;p&gt;Here's the whole idea in one sentence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For each design axis, pick exactly &lt;strong&gt;one&lt;/strong&gt; value or family, encode it as a token, and apply it &lt;strong&gt;everywhere&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Coherence is not "every screen is identical." It's that the same &lt;em&gt;decisions&lt;/em&gt; recur. A settings page and a dashboard can look very different and still feel like one product — as long as they share a radius personality, a shadow language, an accent, and a spacing unit.&lt;/p&gt;

&lt;p&gt;These are the axes that have to stay unified:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Axis&lt;/th&gt;
&lt;th&gt;Pick ONE, system-wide&lt;/th&gt;
&lt;th&gt;Failure mode when mixed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Corner / radius&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;sharp 0–4px · soft 8–12px · pill 9999px&lt;/td&gt;
&lt;td&gt;sharp dialog + rounded buttons = "two products glued together"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Shadow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;one scale, one light source (above-left), one tint&lt;/td&gt;
&lt;td&gt;"a scene with two suns"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Accent color&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;one accent for emphasis (+ semantic red/green/amber)&lt;/td&gt;
&lt;td&gt;nothing reads as &lt;em&gt;the&lt;/em&gt; action&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Spacing unit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;one 8px grid (4px half-step)&lt;/td&gt;
&lt;td&gt;off-grid 7/13/19px reads as "sloppy"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Icon style&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;one family, one fill mode, one stroke weight&lt;/td&gt;
&lt;td&gt;mixing sets looks "out of place"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Type scale&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;one modular scale, ≤2 families&lt;/td&gt;
&lt;td&gt;arbitrary sizes destroy rhythm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Motion&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;one duration set + one easing family&lt;/td&gt;
&lt;td&gt;some snappy, some sluggish = different apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Control height&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;shared height set (e.g. 40px)&lt;/td&gt;
&lt;td&gt;a 44px input beside a 32px button breaks the baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The single best instinct I've heard a non-designer voice it as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"If the corners are sharp, everything should be sharp."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Exactly right — and it generalizes to &lt;em&gt;every&lt;/em&gt; row in that table. &lt;strong&gt;Treat a mixed axis as a lint error, not a style choice.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l2zltx9sf7kih8dxwe0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3l2zltx9sf7kih8dxwe0.png" alt="Every decision in a StyleSeed UI tied to the rule behind it — numbers 2:1, one accent, content in cards, one corner radius." width="799" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's make four of these concrete.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Pick one corner personality (and respect the nesting law)
&lt;/h2&gt;

&lt;p&gt;First, radius is a &lt;strong&gt;token&lt;/strong&gt;, never a magic number. Define a small scale and reference it everywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--radius-sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c"&gt;/* inputs, buttons   */&lt;/span&gt;
  &lt;span class="py"&gt;--radius-md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;12px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;/* cards, menus      */&lt;/span&gt;
  &lt;span class="py"&gt;--radius-lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;/* modals, sheets    */&lt;/span&gt;
  &lt;span class="py"&gt;--radius-full&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9999px&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;Then pick &lt;strong&gt;one personality&lt;/strong&gt; — all sharp, all soft, or all pill — and apply it across card / button / input / modal / image. Don't let the model freestyle a &lt;code&gt;rounded-none&lt;/code&gt; panel with &lt;code&gt;rounded-full&lt;/code&gt; buttons.&lt;/p&gt;

&lt;p&gt;There's a subtle second rule most people miss: &lt;strong&gt;nested radii must share a center.&lt;/strong&gt; When a rounded element sits inside a rounded container with padding, the inner radius should be &lt;em&gt;smaller&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;inner radius = outer radius − padding
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--pad&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;16px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--radius-lg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;          &lt;span class="c"&gt;/* 16px */&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--pad&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.card&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;.thumbnail&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* 16 − 16 = 0; clamp so it never goes negative */&lt;/span&gt;
  &lt;span class="nl"&gt;border-radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;calc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--radius-lg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;-&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--pad&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;If you give the inner element the &lt;em&gt;same&lt;/em&gt; radius as the outer one, its corner visibly bulges past the container's arc. Apple's new "Liquid Glass" system formalizes exactly this concentric-corner math; Cloud Four has the canonical write-up.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Layered shadows from a single light source
&lt;/h2&gt;

&lt;p&gt;The fastest way to make depth look fake is one hard &lt;code&gt;box-shadow&lt;/code&gt;. Real shadows are a gradient (a penumbra), so stack several low-opacity layers, and — critically — &lt;strong&gt;tint them toward the surface hue&lt;/strong&gt; instead of using pure black, which goes muddy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* card — sits near the page */&lt;/span&gt;
&lt;span class="nt"&gt;--shadow-md&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt; &lt;span class="err"&gt;1&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt; &lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;  &lt;span class="nt"&gt;hsl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;220&lt;/span&gt; &lt;span class="err"&gt;40&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;20&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;08&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt; &lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt; &lt;span class="err"&gt;4&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;  &lt;span class="nt"&gt;hsl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;220&lt;/span&gt; &lt;span class="err"&gt;40&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;20&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;08&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt; &lt;span class="err"&gt;4&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt; &lt;span class="err"&gt;8&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;  &lt;span class="nt"&gt;hsl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;220&lt;/span&gt; &lt;span class="err"&gt;40&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;20&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;08&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c"&gt;/* modal — floats toward the user */&lt;/span&gt;
&lt;span class="nt"&gt;--shadow-xl&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt; &lt;span class="err"&gt;2&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt; &lt;span class="err"&gt;4&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;   &lt;span class="nt"&gt;hsl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;220&lt;/span&gt; &lt;span class="err"&gt;40&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;20&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;06&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt; &lt;span class="err"&gt;8&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt; &lt;span class="err"&gt;16&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt;  &lt;span class="nt"&gt;hsl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;220&lt;/span&gt; &lt;span class="err"&gt;40&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;20&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;06&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt; &lt;span class="err"&gt;16&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt; &lt;span class="err"&gt;32&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt; &lt;span class="nt"&gt;hsl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;220&lt;/span&gt; &lt;span class="err"&gt;40&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;20&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;06&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
  &lt;span class="err"&gt;0&lt;/span&gt; &lt;span class="err"&gt;32&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt; &lt;span class="err"&gt;64&lt;/span&gt;&lt;span class="nt"&gt;px&lt;/span&gt; &lt;span class="nt"&gt;hsl&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;220&lt;/span&gt; &lt;span class="err"&gt;40&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="err"&gt;20&lt;/span&gt;&lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;0&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="err"&gt;06&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The coherence rule: &lt;strong&gt;one light direction for the whole page&lt;/strong&gt; (convention: above and slightly left), with vertical offset roughly 2× the horizontal. As elevation rises, offset and blur go up while opacity goes down. A card and a modal then read as the &lt;em&gt;same&lt;/em&gt; light hitting objects at different heights — not two unrelated effects. (Josh Comeau and Tobias Ahlin both have excellent deep-dives on this.)&lt;/p&gt;

&lt;p&gt;In dark mode, shadows nearly vanish, so switch to &lt;strong&gt;tonal elevation&lt;/strong&gt; — lighter surfaces for higher elevation — and never use pure &lt;code&gt;#000&lt;/code&gt; as your base (Material recommends &lt;code&gt;#121212&lt;/code&gt; with white overlays per level).&lt;/p&gt;




&lt;h2&gt;
  
  
  3. One accent, everything else greyscale
&lt;/h2&gt;

&lt;p&gt;Most "AI rainbow" UIs come from reaching for a new hue every time something needs emphasis. The discipline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One&lt;/strong&gt; accent for interactive emphasis.&lt;/li&gt;
&lt;li&gt;A full &lt;strong&gt;tinted-grey ramp&lt;/strong&gt; (not pure grey — nudge it 5–15% toward the brand hue) for text, surfaces, and borders.&lt;/li&gt;
&lt;li&gt;Only &lt;strong&gt;four&lt;/strong&gt; semantic colors — success / warning / error / info — used &lt;em&gt;strictly&lt;/em&gt; by meaning, never decoration.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#5b5bd6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* greys tinted slightly toward the accent's hue, not 0-saturation */&lt;/span&gt;
  &lt;span class="py"&gt;--grey-50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="n"&gt;hsl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;20%&lt;/span&gt; &lt;span class="m"&gt;98%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--grey-200&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hsl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;14%&lt;/span&gt; &lt;span class="m"&gt;90%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--grey-500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hsl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;8%&lt;/span&gt;  &lt;span class="m"&gt;55%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="py"&gt;--grey-900&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;hsl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;240&lt;/span&gt; &lt;span class="m"&gt;12%&lt;/span&gt; &lt;span class="m"&gt;14%&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c"&gt;/* body text — NOT #000 */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And one accessibility floor you don't get to negotiate (WCAG 2.2 AA): &lt;strong&gt;4.5:1&lt;/strong&gt; contrast for body text, &lt;strong&gt;3:1&lt;/strong&gt; for large text and UI components. Never convey information by color alone — pair it with an icon, text, or shape. A red border on an invalid field is not enough; add the icon and the message.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. One spacing grid, with proximity doing the grouping
&lt;/h2&gt;

&lt;p&gt;Snap every margin, padding, and gap to a single scale — &lt;code&gt;4, 8, 12, 16, 24, 32, 48, 64&lt;/code&gt; — and let &lt;strong&gt;proximity&lt;/strong&gt; carry meaning. The rule from Gestalt (and Refactoring UI's "avoid ambiguous spacing"): the space &lt;em&gt;around&lt;/em&gt; a group must be clearly larger than the space &lt;em&gt;within&lt;/em&gt; it.&lt;/p&gt;

&lt;p&gt;A concrete ladder for forms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;label → input        : 4–8px
input → input         : 12–16px
group → group (section): 24–32px
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When everything is evenly spaced, the eye can't tell what belongs together. The doubling at each level is what makes a label bind to &lt;em&gt;its&lt;/em&gt; field and a section separate from the next.&lt;/p&gt;




&lt;h2&gt;
  
  
  Make the machine check itself
&lt;/h2&gt;

&lt;p&gt;Writing the decisions down is half the battle; the other half is catching violations. This is where AI can actually help — if you give it the rubric.&lt;/p&gt;

&lt;p&gt;I bake all of the above into an open-source design engine (&lt;a href="https://github.com/bitjaru/styleseed" rel="noopener noreferrer"&gt;StyleSeed&lt;/a&gt;) that Claude Code and Cursor read automatically, but the idea is tool-agnostic. The most useful piece turned out to be a &lt;strong&gt;coherence grader&lt;/strong&gt;: a check that scores a file and flags &lt;em&gt;mixed axes&lt;/em&gt; as real deductions.&lt;/p&gt;

&lt;p&gt;A trimmed example of what that output looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Design Score: 70 / 100   (Dashboard.tsx)&lt;/span&gt;

Color discipline   13/18  ▓▓▓░  #000 headings; 3 accent hues
Cards &amp;amp; elevation   8/12  ▓▓░░  1px borders doing separation
Coherence           6/12  ▓▓░░  sharp cards (l.22) + pill buttons (l.48); 3 accents

&lt;span class="gu"&gt;### Fix first (highest score gain)&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Unify radius (pick soft 8–12px) + collapse to one accent  → +9
&lt;span class="p"&gt;2.&lt;/span&gt; Add empty + loading states to the orders list            → +7
&lt;span class="p"&gt;3.&lt;/span&gt; Drop 1px borders, use tone + ≤8% shadow                  → +4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "Coherence" category is the one that most predicts "looks AI-generated," precisely because it measures &lt;em&gt;system-wide consistency&lt;/em&gt; rather than per-component prettiness. A component can be beautiful and still wrong — if it disagrees with its neighbors.&lt;/p&gt;




&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;If you remember one thing:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Beautiful parts don't make a beautiful UI. &lt;strong&gt;Agreeing&lt;/strong&gt; parts do.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Pick one value per axis — radius, shadow, accent, spacing, icons, type, motion — write it down as a token, and make every new element inherit it instead of inventing a fresh choice. That single discipline is the difference between "a robot made this" and "a designer made this," and it's the cheapest, highest-leverage thing you can do for an AI-built interface.&lt;/p&gt;

&lt;p&gt;The corners are sharp? Then everything is sharp. All the way down.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Want the practical side? I covered &lt;a href="https://dev.to/kiwibreaksme/your-vibe-coded-app-looks-ugly-heres-the-one-file-fix-4ef0"&gt;the one-file fix that makes your vibe-coded app stop looking ugly&lt;/a&gt; in a companion post. And the full research-backed version — concrete spacing numbers, a type recipe per app type, layered-shadow and nested-radius recipes, all grounded in Refactoring UI / Material 3 / Apple HIG / WCAG — is open source (MIT): &lt;a href="https://github.com/bitjaru/styleseed" rel="noopener noreferrer"&gt;github.com/bitjaru/styleseed&lt;/a&gt;. A ⭐ helps more devs (and more AI tools) find it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>design</category>
      <category>css</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
    <item>
      <title>Token Efficiency in Claude Code</title>
      <dc:creator>kavyarani7</dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:31:45 +0000</pubDate>
      <link>https://dev.to/kavyarani7/token-efficiency-in-claude-code-2kpi</link>
      <guid>https://dev.to/kavyarani7/token-efficiency-in-claude-code-2kpi</guid>
      <description>&lt;p&gt;&lt;strong&gt;SectorFlow Engineering Series&lt;/strong&gt;   ·   Part 1 of 3   ·   Parent article&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Notes on where our context budget was actually going, and what we did about it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;June 2026  ·  SectorFlow Engineering&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In this series&lt;/strong&gt;    &lt;a href="https://dev.to/kavyarani7/the-skills-file-pattern-2oam"&gt;Part 2: The Skills File Pattern&lt;/a&gt; — fixing CLAUDE.md bloat with imports.    &lt;a href="https://dev.to/kavyarani7/picking-models-and-tools-gmn"&gt;Part 3: Picking Models and Tools&lt;/a&gt; — the MCPs we tried, refused, and why.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem nobody warns you about
&lt;/h2&gt;

&lt;p&gt;Claude Code can do a lot. The catch is that all of it runs on context, and you don't get much of that.&lt;/p&gt;

&lt;p&gt;When we started SectorFlow we did the obvious thing. Kept a &lt;code&gt;CLAUDE.md&lt;/code&gt; at the repo root, and every time something went wrong — wrong model string, a cache TTL that didn't match, a chart that came out looking off — we'd write a rule and stick it on the end. The file kept growing. We didn't really clock it as a problem until it was one.&lt;/p&gt;

&lt;p&gt;By about week six it was 400 lines. Every session loaded the whole thing. Frontend rules sitting next to deployment runbooks sitting next to database decisions, none of it sorted. And because we'd added the rules one at a time over weeks, some of them flatly disagreed with each other. Claude would follow the new one, or the old one, or try to split the difference. We got something wrong either way.&lt;/p&gt;

&lt;p&gt;I want to be clear this isn't a Claude Code problem. It's on us, and it's fixable. But fixing it meant we had to stop treating &lt;code&gt;CLAUDE.md&lt;/code&gt; like a junk drawer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The thing that actually hurts isn't the per-token price. It's that every token spent loading context is a token you don't get back for the work. Burn 30,000 on setup and you've got far less room to write code than if you'd burned 5,000. You hit the ceiling partway through a file and whatever you were in the middle of is just gone.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Context is a budget
&lt;/h2&gt;

&lt;p&gt;Here's the shift, and it's simple once you see it: anything Claude reads at the start of a session is something it can't use later for code. Most projects pile everything into &lt;code&gt;CLAUDE.md&lt;/code&gt; on the theory that the model might need it someday. We flipped the question. What does the model need for this task? Load that. Skip the rest.&lt;/p&gt;

&lt;p&gt;Two rules came out of it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Precision over completeness. A small context that's right does more for you than a big one trying to cover all the bases.&lt;/li&gt;
&lt;li&gt;Load on demand. Structure things so only the relevant part shows up for a given task.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those turned into three actual practices, and each one gets its own article in this series. This one is just the overview — what we measured and why it matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we actually changed
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Session startup cost
&lt;/h3&gt;

&lt;p&gt;Every session loads &lt;code&gt;CLAUDE.md&lt;/code&gt; plus whatever it imports. Before, that was the one 400-line file, every time, regardless of the task. After we split it into separate skill files, a UI task pulls in &lt;code&gt;core.md&lt;/code&gt; (the constraints) and &lt;code&gt;design.md&lt;/code&gt; (the visual stuff) and nothing else. An infra task gets &lt;code&gt;core.md&lt;/code&gt; and &lt;code&gt;infrastructure.md&lt;/code&gt;. Startup cost dropped about 60%.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Reading tickets
&lt;/h3&gt;

&lt;p&gt;We had the Linear MCP hooked up so Claude could read tickets itself. Nice in theory. But one &lt;code&gt;list_issues&lt;/code&gt; call runs about 3,500 tokens, and the whole read-it / mark-done / comment loop is around 9,000. So now the engineer just pastes the acceptance criteria. That's maybe 400 tokens. The 8,600 difference doesn't sound like much until you multiply it across 60-plus tickets — that's something like 7 or 8 full context windows handed back to the actual work.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Reading files it was never asked to read
&lt;/h3&gt;

&lt;p&gt;Left alone, Claude reads files to get its bearings, sometimes three or four of them before it writes a line. So we made a rule: only read files the task names. Need to find a function? &lt;code&gt;grep&lt;/code&gt; for it, then view just those lines. Don't open a file to soak up "context." If something's actually missing, ask. Saves 2,000–4,000 tokens on a complex task.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Spinning up the dev server to check things
&lt;/h3&gt;

&lt;p&gt;Verifying a change by eye means starting the server, waiting, navigating, screenshotting, evaluating — a whole chain of calls. For anything you can't see in a browser, like server logic or data contracts or route handlers, that chain tells you nothing. So we only do the visual check when the change is something a person could actually see in a browser. For syntax we run &lt;code&gt;node --check&lt;/code&gt;. One Bash call.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source of overhead&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;th&gt;Saving&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Session context load&lt;/td&gt;
&lt;td&gt;~400 lines, every session&lt;/td&gt;
&lt;td&gt;60–120 lines, task-specific&lt;/td&gt;
&lt;td&gt;~60%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ticket ingestion (per ticket)&lt;/td&gt;
&lt;td&gt;~9,000 tokens via MCP&lt;/td&gt;
&lt;td&gt;~400 tokens via paste&lt;/td&gt;
&lt;td&gt;~8,600 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;File reads per task&lt;/td&gt;
&lt;td&gt;3–5 files speculatively&lt;/td&gt;
&lt;td&gt;Named files only&lt;/td&gt;
&lt;td&gt;2,000–4,000 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verification overhead&lt;/td&gt;
&lt;td&gt;Dev server + screenshot&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;node --check&lt;/code&gt; only&lt;/td&gt;
&lt;td&gt;4–6 tool calls&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each of these on its own is fine, nothing dramatic. Put together they change what fits in a session. Stuff that used to take two or three sessions now usually fits in one. That's the whole point.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the parts fit
&lt;/h2&gt;

&lt;p&gt;The other two articles each take one piece of this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Part 2, the skills file pattern, is about the startup cost and the contradicting-rules mess. It's where the import-on-demand structure comes from.&lt;/li&gt;
&lt;li&gt;Part 3, models and tools, covers the ticket overhead and the file-reading habit, plus which MCPs we said no to and where we drew the line between Haiku and Sonnet.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read this one first for the why. Then either of the others for the how.&lt;/p&gt;

&lt;h2&gt;
  
  
  One thing to keep in mind
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Claude Code does its best work when the context is small, accurate, and honest about what's actually known versus what you're hoping for. Vague in, vague out. And a context file that tries to cover everything ends up covering nothing properly.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Continue reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/kavyarani7/the-skills-file-pattern-2oam"&gt;Part 2: The Skills File Pattern&lt;/a&gt; — fixing CLAUDE.md bloat with imports.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.to/kavyarani7/picking-models-and-tools-gmn"&gt;Part 3: Picking Models and Tools&lt;/a&gt; — the MCPs we tried, refused, and why.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>claude</category>
      <category>ai</category>
      <category>devtools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Auditing Kubernetes Manifests With AI: A Practical Workflow</title>
      <dc:creator>James Joyner</dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:31:15 +0000</pubDate>
      <link>https://dev.to/devopsaitoolkit/auditing-kubernetes-manifests-with-ai-a-practical-workflow-4368</link>
      <guid>https://dev.to/devopsaitoolkit/auditing-kubernetes-manifests-with-ai-a-practical-workflow-4368</guid>
      <description>&lt;p&gt;A senior K8s engineer I work with audits manifests faster than I read them. He's seen so many patterns that "missing readinessProbe on a Deployment that takes 45 seconds to start" jumps off the page. Most of us don't have that pattern library memorized — and increasingly, we don't need to. AI assistants have read more Kubernetes manifests than any human ever will.&lt;/p&gt;

&lt;p&gt;The catch: a generic "review this YAML" prompt produces generic noise. You need to direct the model toward the categories of issues that actually matter in your environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two mistakes everyone makes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Mistake 1: Asking for "a security review."&lt;/strong&gt; You'll get a bullet list of every possible concern, ranked alphabetically, with no signal about which matter. You'll skim, dismiss, and learn nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mistake 2: Pasting one manifest.&lt;/strong&gt; Real Kubernetes problems live in the interaction between resources — a Deployment's readiness probe and a Service's selector, a NetworkPolicy and the actual app traffic. One YAML in isolation hides most of the bugs.&lt;/p&gt;

&lt;p&gt;The fix for both is the same: give the model a &lt;em&gt;bounded scope&lt;/em&gt; and &lt;em&gt;enough context&lt;/em&gt; to reason about interactions.&lt;/p&gt;

&lt;h2&gt;
  
  
  A workflow that works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Pick the audit dimension
&lt;/h3&gt;

&lt;p&gt;Pre-decide what you're checking &lt;em&gt;for&lt;/em&gt;. Different prompts for different dimensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resource limits &amp;amp; QoS&lt;/strong&gt; — are requests/limits set, does QoS match intent, are limits realistic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Probes &amp;amp; lifecycle&lt;/strong&gt; — readiness, liveness, startup, preStop, terminationGracePeriodSeconds&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security context&lt;/strong&gt; — runAsNonRoot, capabilities, readOnlyRootFilesystem, seccomp&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network exposure&lt;/strong&gt; — NetworkPolicy, Service type, Ingress rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability&lt;/strong&gt; — PodDisruptionBudget, topology spread, replica count&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State &amp;amp; storage&lt;/strong&gt; — PVC access modes, retention policies, backup tags&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mixing dimensions in one review produces wishy-washy output. Pick one, get a clean answer, move on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Paste the manifest + related context
&lt;/h3&gt;

&lt;p&gt;For a workload review, paste:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The Deployment / StatefulSet / DaemonSet&lt;/li&gt;
&lt;li&gt;Its Service(s) and Ingress&lt;/li&gt;
&lt;li&gt;Any NetworkPolicies that match its labels&lt;/li&gt;
&lt;li&gt;The HPA if relevant&lt;/li&gt;
&lt;li&gt;The ConfigMaps and Secrets it references (sanitize first)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For YAML this is usually under 500 lines, well within any model's context window. The model can now reason about interactions, not just isolated fields.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Use a directive prompt
&lt;/h3&gt;

&lt;p&gt;The big difference between "tell me about this YAML" and a useful review is &lt;em&gt;the instruction format&lt;/em&gt;. Compare:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Review this Kubernetes manifest.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;versus:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You are reviewing a production Deployment + Service + NetworkPolicy bundle. For each finding, give: (1) severity (critical/high/medium/low), (2) the exact field path that's wrong, (3) one sentence on why it matters, (4) the corrected YAML snippet. Focus only on probes, lifecycle, and graceful shutdown. Ignore documentation/comments.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first prompt produces an essay. The second produces a list of fixable issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Verify before applying
&lt;/h3&gt;

&lt;p&gt;This is where most reviews go wrong. The model is right &lt;em&gt;most of the time&lt;/em&gt;. It's wrong some of the time, often in ways that look correct.&lt;/p&gt;

&lt;p&gt;Common AI failure modes in K8s review:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hallucinated field names&lt;/strong&gt; — &lt;code&gt;spec.template.spec.terminationGracePeriod&lt;/code&gt; (it's &lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Outdated API versions&lt;/strong&gt; — &lt;code&gt;policy/v1beta1 PodDisruptionBudget&lt;/code&gt; (removed in 1.25)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrong defaults claimed&lt;/strong&gt; — claiming &lt;code&gt;failureThreshold&lt;/code&gt; defaults to 1 when it's 3&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Misreading the use case&lt;/strong&gt; — recommending &lt;code&gt;runAsNonRoot: true&lt;/code&gt; for a workload that legitimately needs root&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For every "fix" the model suggests, glance at the official K8s docs for that field. This adds 30 seconds per finding and catches the wrong ones. Without this step, you will apply changes that break things.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real example
&lt;/h2&gt;

&lt;p&gt;Here's a Deployment I reviewed last week:&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;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;apps/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deployment&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;payments&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;matchLabels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;payments&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;payments&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
    &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.example.com/payments:v3.1.0&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;containerPort&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DB_URL&lt;/span&gt;
          &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://payments-db:5432/payments&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2"&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2Gi"&lt;/span&gt;
        &lt;span class="na"&gt;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;httpGet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;/healthz&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;8080&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
          &lt;span class="na"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I asked Claude to review for probes and graceful shutdown only. The findings:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;No &lt;code&gt;requests&lt;/code&gt;, only &lt;code&gt;limits&lt;/code&gt;&lt;/strong&gt; → pod gets &lt;code&gt;BestEffort&lt;/code&gt; QoS, first to be evicted under pressure. Set requests equal to or below limits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;initialDelaySeconds: 5&lt;/code&gt;&lt;/strong&gt; → Java/Spring apps typically need 30-90 seconds to start. Add &lt;code&gt;startupProbe&lt;/code&gt; with longer threshold.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No &lt;code&gt;livenessProbe&lt;/code&gt;&lt;/strong&gt; → kubelet won't restart if the app deadlocks. Mirror readinessProbe with looser thresholds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No &lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt;&lt;/strong&gt; → defaults to 30s; for a payment service with in-flight requests, this is borderline. Set to 60s.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No &lt;code&gt;preStop&lt;/code&gt; hook&lt;/strong&gt; → SIGTERM hits immediately; load balancers may still send traffic for ~10s after pod marked Terminating. Add &lt;code&gt;sleep 15&lt;/code&gt; preStop.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All five were real, all five were fixable in two minutes of YAML editing. The model didn't tell me about anything irrelevant. That's because I scoped the prompt to "probes and graceful shutdown only."&lt;/p&gt;

&lt;p&gt;The big one — #5 — is something I've personally been bitten by twice. The model wouldn't have prioritized it without the directive prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  What about Kyverno / OPA / Pod Security Admission?
&lt;/h2&gt;

&lt;p&gt;Yes, you should run those too. They catch consistent issues at admission time. They don't catch issues that require &lt;em&gt;judgment&lt;/em&gt;: "is 30 seconds enough graceful shutdown for this specific service?" Policy enforcement is a floor; AI review is a directed second opinion above that floor.&lt;/p&gt;

&lt;p&gt;I run both. Kyverno catches "no securityContext at all" before it ever lands. AI review catches "readinessProbe path doesn't match what the app exposes" — something only a human (or an AI imitating one) would notice.&lt;/p&gt;

&lt;h2&gt;
  
  
  A starter prompt
&lt;/h2&gt;

&lt;p&gt;If you want a template, here's the one I use most:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You are reviewing a Kubernetes workload bundle for production readiness. Focus only on: probes (readiness, liveness, startup), &lt;code&gt;terminationGracePeriodSeconds&lt;/code&gt;, preStop hooks, and rolling update strategy. For each finding produce: severity, exact field path, why it matters in one sentence, corrected YAML. Ignore everything else (security context, network policies, resource limits — those are separate reviews). The workload is [serves HTTP at /api on port 8080 / consumes from a queue / batch processor that runs N hours].&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The bracketed context at the end is what makes the review accurate for &lt;em&gt;your&lt;/em&gt; workload. Without it, the model assumes a generic web service.&lt;/p&gt;

&lt;p&gt;For our full prompt library on Kubernetes review, see the &lt;a href="https://dev.to/categories/kubernetes-helm/"&gt;Kubernetes &amp;amp; Helm category&lt;/a&gt; — especially &lt;a href="https://dev.to/prompts/kubernetes-yaml-security-review/"&gt;kubernetes-yaml-security-review&lt;/a&gt; and &lt;a href="https://dev.to/prompts/kubernetes-resource-limits-tuning/"&gt;kubernetes-resource-limits-tuning&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This article was originally published on &lt;a href="https://devopsaitoolkit.com/blog/auditing-kubernetes-manifests-with-ai/" rel="noopener noreferrer"&gt;DevOps AI ToolKit&lt;/a&gt; — practical AI workflows for cloud engineers.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>kubernetes</category>
      <category>yaml</category>
      <category>security</category>
    </item>
    <item>
      <title>Using ChatGPT Custom Connectors With a Remote MCP Form Server</title>
      <dc:creator>Lovanaut </dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:28:27 +0000</pubDate>
      <link>https://dev.to/lovanaut55/using-chatgpt-custom-connectors-with-a-remote-mcp-form-server-24h5</link>
      <guid>https://dev.to/lovanaut55/using-chatgpt-custom-connectors-with-a-remote-mcp-form-server-24h5</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpl9j9ahwuxcw5tw82ggh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpl9j9ahwuxcw5tw82ggh.png" alt="ChatGPT connected to a remote MCP form server that creates a draft form and returns a preview URL." width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ChatGPT can suggest form fields in seconds.&lt;/p&gt;

&lt;p&gt;That is useful, but it still leaves a gap:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Ask ChatGPT for fields
Open a form builder
Create the form manually
Copy labels
Configure response emails
Preview
Fix issues
Publish
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a remote MCP server, ChatGPT can move one layer deeper. Instead of only describing the form, it can call tools that create and operate the form.&lt;/p&gt;

&lt;p&gt;This post walks through a narrow version of that setup using FORMLOVA as the remote MCP form server. The goal is not to explain every FORMLOVA feature. The goal is to show the practical connection path: ChatGPT developer mode, a remote MCP URL, OAuth, a first draft form, and the safety boundaries you should keep in place.&lt;/p&gt;

&lt;p&gt;OpenAI's current help content may call this area "developer mode", "MCP apps", "custom apps", or formerly "custom connectors". The naming can move. The underlying shape is the same: ChatGPT can connect to an external MCP server and call approved tools.&lt;/p&gt;

&lt;p&gt;Reference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://help.openai.com/en/articles/12584461-developer-mode-and-mcp-apps-in-chatgpt" rel="noopener noreferrer"&gt;OpenAI Help: Developer mode and MCP apps in ChatGPT&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://help.openai.com/en/articles/11487775-connectors-in-chatgpt" rel="noopener noreferrer"&gt;OpenAI Help: Apps in ChatGPT&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What We Are Connecting
&lt;/h2&gt;

&lt;p&gt;The server URL for FORMLOVA is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://formlova.com/api/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is a remote MCP server. You do not install a local package, run a process on your laptop, or paste a shared API key into a config file.&lt;/p&gt;

&lt;p&gt;The connection flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ChatGPT
  -&amp;gt; remote MCP server URL
  -&amp;gt; OAuth
  -&amp;gt; FORMLOVA tools
  -&amp;gt; draft form / preview / review / operations
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OAuth matters because a form system is not a read-only documentation source. It can create forms, inspect submissions, configure emails, and eventually operate workflows. The server needs to know which FORMLOVA user is acting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;You need:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ChatGPT developer mode / MCP app access&lt;/td&gt;
&lt;td&gt;Availability depends on plan and workspace settings.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A FORMLOVA account&lt;/td&gt;
&lt;td&gt;The free plan is enough for the first connection test.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A browser session&lt;/td&gt;
&lt;td&gt;OAuth redirects through the browser.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remote MCP URL&lt;/td&gt;
&lt;td&gt;&lt;code&gt;https://formlova.com/api/mcp&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you are in a workspace plan, an admin may need to allow developer mode or custom apps before you can create an MCP app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Create The App In ChatGPT
&lt;/h2&gt;

&lt;p&gt;In ChatGPT settings, look for the area related to apps, connectors, or developer mode. The labels may differ by account type, but the fields you are looking for are roughly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name: FORMLOVA
Description: Create and operate forms from ChatGPT
MCP server URL: https://formlova.com/api/mcp
Authentication: OAuth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the exact domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://formlova.com/api/mcp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not add a random server URL you found in a thread or snippet. MCP apps can expose tools that perform write actions. Treat the server URL like an application you are granting permission to operate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Complete OAuth
&lt;/h2&gt;

&lt;p&gt;After creating the app, ChatGPT should send you through the FORMLOVA authorization flow.&lt;/p&gt;

&lt;p&gt;The rough flow is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ChatGPT settings
  -&amp;gt; create MCP app
  -&amp;gt; FORMLOVA login / consent
  -&amp;gt; return to ChatGPT
  -&amp;gt; app connected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the connection appears successful but tools do not run, check whether the app is enabled in the specific chat. Connecting an MCP app in settings and selecting it in a conversation can be two separate steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Create A Draft Form
&lt;/h2&gt;

&lt;p&gt;Start with a low-risk draft. Do not begin by publishing or sending emails.&lt;/p&gt;

&lt;p&gt;Example prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use FORMLOVA to create a draft registration form for an internal engineering meetup.
Ask for name, email, team, attendance type, dietary restrictions, and one question for the speaker.
Keep it unpublished and return the preview URL.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The expected result is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;draft form created
preview URL returned
no public publish action yet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the right first test because it proves that ChatGPT can call the remote MCP server without immediately creating public side effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Better Test Prompt
&lt;/h2&gt;

&lt;p&gt;The first prompt can be short, but a realistic prompt should include the workflow after submission.&lt;/p&gt;

&lt;p&gt;Forms are rarely just fields. They are the beginning of an operational process.&lt;/p&gt;

&lt;p&gt;Here is a better test prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use FORMLOVA to create a draft contact form for a B2B SaaS website.

Required fields:
- name
- work email
- company
- inquiry type
- message

Optional fields:
- company size
- current tool
- expected timeline

After submission, the team wants to route pricing inquiries faster, ignore obvious sales pitches, and send a confirmation email that sets a realistic response-time expectation.

Keep the form unpublished.
Return the preview URL.
After creating it, review the form for mobile input friction, unnecessary required fields, and response-management gaps.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prompt gives the model three kinds of information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;field requirements
operational intent
safety boundary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The safety boundary matters. "Keep it unpublished" and "return the preview URL" make the first test observable without turning it into a production action.&lt;/p&gt;

&lt;h2&gt;
  
  
  How To Tell If The MCP App Actually Ran
&lt;/h2&gt;

&lt;p&gt;A weak test is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a contact form.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ChatGPT can answer that without using any tool. It can produce a nice Markdown list of fields and still never touch the remote MCP server.&lt;/p&gt;

&lt;p&gt;For a real test, verify the artifacts:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Preview URL exists&lt;/td&gt;
&lt;td&gt;Proves a draft was created outside the chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Form is unpublished&lt;/td&gt;
&lt;td&gt;Confirms the write action was bounded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fields match the prompt&lt;/td&gt;
&lt;td&gt;Confirms the tool call used the requested schema&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mobile-specific review exists&lt;/td&gt;
&lt;td&gt;Confirms the model did more than generate labels&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Follow-up operations are identified&lt;/td&gt;
&lt;td&gt;Confirms the form is treated as intake, not a static page&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I also like asking the model to summarize what it did:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Summarize the FORMLOVA actions you took.
Include the preview URL, publish status, and any review findings.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That response is not the source of truth by itself. The preview URL and the server-side draft are the evidence. But the summary helps you catch when the model is pretending to operate while only writing advice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Ask For A Review Before Publishing
&lt;/h2&gt;

&lt;p&gt;Once the draft exists, ask for a review pass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review this form before publishing.
Check whether it is clear on mobile, whether any required fields are unnecessary, and whether the confirmation message sets the right expectation.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For form software, this step matters more than the initial generation. AI-generated form fields are usually plausible. The operational mistakes come later: too many required fields, weak confirmation text, unclear response handling, missing status columns, or a form that is painful on mobile.&lt;/p&gt;

&lt;h2&gt;
  
  
  What The Review Should Catch
&lt;/h2&gt;

&lt;p&gt;A useful form review should not only say "looks good."&lt;/p&gt;

&lt;p&gt;It should catch issues like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The phone field is optional, but the follow-up workflow assumes phone calls.
The inquiry type is free text, which makes routing harder.
The form asks for budget too early, increasing friction.
The confirmation message says "we will reply soon" but does not set a response-time expectation.
The form is long on mobile and should split contact details from request details.
The message field is required but has no guidance about what to include.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a contact form, the review should connect form fields to response operations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Form decision&lt;/th&gt;
&lt;th&gt;Operational consequence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Inquiry type as select&lt;/td&gt;
&lt;td&gt;Enables routing and filtering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Work email required&lt;/td&gt;
&lt;td&gt;Reduces low-quality consumer traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Timeline optional&lt;/td&gt;
&lt;td&gt;Helps prioritize without blocking submission&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Confirmation message&lt;/td&gt;
&lt;td&gt;Sets expectations and reduces duplicate submissions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sales-pitch filtering&lt;/td&gt;
&lt;td&gt;Keeps real inquiries visible&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This is where MCP-backed form software becomes more useful than a static AI answer. The same conversation can move from field creation to response workflow decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Keep Write Actions Bounded
&lt;/h2&gt;

&lt;p&gt;MCP gives ChatGPT a tool surface. That is powerful, but it should not remove product safety.&lt;/p&gt;

&lt;p&gt;I would separate early usage into these tiers:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tier&lt;/th&gt;
&lt;th&gt;Examples&lt;/th&gt;
&lt;th&gt;Risk&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;list forms, inspect draft, get preview&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Draft write&lt;/td&gt;
&lt;td&gt;create unpublished form, edit labels&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Publish&lt;/td&gt;
&lt;td&gt;make a form public, change public copy&lt;/td&gt;
&lt;td&gt;Higher&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;External effects&lt;/td&gt;
&lt;td&gt;send emails, trigger reminders, export data&lt;/td&gt;
&lt;td&gt;Highest&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For the first session, stay in the first two tiers. Create a draft, preview it, edit it, review it. Publish only after the generated result has been inspected.&lt;/p&gt;

&lt;p&gt;This is also why a form MCP server should expose domain-level operations rather than one giant "do anything" endpoint. A tool named &lt;code&gt;create_draft_form&lt;/code&gt; is easier to reason about than a generic &lt;code&gt;mutate&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;If ChatGPT does not call the server, try making the target app explicit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Use the FORMLOVA MCP app to create this draft form.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If OAuth loops, check browser blockers, workspace permissions, and whether you are logged into the expected FORMLOVA account.&lt;/p&gt;

&lt;p&gt;If the app connects but no tools appear, check whether developer mode is enabled for your account or workspace and whether the current chat has the app selected.&lt;/p&gt;

&lt;p&gt;If the model tries to answer with a static checklist instead of operating the tool, ask for a concrete action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create the draft in FORMLOVA and return the preview URL.
Do not just suggest fields.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When This Pattern Is Worth Using
&lt;/h2&gt;

&lt;p&gt;This setup is not necessary for every form.&lt;/p&gt;

&lt;p&gt;If you only need a one-off internal poll, a static prompt and a simple form builder may be enough.&lt;/p&gt;

&lt;p&gt;The remote MCP pattern becomes more valuable when the form has operational weight:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;responses need triage
emails need different wording by answer
submissions have statuses
follow-up needs timing
spam or sales pitches need filtering
forms are revised repeatedly
multiple people inspect the same intake queue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In those cases, ChatGPT is not just generating a form. It is becoming the operating surface for the workflow around the form.&lt;/p&gt;

&lt;p&gt;That is the real reason to connect a remote MCP form server.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Post Is Not
&lt;/h2&gt;

&lt;p&gt;This is not a tutorial on building an MCP server from scratch. It is not a full Apps SDK UI guide. It also does not cover Claude, Cursor, or Gemini CLI setup.&lt;/p&gt;

&lt;p&gt;The narrower point is that ChatGPT can be used as an operating surface for a remote form system when developer mode and MCP apps are available.&lt;/p&gt;

&lt;p&gt;The full FORMLOVA guide covers the longer path: setup, OAuth, preview, publishing, response management, email operations, workflows, and safety boundaries.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://formlova.com/en/blog/chatgpt-formlova-guide-en?utm_source=devto&amp;amp;utm_medium=external_article&amp;amp;utm_campaign=external_068_072_wave1" rel="noopener noreferrer"&gt;Read the full FORMLOVA ChatGPT guide&lt;/a&gt;&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>mcp</category>
      <category>productivity</category>
      <category>forms</category>
    </item>
    <item>
      <title>The Day We Fixed Our Signup Pipeline</title>
      <dc:creator>Oge Obubu</dc:creator>
      <pubDate>Tue, 16 Jun 2026 04:23:48 +0000</pubDate>
      <link>https://dev.to/ogeobubu/the-day-we-fixed-our-signup-pipeline-3664</link>
      <guid>https://dev.to/ogeobubu/the-day-we-fixed-our-signup-pipeline-3664</guid>
      <description>&lt;p&gt;It started with a graph.&lt;/p&gt;

&lt;p&gt;Our signup numbers were climbing every week. The team was excited—growth was happening. But something felt off. A lot of those "users" never came back. Their email addresses looked strange. And our activation rate was quietly dropping.&lt;/p&gt;

&lt;p&gt;One Friday afternoon, I decided to dig into the data.&lt;/p&gt;

&lt;p&gt;What I found was not growth. It was noise.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Diagnosis&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I ran a simple query: group signups by IP address, count them, and sort descending.&lt;/p&gt;

&lt;p&gt;The top result: one IP address had registered hundreds of accounts in the past 24 hours.&lt;/p&gt;

&lt;p&gt;Same browser fingerprint. Same pattern. A script, probably hitting our register endpoint, generating accounts with throwaway email domains, and moving on.&lt;/p&gt;

&lt;p&gt;I checked the user agents. Tools, not people.&lt;/p&gt;

&lt;p&gt;I checked the email domains. Addresses that expire before the welcome email is even sent.&lt;/p&gt;

&lt;p&gt;Our signup pipeline was wide open. Anyone, or anything, could walk in.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We needed protection in layers. Not a single gate, but a series of filters. Each one catches something the others might miss.&lt;/p&gt;

&lt;p&gt;We decided on three layers. And we built them all in a single sprint.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Layer 1: Throttling&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most obvious fix: rate limiting. But not just one limit, two.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-IP throttling:&lt;/strong&gt; A small number of signup attempts per IP address within a short window. If you hit the limit, you get a clear response telling you when to retry.&lt;/p&gt;

&lt;p&gt;No ambiguity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Per-domain throttling:&lt;/strong&gt; An even smaller tolerance for signups from the same email domain within a longer window. This catches distributed attacks, a bot using different IPs but always the same throwaway domain.&lt;/p&gt;

&lt;p&gt;Two counters. A bot that shares either an IP or a domain gets stopped.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Layer 2: Blocklists&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rate limiting slows things down, but it doesn't stop determined attackers. They'll wait and come back. We needed permanent blocks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blocked email domains:&lt;/strong&gt; We maintain a list of disposable email domains. Any registration attempt using one gets rejected before the data touches the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blocked user agents:&lt;/strong&gt; If the request comes from a non-browser tool, it's rejected with a simple error. No explanation. No details. Attackers don't need to know why they were blocked.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Layer 3: IP Blocklist&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some IPs are persistent. They've been flagged before. They've abused other endpoints, not just signup. Rate limiting won't stop them. Blocking their user agent won't stop them if they switch.&lt;/p&gt;

&lt;p&gt;We needed a hard blocklist, IPs that are rejected for every request, not just registration.&lt;/p&gt;

&lt;p&gt;The message is deliberately terse. No explanation. No recourse. The middleware runs on every route, every method, every request. If your IP is on the list, you don't get through.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Before and After&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hundreds of accounts from a single IP in 24 hours&lt;/li&gt;
&lt;li&gt;Throwaway email domains made up a significant percentage of new signups&lt;/li&gt;
&lt;li&gt;Activation rate was diluted by fake accounts&lt;/li&gt;
&lt;li&gt;Every data-driven decision was slightly wrong because the denominator was inflated&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Same-IP registration farming dropped to zero&lt;/li&gt;
&lt;li&gt;Disposable domain signups disappeared&lt;/li&gt;
&lt;li&gt;Our signup numbers reflected actual human intent&lt;/li&gt;
&lt;li&gt;Activation rate recovered, because the denominator now represented real users&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;What We Learned&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Signal matters more than volume.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When signups are inflated by bots, every metric that depends on user count becomes unreliable. Activation rate. Retention. Revenue per user. Cleaning the pipeline made every downstream number trustworthy again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. A small amount of code can fix a big problem.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The solution wasn't complex. It was layered. Three simple mechanisms, each handling what the others miss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Layers matter.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A single rate limit would have slowed the bots but not stopped them. A single blocklist would have caught the obvious ones but missed the persistent ones. The combination covers more surface area than any one approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Not all rejections are equal.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We chose our responses carefully. Legitimate users get useful feedback. Bad actors get silence. The message you send reveals how much information you want the requester to have.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;The Takeaway&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A healthy signup pipeline doesn't make the news. Nobody sends a tweet saying "I tried to sign up and couldn't because the IP throttling worked correctly."&lt;/p&gt;

&lt;p&gt;But the absence of this work would eventually make headlines, just not the kind you want.&lt;/p&gt;

&lt;p&gt;Growth is not just about getting users in the door. It's about making sure the users who come through are real. Every decision you make, product direction, marketing spend, feature priorities, depends on the quality of your data. And your data quality starts at the first line of defense: the registration endpoint.&lt;/p&gt;

&lt;p&gt;The bots stopped. The data cleaned up. And our growth metrics finally told the truth.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>devops</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
