<?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: DatanestDigital</title>
    <description>The latest articles on DEV Community by DatanestDigital (@datanestdigital).</description>
    <link>https://dev.to/datanestdigital</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3835307%2Fc0f62441-93d5-47d0-8c94-149af7c53177.png</url>
      <title>DEV Community: DatanestDigital</title>
      <link>https://dev.to/datanestdigital</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/datanestdigital"/>
    <language>en</language>
    <item>
      <title>Social Media Content Kit</title>
      <dc:creator>DatanestDigital</dc:creator>
      <pubDate>Thu, 25 Jun 2026 12:54:51 +0000</pubDate>
      <link>https://dev.to/datanestdigital/social-media-content-kit-1j3a</link>
      <guid>https://dev.to/datanestdigital/social-media-content-kit-1j3a</guid>
      <description>&lt;h1&gt;
  
  
  Social Media Content Kit
&lt;/h1&gt;

&lt;p&gt;200+ social post templates for Twitter/X, LinkedIn, Instagram, and Threads. Includes fill-in-the-blank frameworks organized by goal (engagement, authority, promotion, storytelling), hashtag strategy guides, scheduling systems, and an engagement playbook. Never run out of content ideas again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;200+ post templates&lt;/strong&gt; across 4 platforms, organized by content goal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform-specific formatting&lt;/strong&gt; — character limits, line breaks, and hashtag rules per network&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Content calendar integration&lt;/strong&gt; — 30-day posting schedules for each platform&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hashtag strategy guide&lt;/strong&gt; — research methods, volume tiers, and per-post limits&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Engagement playbook&lt;/strong&gt; — comment strategies, DM templates, and community-building tactics&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Analytics tracking template&lt;/strong&gt; — weekly metrics for reach, engagement rate, and follower growth&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's Included
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;social-media-content-kit/
├── templates/
│   ├── twitter/
│   │   ├── educational-threads.md      # Step-by-step, mental models, how-to
│   │   ├── storytelling-posts.md       # Personal stories, origin stories
│   │   ├── hot-takes-opinions.md       # Contrarian views, debates
│   │   ├── list-posts.md               # Numbered lists, resource compilations
│   │   ├── build-in-public.md          # Progress updates, transparency
│   │   ├── promotional-posts.md        # Product launches, value-first promo
│   │   └── engagement-posts.md         # Questions, polls, conversations
│   ├── linkedin/
│   │   ├── professional-posts.md       # Career insights, framework posts
│   │   └── story-posts.md              # Personal narratives, lessons
│   ├── threads/
│   │   └── conversation-starters.md    # Casual, authentic, community-focused
│   └── instagram/
│       ├── educational-carousels.md    # Carousel templates, step-by-step
│       ├── storytelling-posts.md       # Caption templates, narratives
│       └── engagement-promotional.md   # Growth, community, soft selling
├── guides/
│   ├── hook-formulas.md                # 50+ opening line formulas
│   ├── hashtag-strategy.md             # Platform-specific hashtag guide
│   ├── engagement-playbook.md          # Community growth tactics
│   └── content-calendar-strategy.md    # Content pillar planning
├── tools/
│   ├── posting-schedule.csv            # 4-week multi-platform calendar
│   ├── post-performance-tracker.csv    # Track what works
│   ├── hashtag-bank.csv                # Organized hashtag collections
│   └── content-ideas-bank.csv          # Idea capture system
├── examples/
│   ├── sample_twitter_week.md          # 7 days of Twitter content
│   └── sample_linkedin_week.md         # 7 days of LinkedIn content
├── README.md
└── LICENSE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Pick your &lt;strong&gt;primary platform&lt;/strong&gt; (start with one, expand later)&lt;/li&gt;
&lt;li&gt;Choose 5-7 templates from the matching template file&lt;/li&gt;
&lt;li&gt;Fill in placeholders with your content and expertise&lt;/li&gt;
&lt;li&gt;Schedule posts using the &lt;strong&gt;30-Day Calendar&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Apply the &lt;strong&gt;Engagement Playbook&lt;/strong&gt; daily (15 minutes)&lt;/li&gt;
&lt;li&gt;Track results weekly with the &lt;strong&gt;Analytics Tracker&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Template Categories
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Goal&lt;/th&gt;
&lt;th&gt;Templates&lt;/th&gt;
&lt;th&gt;Best Platforms&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authority&lt;/td&gt;
&lt;td&gt;Build credibility&lt;/td&gt;
&lt;td&gt;40+&lt;/td&gt;
&lt;td&gt;LinkedIn, Twitter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engagement&lt;/td&gt;
&lt;td&gt;Drive replies and shares&lt;/td&gt;
&lt;td&gt;50+&lt;/td&gt;
&lt;td&gt;Twitter, Threads&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Storytelling&lt;/td&gt;
&lt;td&gt;Build personal brand&lt;/td&gt;
&lt;td&gt;35+&lt;/td&gt;
&lt;td&gt;LinkedIn, Instagram&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Promotion&lt;/td&gt;
&lt;td&gt;Drive traffic/sales&lt;/td&gt;
&lt;td&gt;25+&lt;/td&gt;
&lt;td&gt;All platforms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Education&lt;/td&gt;
&lt;td&gt;Share expertise&lt;/td&gt;
&lt;td&gt;35+&lt;/td&gt;
&lt;td&gt;LinkedIn, Twitter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Community&lt;/td&gt;
&lt;td&gt;Start conversations&lt;/td&gt;
&lt;td&gt;20+&lt;/td&gt;
&lt;td&gt;Threads, Twitter&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Twitter/X Templates (Samples)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Authority Post
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;[TOPIC] isn't as complicated as people make it.

Here's the framework I use:

Step 1: [ACTION]
Step 2: [ACTION]
Step 3: [ACTION]

The key most people miss: [INSIGHT].

[OPTIONAL: thread continues below]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Engagement Post
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Hot take: [CONTRARIAN_OPINION about TOPIC].

[REASON_1 — one sentence]
[REASON_2 — one sentence]

Agree or disagree? Quote tweet with your take.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Thread Opener
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;I spent [TIME] [DOING_THING] and learned [NUMBER] lessons the hard way.

Here's what I wish someone told me on day 1:

Thread ↓
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  LinkedIn Templates (Samples)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Value Post
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;I've [DONE_THING] for [TIME_PERIOD].

Here are [NUMBER] things I'd do differently:
&lt;span class="p"&gt;
1.&lt;/span&gt; [LESSON] → [WHY_IT_MATTERS]
&lt;span class="p"&gt;2.&lt;/span&gt; [LESSON] → [WHY_IT_MATTERS]
&lt;span class="p"&gt;3.&lt;/span&gt; [LESSON] → [WHY_IT_MATTERS]

The biggest one? Number [X].

Because [EXPLANATION — 2-3 sentences].

What would you add to this list?

&lt;span class="gh"&gt;#[HASHTAG_1] #[HASHTAG_2] #[HASHTAG_3]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Story Post
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Last [TIMEFRAME], something happened that changed how I think about [TOPIC].

[STORY — 4-6 sentences, specific details, emotional arc]

The takeaway?

[LESSON — one clear sentence]

This applies to [BROADER_CONTEXT] too.

Have you experienced something similar? I'd love to hear your story.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  30-Day Posting Schedule (Twitter/X)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Day&lt;/th&gt;
&lt;th&gt;Post Type&lt;/th&gt;
&lt;th&gt;Template Category&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;Mon&lt;/td&gt;
&lt;td&gt;Thread (5-8 tweets)&lt;/td&gt;
&lt;td&gt;Education&lt;/td&gt;
&lt;td&gt;Deep-dive on one topic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tue&lt;/td&gt;
&lt;td&gt;Single tweet&lt;/td&gt;
&lt;td&gt;Engagement&lt;/td&gt;
&lt;td&gt;Question or hot take&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Wed&lt;/td&gt;
&lt;td&gt;Quote tweet + commentary&lt;/td&gt;
&lt;td&gt;Authority&lt;/td&gt;
&lt;td&gt;React to industry news&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thu&lt;/td&gt;
&lt;td&gt;Story tweet&lt;/td&gt;
&lt;td&gt;Storytelling&lt;/td&gt;
&lt;td&gt;Personal experience&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fri&lt;/td&gt;
&lt;td&gt;List tweet&lt;/td&gt;
&lt;td&gt;Education&lt;/td&gt;
&lt;td&gt;"[NUMBER] tools/tips for [TOPIC]"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sat&lt;/td&gt;
&lt;td&gt;Repost best performer&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Share your top tweet from the week&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sun&lt;/td&gt;
&lt;td&gt;Reflection / question&lt;/td&gt;
&lt;td&gt;Community&lt;/td&gt;
&lt;td&gt;Ask your audience something&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Repeat weekly. Rotate topics to avoid repetition.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Hashtag Quick Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Max Hashtags&lt;/th&gt;
&lt;th&gt;Best Practice&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Twitter/X&lt;/td&gt;
&lt;td&gt;2-3&lt;/td&gt;
&lt;td&gt;Less is more — 1-2 targeted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LinkedIn&lt;/td&gt;
&lt;td&gt;3-5&lt;/td&gt;
&lt;td&gt;Mix broad + niche, end of post&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Instagram&lt;/td&gt;
&lt;td&gt;5-15&lt;/td&gt;
&lt;td&gt;Mix size tiers, first comment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Threads&lt;/td&gt;
&lt;td&gt;0-2&lt;/td&gt;
&lt;td&gt;Very sparingly, inline only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Use 3 tiers: &lt;strong&gt;Large&lt;/strong&gt; (100K+ posts, broad reach), &lt;strong&gt;Medium&lt;/strong&gt; (10K-100K, niche), &lt;strong&gt;Small&lt;/strong&gt; (1K-10K, targeted). Mix tiers per post for optimal reach vs. discoverability.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Batch-create a week of content in 60-90 minutes.&lt;/strong&gt; Fill 7 templates, schedule, and forget.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customize every template.&lt;/strong&gt; The framework is the skeleton — your voice and expertise are the flesh.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post consistently before posting frequently.&lt;/strong&gt; 3 great posts/week beats 3 mediocre posts/day.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Engage 15 minutes before and after posting.&lt;/strong&gt; The algorithm rewards active users.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Lead with value, not promotion.&lt;/strong&gt; Follow the 80/20 rule — 80% value, 20% promotional.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use the first line as a hook.&lt;/strong&gt; On every platform, you have 1-2 seconds to stop the scroll.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repurpose across platforms but reformat.&lt;/strong&gt; A LinkedIn post is not a tweet. Adapt the template per platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Track your top 5 posts monthly.&lt;/strong&gt; Identify what content type and topic resonates — double down.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Engage on others' posts as much as you create.&lt;/strong&gt; Commenting builds relationships that sharing alone can't.&lt;/li&gt;
&lt;/ol&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is 1 of 11 resources in the &lt;a href="https://datanest-stores.pages.dev/content-creator/" rel="noopener noreferrer"&gt;Content Creator Toolkit&lt;/a&gt; toolkit.&lt;/strong&gt; Get the complete [Social Media Content Kit] with all files, templates, and documentation for $25.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://buy.stripe.com/14A5kD9uTc98dNEdSFgjG3P" rel="noopener noreferrer"&gt;Get the Full Kit →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Or grab the entire Content Creator Toolkit bundle (11 products) for $129 — save 30%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://buy.stripe.com/6oUdR95eD7SSeRI3e1gjG3W" rel="noopener noreferrer"&gt;Get the Complete Bundle →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/datanestdigital/technical-blog-post-templates-4mol"&gt;Technical Blog Post Templates&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/datanestdigital/content-repurposing-system-46lb"&gt;Content Repurposing System&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/datanestdigital/online-course-creation-framework-2g8l"&gt;Online Course Creation Framework&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>writing</category>
      <category>marketing</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Choosing a Vector Database in 2026: pgvector vs Pinecone vs Qdrant vs Weaviate</title>
      <dc:creator>DatanestDigital</dc:creator>
      <pubDate>Sat, 20 Jun 2026 11:30:48 +0000</pubDate>
      <link>https://dev.to/datanestdigital/choosing-a-vector-database-in-2026-pgvector-vs-pinecone-vs-qdrant-vs-weaviate-lef</link>
      <guid>https://dev.to/datanestdigital/choosing-a-vector-database-in-2026-pgvector-vs-pinecone-vs-qdrant-vs-weaviate-lef</guid>
      <description>&lt;p&gt;Once your RAG prototype works, the next decision has real consequences: where do the embeddings live? Pick wrong and you either over-pay for a managed service you didn't need, or you bolt a vector index onto a database that buckles at scale. This is the comparison I wish existed when I had to choose.&lt;/p&gt;

&lt;p&gt;We'll look at four representative options — &lt;strong&gt;pgvector&lt;/strong&gt;, &lt;strong&gt;Pinecone&lt;/strong&gt;, &lt;strong&gt;Qdrant&lt;/strong&gt;, and &lt;strong&gt;Weaviate&lt;/strong&gt; — and, more usefully, the questions that should drive the decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, do you even need a dedicated vector database?
&lt;/h2&gt;

&lt;p&gt;If you have under a few hundred thousand vectors and already run Postgres, the answer is often &lt;strong&gt;no&lt;/strong&gt;. &lt;code&gt;pgvector&lt;/code&gt; adds vector search to a database you already operate, back up, and trust.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;bigserial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;vector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1536&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;hnsw&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="n"&gt;vector_cosine_ops&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;docs&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'[...]'&lt;/span&gt;   &lt;span class="c1"&gt;-- cosine distance&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;One datastore, transactional consistency with your real data, no new service.&lt;/strong&gt; The trade-off is that very large or very high-QPS workloads will eventually outgrow it, and its filtering/index tuning is less specialized than purpose-built engines.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four, at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;pgvector&lt;/th&gt;
&lt;th&gt;Pinecone&lt;/th&gt;
&lt;th&gt;Qdrant&lt;/th&gt;
&lt;th&gt;Weaviate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;Postgres extension&lt;/td&gt;
&lt;td&gt;fully managed SaaS&lt;/td&gt;
&lt;td&gt;open-source + managed&lt;/td&gt;
&lt;td&gt;open-source + managed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run it yourself&lt;/td&gt;
&lt;td&gt;yes (it's Postgres)&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best when&lt;/td&gt;
&lt;td&gt;already on Postgres, modest scale&lt;/td&gt;
&lt;td&gt;want zero ops, fast scale&lt;/td&gt;
&lt;td&gt;want control + strong filtering&lt;/td&gt;
&lt;td&gt;want built-in hybrid/modules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metadata filtering&lt;/td&gt;
&lt;td&gt;SQL &lt;code&gt;WHERE&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;good&lt;/td&gt;
&lt;td&gt;excellent&lt;/td&gt;
&lt;td&gt;good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ops burden&lt;/td&gt;
&lt;td&gt;low (existing DB)&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;medium&lt;/td&gt;
&lt;td&gt;medium&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Pinecone — pay to not think about infrastructure
&lt;/h2&gt;

&lt;p&gt;Pinecone is fully managed: no servers, no index tuning, scales horizontally on demand. You trade money and vendor lock-in for never paging anyone about your vector store.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose it when&lt;/strong&gt; ops time is more expensive than service fees, you need to scale fast, and you don't want to own infrastructure. &lt;strong&gt;Be wary when&lt;/strong&gt; cost at scale matters or you need to self-host for data-residency reasons.&lt;/p&gt;

&lt;h2&gt;
  
  
  Qdrant — open-source with serious filtering
&lt;/h2&gt;

&lt;p&gt;Qdrant is a purpose-built vector engine (Rust) with excellent &lt;strong&gt;payload filtering&lt;/strong&gt; — combining vector similarity with structured conditions efficiently, which is exactly what production RAG needs ("similar chunks, but only from this tenant, in the last 90 days").&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;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;collection_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;docs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;query_vector&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;query_filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;must&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tenant&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;match&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;value&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;acme&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}}]},&lt;/span&gt;
    &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&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;&lt;strong&gt;Choose it when&lt;/strong&gt; you want to self-host, need strong metadata filtering, and want a managed option available later. Run it in Docker for dev, scale to a cluster for prod.&lt;/p&gt;

&lt;h2&gt;
  
  
  Weaviate — batteries-included with hybrid search
&lt;/h2&gt;

&lt;p&gt;Weaviate bundles more out of the box: built-in &lt;strong&gt;hybrid search&lt;/strong&gt; (combining keyword BM25 with vector similarity) and optional modules that generate embeddings for you. It's the "more than a vector store" option.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Choose it when&lt;/strong&gt; you want hybrid search without wiring it yourself, or want the database to handle vectorization. &lt;strong&gt;Be wary when&lt;/strong&gt; you prefer a minimal, do-one-thing component you fully control.&lt;/p&gt;

&lt;h2&gt;
  
  
  The questions that actually decide it
&lt;/h2&gt;

&lt;p&gt;Forget feature checklists; answer these:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Scale.&lt;/strong&gt; Under ~1M vectors and modest QPS? pgvector likely wins on simplicity. Tens of millions and growing? Look at the purpose-built engines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ops appetite.&lt;/strong&gt; Want zero infrastructure? Pinecone (or a managed Qdrant/Weaviate). Happy to run a container? Self-host and save.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filtering.&lt;/strong&gt; If most queries are "similar &lt;em&gt;and&lt;/em&gt; matching these attributes," weight filtering quality heavily — Qdrant is strong here, and pgvector gets full SQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data gravity.&lt;/strong&gt; If your source data is already in Postgres, keeping vectors there removes a sync pipeline and a consistency headache.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hybrid search.&lt;/strong&gt; Need keyword + semantic together? Weaviate gives it natively; others need extra work.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  My default recommendation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Start with pgvector.&lt;/strong&gt; Most teams over-engineer this decision and reach for a distributed vector database to store 50,000 chunks. Ship on Postgres, measure real query latency and recall, and migrate to a dedicated engine &lt;em&gt;only&lt;/em&gt; when you have evidence you've outgrown it. The migration is straightforward once you know your actual access patterns — and you'll choose far better with real numbers than with a benchmark blog post.&lt;/p&gt;

&lt;p&gt;If you'd rather skip the boilerplate of wiring up embeddings, indexing, and a swappable retrieval layer, the &lt;a href="https://datanest-stores.pages.dev/ai-llm-toolkit/preview-vector-database-toolkit.html" rel="noopener noreferrer"&gt;Vector Database Toolkit&lt;/a&gt; provides adapters and ingestion patterns across these backends so you can prototype on pgvector and switch engines later without rewriting your app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;There's no universally best vector database — there's the one that matches your scale, ops appetite, and filtering needs. Resist premature optimization: begin with the simplest thing that could work (often pgvector), instrument it, and let real usage, not hype, tell you when to graduate.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>database</category>
      <category>machinelearning</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Stop Hardcoding Secrets: A Practical Secrets-Management Guide (2026)</title>
      <dc:creator>DatanestDigital</dc:creator>
      <pubDate>Sat, 20 Jun 2026 11:30:12 +0000</pubDate>
      <link>https://dev.to/datanestdigital/stop-hardcoding-secrets-a-practical-secrets-management-guide-2026-43n6</link>
      <guid>https://dev.to/datanestdigital/stop-hardcoding-secrets-a-practical-secrets-management-guide-2026-43n6</guid>
      <description>&lt;p&gt;Every secret leak starts the same way: someone, under deadline pressure, pastes an API key directly into the code "just to test it," and forgets. Six months later it's in your git history, on a laptop backup, and in a screenshot in a Slack thread. By the time you notice, it has been public for weeks.&lt;/p&gt;

&lt;p&gt;Secrets management isn't a product you buy once — it's a set of habits that make leaking a secret hard and recovering from one fast. Here's the practical version.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one rule: secrets never touch source code
&lt;/h2&gt;

&lt;p&gt;If a credential is in a file you commit, it's compromised the moment that repo is cloned, forked, or leaked — and rewriting git history rarely fully erases it. So the entire game is keeping secrets &lt;em&gt;out&lt;/em&gt; of the code and &lt;em&gt;out&lt;/em&gt; of the repo.&lt;/p&gt;

&lt;p&gt;Start with a &lt;code&gt;.gitignore&lt;/code&gt; and a &lt;code&gt;.dockerignore&lt;/code&gt; that exclude them, and commit a &lt;code&gt;.env.example&lt;/code&gt; with empty placeholders so contributors know what's needed without seeing real values.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# .gitignore
&lt;/span&gt;.&lt;span class="n"&gt;env&lt;/span&gt;
*.&lt;span class="n"&gt;pem&lt;/span&gt;
*.&lt;span class="n"&gt;key&lt;/span&gt;
**/&lt;span class="n"&gt;secrets&lt;/span&gt;*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Level 1: environment variables (the floor, not the ceiling)
&lt;/h2&gt;

&lt;p&gt;The minimum viable approach: load secrets from the environment at runtime, never from a committed file.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="n"&gt;DB_PASSWORD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;   &lt;span class="c1"&gt;# fail loudly if missing
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is fine for small apps, but env vars have real limits: they leak through crash dumps, child processes, and &lt;code&gt;/proc&lt;/code&gt;; they're hard to rotate; and there's no audit trail. Treat them as a starting point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Level 2: a real secret manager
&lt;/h2&gt;

&lt;p&gt;For anything production, use a dedicated secret store — HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, or your platform's built-in store. The app authenticates with a short-lived identity and fetches secrets at startup or on demand.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_secret&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secretsmanager&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_secret_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SecretId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SecretString&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What you gain over plain env vars: centralized storage, access control per identity, an audit log of who read what, and rotation support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rotate on a schedule, not after a breach
&lt;/h2&gt;

&lt;p&gt;A secret that never changes is a secret that's one leak away from a long, quiet compromise. Rotate credentials regularly and automatically. The pattern that makes rotation painless is &lt;strong&gt;two active versions at once&lt;/strong&gt;: issue the new secret, let both work during an overlap window, deploy, then retire the old one. No big-bang cutover, no downtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prefer short-lived credentials over long-lived keys
&lt;/h2&gt;

&lt;p&gt;The best secret is one that expires on its own. Where you can, replace static API keys with short-lived tokens: cloud IAM roles, OIDC workload identity, and database credentials minted on demand with a TTL. A token that's valid for fifteen minutes is far less valuable to steal than a key that's valid forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scan for leaks before they ship
&lt;/h2&gt;

&lt;p&gt;Add automated secret detection so a hardcoded key is caught in review, not in production. Run a scanner as a pre-commit hook &lt;em&gt;and&lt;/em&gt; in CI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# pre-commit / CI&lt;/span&gt;
gitleaks detect &lt;span class="nt"&gt;--no-banner&lt;/span&gt;
&lt;span class="c"&gt;# or: trufflehog, detect-secrets&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pre-commit catches it on the developer's machine; CI catches what slipped past. Use both — defense in depth costs almost nothing here.&lt;/p&gt;

&lt;h2&gt;
  
  
  When a secret leaks: revoke first, investigate second
&lt;/h2&gt;

&lt;p&gt;Speed beats elegance. The moment you suspect exposure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Revoke/rotate the secret immediately.&lt;/strong&gt; A removed commit is still public; assume it's compromised.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issue a replacement&lt;/strong&gt; through your secret manager.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check the audit logs&lt;/strong&gt; for use you didn't expect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Then&lt;/strong&gt; do the post-mortem — how it leaked, and which guardrail would have caught it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The instinct to "quietly remove the commit and hope" is exactly wrong. A leaked key is compromised; treat it that way.&lt;/p&gt;

&lt;h2&gt;
  
  
  The checklist
&lt;/h2&gt;

&lt;p&gt;Secrets out of code and git? ✅ Real secret manager in production? ✅ Rotation automated with an overlap window? ✅ Short-lived credentials where possible? ✅ Leak scanning in pre-commit and CI? ✅ A revoke-first incident plan? ✅ Six yeses and the "key in a screenshot" disaster mostly can't happen to you.&lt;/p&gt;

&lt;p&gt;If you'd rather adopt this as a ready-made setup than assemble it piece by piece, the &lt;a href="https://datanest-stores.pages.dev/security-engineer/preview-secrets-management-guide.html" rel="noopener noreferrer"&gt;Secrets Management Guide&lt;/a&gt; packages the patterns, config templates, rotation runbooks, and leak-scanning hooks so your team standardizes on the safe path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;You can't prevent every mistake, but you can make the safe way the easy way and the unsafe way loud. Keep secrets out of the repo, manage them centrally, rotate them automatically, and rehearse revocation — and a leaked credential becomes a non-event instead of a breach.&lt;/p&gt;

</description>
      <category>security</category>
      <category>devops</category>
      <category>webdev</category>
      <category>backend</category>
    </item>
    <item>
      <title>pytest Patterns That Make Tests Actually Maintainable (2026)</title>
      <dc:creator>DatanestDigital</dc:creator>
      <pubDate>Sat, 20 Jun 2026 11:29:36 +0000</pubDate>
      <link>https://dev.to/datanestdigital/pytest-patterns-that-make-tests-actually-maintainable-2026-5heo</link>
      <guid>https://dev.to/datanestdigital/pytest-patterns-that-make-tests-actually-maintainable-2026-5heo</guid>
      <description>&lt;p&gt;Most Python test suites don't fail because the team can't write tests. They fail because the tests become a maintenance burden: copy-pasted setup, brittle mocks, and a suite so slow nobody runs it locally. pytest gives you the tools to avoid all of that — if you use them deliberately.&lt;/p&gt;

&lt;p&gt;Here are the patterns that keep a suite fast, readable, and cheap to change as the code evolves.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Fixtures over setup/teardown
&lt;/h2&gt;

&lt;p&gt;Fixtures are pytest's superpower: reusable, composable setup that's requested by name. A test declares what it needs as an argument and pytest builds it.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;db_session&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;            &lt;span class="c1"&gt;# test runs here
&lt;/span&gt;    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rollback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;       &lt;span class="c1"&gt;# teardown after yield
&lt;/span&gt;    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_session&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ada&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything after &lt;code&gt;yield&lt;/code&gt; is teardown — guaranteed to run even if the test fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Scope fixtures to control cost
&lt;/h2&gt;

&lt;p&gt;A fixture that spins up a database shouldn't rebuild it for every test. Set &lt;code&gt;scope&lt;/code&gt; so expensive setup is shared.&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="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;eng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_engine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgresql://...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;eng&lt;/span&gt;
    &lt;span class="n"&gt;eng&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;function&lt;/code&gt; (default), &lt;code&gt;class&lt;/code&gt;, &lt;code&gt;module&lt;/code&gt;, &lt;code&gt;session&lt;/code&gt; — widen the scope for expensive resources, keep it narrow for anything that holds mutable state.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Parametrize instead of copy-pasting tests
&lt;/h2&gt;

&lt;p&gt;When the only thing changing is the inputs and expected output, &lt;code&gt;parametrize&lt;/code&gt; turns ten near-identical tests into one.&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="nd"&gt;@pytest.mark.parametrize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;amount, expected&lt;/span&gt;&lt;span class="sh"&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="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.00&lt;/span&gt;&lt;span class="sh"&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.00&lt;/span&gt;&lt;span class="sh"&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;99999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;999.99&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-0.50&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_format_cents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nf"&gt;format_cents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each row reports as its own test, so a failure points straight at the offending case.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Factory fixtures for flexible test data
&lt;/h2&gt;

&lt;p&gt;When tests need objects with slight variations, return a &lt;em&gt;function&lt;/em&gt; from the fixture instead of a fixed object.&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="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_session&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_make&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;overrides&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;viewer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;overrides&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;create_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db_session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_make&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_admin_can_delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;make_user&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;admin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;make_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;admin&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;can_delete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No more ten fixtures for ten slightly different users.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Mock at the boundary, not everywhere
&lt;/h2&gt;

&lt;p&gt;Mock the things you don't control — network, time, third-party APIs — and let your own code run for real. Over-mocking produces tests that pass while the app is broken.&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_sends_welcome_email&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;make_user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mocker&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;send&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mocker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;myapp.email.send&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;# patch where it's USED
&lt;/span&gt;    &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;make_user&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assert_called_once&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The classic gotcha: patch the name in the module that &lt;em&gt;uses&lt;/em&gt; it, not where it's defined.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Markers to slice the suite
&lt;/h2&gt;

&lt;p&gt;Tag tests so you can run a fast subset locally and the full suite in CI.&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="nd"&gt;@pytest.mark.slow&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_full_import_pipeline&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="bp"&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 shell"&gt;&lt;code&gt;pytest &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"not slow"&lt;/span&gt;     &lt;span class="c"&gt;# quick feedback loop&lt;/span&gt;
pytest                   &lt;span class="c"&gt;# everything, in CI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register markers in &lt;code&gt;pyproject.toml&lt;/code&gt; so a typo doesn't silently skip tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Put shared fixtures in &lt;code&gt;conftest.py&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Fixtures in a &lt;code&gt;conftest.py&lt;/code&gt; are auto-discovered by every test in that directory and below — no imports. Keep global fixtures at the root &lt;code&gt;conftest.py&lt;/code&gt;, and package-specific ones in nested ones.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;tests/
├── conftest.py          # engine, app, client
├── api/
│   ├── conftest.py      # authed_client
│   └── test_users.py
└── unit/
    └── test_format.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. Assert on behavior, not implementation
&lt;/h2&gt;

&lt;p&gt;Test what the function &lt;em&gt;does&lt;/em&gt;, not how. &lt;code&gt;assert result == expected&lt;/code&gt; survives refactors; asserting that an internal helper was called three times breaks the moment you tidy the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The payoff
&lt;/h2&gt;

&lt;p&gt;Fixtures kill setup duplication, scopes keep the suite fast, parametrize collapses repetition, and factory fixtures make varied data trivial. Together they produce the property that matters most: a suite people actually run, that fails for real reasons, and that doesn't fight you when the code changes.&lt;/p&gt;

&lt;p&gt;Standing this up cleanly — &lt;code&gt;conftest&lt;/code&gt; layout, database and HTTP-client fixtures, factories, coverage config — is the same scaffolding every project needs. The &lt;a href="https://datanest-stores.pages.dev/python-developer/preview-python-testing-toolkit.html" rel="noopener noreferrer"&gt;Python Testing Toolkit&lt;/a&gt; bundles these patterns as ready-to-use fixtures and config so a new repo has a maintainable suite from the first commit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;Maintainable tests aren't about discipline alone — they're about using pytest's composition tools so the easy path is also the clean one. Lean on fixtures, parametrize, and good &lt;code&gt;conftest&lt;/code&gt; structure, and your test suite becomes an asset instead of a chore.&lt;/p&gt;

</description>
      <category>python</category>
      <category>testing</category>
      <category>pytest</category>
      <category>programming</category>
    </item>
    <item>
      <title>Kubernetes for Developers: The 8 Objects You Actually Need to Know (2026)</title>
      <dc:creator>DatanestDigital</dc:creator>
      <pubDate>Sat, 20 Jun 2026 11:29:00 +0000</pubDate>
      <link>https://dev.to/datanestdigital/kubernetes-for-developers-the-8-objects-you-actually-need-to-know-2026-47h5</link>
      <guid>https://dev.to/datanestdigital/kubernetes-for-developers-the-8-objects-you-actually-need-to-know-2026-47h5</guid>
      <description>&lt;p&gt;Kubernetes has hundreds of object types, and the documentation introduces them like you're about to operate the cluster. As an application developer, you don't need most of them. You need maybe eight, and once they click, deploying to Kubernetes stops feeling like sorcery.&lt;/p&gt;

&lt;p&gt;Here are the eight objects that cover almost everything you'll touch, in the order they build on each other.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Pod — the unit that actually runs
&lt;/h2&gt;

&lt;p&gt;A Pod is one or more containers that share a network and lifecycle. It's the smallest thing Kubernetes schedules. You'll rarely create Pods directly — but everything else exists to manage them, so it's the mental foundation.&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;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;Pod&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;web&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;web&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;myapp:1.4.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="nv"&gt;containerPort&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key fact: Pods are &lt;strong&gt;cattle, not pets&lt;/strong&gt;. They get killed, rescheduled, and replaced. Never assume one stays alive or keeps its IP.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Deployment — how you actually run Pods
&lt;/h2&gt;

&lt;p&gt;A Deployment manages a set of identical Pods: it keeps N replicas running, replaces crashed ones, and rolls out new versions without downtime. This is what you write 90% of the time.&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="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;web&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;replicas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&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;web&lt;/span&gt; &lt;span class="pi"&gt;}&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="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&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;web&lt;/span&gt; &lt;span class="pi"&gt;}&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;web&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;myapp:1.4.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the image, &lt;code&gt;kubectl apply&lt;/code&gt;, and Kubernetes rolls Pods one batch at a time. Bad release? &lt;code&gt;kubectl rollout undo&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Service — a stable address for unstable Pods
&lt;/h2&gt;

&lt;p&gt;Pods come and go, so you never talk to them directly. A Service gives a fixed DNS name and load-balances across whichever Pods currently match its selector.&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;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;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;web&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;selector&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;web&lt;/span&gt; &lt;span class="pi"&gt;}&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="nv"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;80&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;targetPort&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now other Pods reach it at &lt;code&gt;http://web&lt;/code&gt;. The Service tracks the Pods for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Ingress — getting traffic in from the outside
&lt;/h2&gt;

&lt;p&gt;A Service is internal by default. An Ingress exposes HTTP routes to the world and handles host/path routing and TLS, so one load balancer can serve many services.&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;networking.k8s.io/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;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;web&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;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app.example.com&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Prefix&lt;/span&gt;
            &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;web&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="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;80&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. ConfigMap — configuration that isn't secret
&lt;/h2&gt;

&lt;p&gt;Keep config out of your image so the same image runs in dev, staging, and prod. A ConfigMap holds non-sensitive key/values you inject as env vars or files.&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;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;ConfigMap&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;web-config&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;LOG_LEVEL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;info"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;FEATURE_X&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Secret — configuration that &lt;em&gt;is&lt;/em&gt; sensitive
&lt;/h2&gt;

&lt;p&gt;Same idea, for credentials and tokens. Reference them as env vars or mounted files and keep them out of your image and your git history. (Enable encryption-at-rest and RBAC — a base64 Secret isn't encrypted by default.)&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;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_PASSWORD&lt;/span&gt;
    &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;password&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. Liveness &amp;amp; readiness probes — let the cluster heal you
&lt;/h2&gt;

&lt;p&gt;These aren't separate objects, but they live in your Deployment and they're the difference between "self-healing" and "silently broken." &lt;strong&gt;Readiness&lt;/strong&gt; decides if a Pod should receive traffic; &lt;strong&gt;liveness&lt;/strong&gt; decides if it should be restarted.&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;readinessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&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="nv"&gt;initialDelaySeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;5&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;span class="na"&gt;livenessProbe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&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="nv"&gt;periodSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;10&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Skip these and Kubernetes will happily route traffic to a Pod that's still booting or wedged.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Resource requests &amp;amp; limits — be a good neighbor
&lt;/h2&gt;

&lt;p&gt;Tell the scheduler what each Pod needs (&lt;code&gt;requests&lt;/code&gt;) and the ceiling it can't exceed (&lt;code&gt;limits&lt;/code&gt;). Without these, one runaway Pod can starve the node.&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;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&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;100m"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&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;128Mi"&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="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&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;500m"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&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;512Mi"&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The mental model
&lt;/h2&gt;

&lt;p&gt;Read top to bottom and it's a pipeline: a &lt;strong&gt;Deployment&lt;/strong&gt; keeps &lt;strong&gt;Pods&lt;/strong&gt; running, a &lt;strong&gt;Service&lt;/strong&gt; gives them a stable address, an &lt;strong&gt;Ingress&lt;/strong&gt; lets the outside reach them, and &lt;strong&gt;ConfigMaps/Secrets&lt;/strong&gt; feed them configuration — while &lt;strong&gt;probes&lt;/strong&gt; and &lt;strong&gt;resource limits&lt;/strong&gt; keep the whole thing healthy. Master these eight and you can deploy and debug the vast majority of real workloads without ever touching the cluster internals.&lt;/p&gt;

&lt;p&gt;The remaining friction is writing all that YAML correctly and consistently. If you'd rather start from working, commented manifests than a blank file, the &lt;a href="https://datanest-stores.pages.dev/devops-toolkit/preview-kubernetes-manifests-pack.html" rel="noopener noreferrer"&gt;Kubernetes Manifests Pack&lt;/a&gt; ships production-ready templates for exactly these objects — Deployment, Service, Ingress, probes, and resource policies — so your first deploy is a fill-in-the-blanks job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;Kubernetes is wide but shallow for developers: a small set of objects does the heavy lifting for application workloads. Learn these eight, wire up probes and limits, and the platform becomes a tool you use instead of a system you fear.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>devops</category>
      <category>docker</category>
      <category>backend</category>
    </item>
    <item>
      <title>REST API Design: 12 Rules for Endpoints Developers Won't Hate (2026)</title>
      <dc:creator>DatanestDigital</dc:creator>
      <pubDate>Sat, 20 Jun 2026 11:28:24 +0000</pubDate>
      <link>https://dev.to/datanestdigital/rest-api-design-12-rules-for-endpoints-developers-wont-hate-2026-141b</link>
      <guid>https://dev.to/datanestdigital/rest-api-design-12-rules-for-endpoints-developers-wont-hate-2026-141b</guid>
      <description>&lt;p&gt;A good REST API feels obvious. You guess the URL and you're right; you read one error and you know exactly what to fix. A bad one makes you keep the docs open in a second monitor and reverse-engineer behavior from 200-responses that secretly contain failures.&lt;/p&gt;

&lt;p&gt;The difference is rarely the framework. It's a handful of conventions applied consistently. Here are twelve rules that, followed together, produce an API integrators don't complain about.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Name resources with plural nouns, not verbs
&lt;/h2&gt;

&lt;p&gt;The HTTP method is the verb. The path is the thing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET    /users           # list
POST   /users           # create
GET    /users/42        # read
PATCH  /users/42        # partial update
DELETE /users/42        # delete
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not &lt;code&gt;/getUser&lt;/code&gt;, &lt;code&gt;/createUser&lt;/code&gt;, &lt;code&gt;/user/delete&lt;/code&gt;. The method already says what you're doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Nest only to show ownership — then stop
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;GET /users/42/orders&lt;/code&gt; is fine: orders belong to a user. But don't go three levels deep. Past one level of nesting, switch to query parameters: &lt;code&gt;GET /orders?user=42&amp;amp;status=open&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Use HTTP status codes honestly
&lt;/h2&gt;

&lt;p&gt;Return the code that's true, not &lt;code&gt;200&lt;/code&gt; with &lt;code&gt;{"success": false}&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;200&lt;/code&gt; OK, &lt;code&gt;201&lt;/code&gt; Created, &lt;code&gt;204&lt;/code&gt; No Content&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;400&lt;/code&gt; bad request, &lt;code&gt;401&lt;/code&gt; unauthenticated, &lt;code&gt;403&lt;/code&gt; unauthorized, &lt;code&gt;404&lt;/code&gt; not found, &lt;code&gt;409&lt;/code&gt; conflict, &lt;code&gt;422&lt;/code&gt; validation failed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;429&lt;/code&gt; rate limited, &lt;code&gt;500&lt;/code&gt; server error&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A client should be able to branch on the status line alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Return a consistent error shape
&lt;/h2&gt;

&lt;p&gt;Every error, everywhere, in the same structure. Machine-readable &lt;code&gt;code&lt;/code&gt;, human &lt;code&gt;message&lt;/code&gt;, and field-level detail when relevant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"validation_failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Email is not valid."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"issue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"format"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pick this shape once and never deviate. Clients build error handling around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. &lt;code&gt;PATCH&lt;/code&gt; for partial, &lt;code&gt;PUT&lt;/code&gt; for full replacement
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;PATCH /users/42&lt;/code&gt; with &lt;code&gt;{ "email": "..." }&lt;/code&gt; changes one field. &lt;code&gt;PUT&lt;/code&gt; means "replace the whole resource with this representation." Don't make &lt;code&gt;PUT&lt;/code&gt; behave like &lt;code&gt;PATCH&lt;/code&gt;; you'll surprise everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Make writes idempotent where you can
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;PUT&lt;/code&gt; and &lt;code&gt;DELETE&lt;/code&gt; should be safe to retry. For &lt;code&gt;POST&lt;/code&gt; (create), support an &lt;code&gt;Idempotency-Key&lt;/code&gt; header so a client that retries after a network blip doesn't create two orders.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Paginate from day one
&lt;/h2&gt;

&lt;p&gt;A collection that returns "all rows" works in dev and falls over in production. Decide on a strategy early. Cursor pagination scales better than offset for large, changing datasets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /users?limit=50&amp;amp;cursor=eyJpZCI6MTAwfQ
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"next_cursor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJpZCI6MTUwfQ"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"has_more"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. Filter, sort, and select with query params
&lt;/h2&gt;

&lt;p&gt;Keep the path about identity; put the query in the query string.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /orders?status=open&amp;amp;sort=-created_at&amp;amp;fields=id,total
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;-created_at&lt;/code&gt; for descending is a tiny convention that saves a lot of guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Version at the edge, before you need to
&lt;/h2&gt;

&lt;p&gt;Put a version in the URL (&lt;code&gt;/v1/...&lt;/code&gt;) or a header from the very first release. The day you must make a breaking change, &lt;code&gt;/v2&lt;/code&gt; lets existing clients keep working instead of breaking in production.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Be consistent about field casing and dates
&lt;/h2&gt;

&lt;p&gt;Pick &lt;code&gt;snake_case&lt;/code&gt; or &lt;code&gt;camelCase&lt;/code&gt; and apply it to every field in every response. Use ISO 8601 UTC for timestamps (&lt;code&gt;2026-06-20T11:00:00Z&lt;/code&gt;). Mixed casing and ambiguous local dates are a thousand tiny paper cuts.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Don't leak your database
&lt;/h2&gt;

&lt;p&gt;Your API is a contract, not a SQL dump. Don't expose internal auto-increment IDs if they reveal volume, don't return columns clients shouldn't see, and don't let the response shape change because you refactored a table. The representation is yours to design.&lt;/p&gt;

&lt;h2&gt;
  
  
  12. Document with real examples, and keep them true
&lt;/h2&gt;

&lt;p&gt;The best docs are copy-pasteable requests and their exact responses. Generate an OpenAPI spec from the code so the docs can't drift from behavior — a lying example is worse than none.&lt;/p&gt;

&lt;h2&gt;
  
  
  The through-line
&lt;/h2&gt;

&lt;p&gt;Every rule above is really one rule: &lt;strong&gt;be predictable.&lt;/strong&gt; When naming, status codes, errors, pagination, and dates are uniform across every endpoint, a developer who learns one part of your API has effectively learned all of it.&lt;/p&gt;

&lt;p&gt;If you want these conventions as a ready-made starting point — endpoint templates, the standard error envelope, pagination helpers, and an OpenAPI skeleton — the &lt;a href="https://datanest-stores.pages.dev/api-developer/preview-rest-api-design-guide.html" rel="noopener noreferrer"&gt;REST API Design Guide&lt;/a&gt; packages them so a new service is consistent on day one instead of after the third refactor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;APIs are read far more often than they're written. Spend the design effort up front on consistency, and every future integrator — including future you — inherits an interface they can guess their way through.&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>backend</category>
      <category>programming</category>
    </item>
    <item>
      <title>10 TypeScript Utility Types That Will Clean Up Your Codebase (2026)</title>
      <dc:creator>DatanestDigital</dc:creator>
      <pubDate>Sat, 20 Jun 2026 11:27:48 +0000</pubDate>
      <link>https://dev.to/datanestdigital/10-typescript-utility-types-that-will-clean-up-your-codebase-2026-2em2</link>
      <guid>https://dev.to/datanestdigital/10-typescript-utility-types-that-will-clean-up-your-codebase-2026-2em2</guid>
      <description>&lt;p&gt;Most TypeScript codebases are full of types that are copies of other types with one field changed. A &lt;code&gt;User&lt;/code&gt;, then a &lt;code&gt;UserUpdate&lt;/code&gt; that's the same minus &lt;code&gt;id&lt;/code&gt;, then a &lt;code&gt;UserResponse&lt;/code&gt; that's the same plus a timestamp. Maintaining those by hand is how types drift out of sync with reality.&lt;/p&gt;

&lt;p&gt;TypeScript ships a set of built-in &lt;strong&gt;utility types&lt;/strong&gt; that derive new types from existing ones, so a change in one place propagates everywhere. Here are the ten I reach for constantly, with the real situations they solve.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;Partial&amp;lt;T&amp;gt;&lt;/code&gt; — everything optional
&lt;/h2&gt;

&lt;p&gt;Perfect for update payloads and patch functions where the caller supplies only the fields they're changing.&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;interface&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;id&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="nl"&gt;name&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="nl"&gt;email&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;changes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;new@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;   &lt;span class="c1"&gt;// ✅ no need to pass name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. &lt;code&gt;Required&amp;lt;T&amp;gt;&lt;/code&gt; — everything mandatory
&lt;/h2&gt;

&lt;p&gt;The inverse. Useful when you receive a loosely-typed config and want to assert that defaults have filled every hole.&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;interface&lt;/span&gt; &lt;span class="nx"&gt;Options&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;timeout&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="nl"&gt;retries&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="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Required&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Options&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* every field guaranteed */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. &lt;code&gt;Readonly&amp;lt;T&amp;gt;&lt;/code&gt; — freeze the shape at the type level
&lt;/h2&gt;

&lt;p&gt;Signals intent and catches accidental mutation at compile time — great for state objects and function parameters you don't own.&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;function&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Readonly&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;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;config&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;x&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// ❌ Cannot assign to 'name', it is read-only&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. &lt;code&gt;Pick&amp;lt;T, K&amp;gt;&lt;/code&gt; — keep only the keys you need
&lt;/h2&gt;

&lt;p&gt;Stop hand-copying a subset of fields. &lt;code&gt;Pick&lt;/code&gt; derives it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UserPreview&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Pick&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&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="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// { id: string; name: string }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. &lt;code&gt;Omit&amp;lt;T, K&amp;gt;&lt;/code&gt; — everything except these keys
&lt;/h2&gt;

&lt;p&gt;The one I use most. "A &lt;code&gt;User&lt;/code&gt; but without the server-generated &lt;code&gt;id&lt;/code&gt;" becomes a one-liner that updates itself when &lt;code&gt;User&lt;/code&gt; changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;NewUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// { name: string; email: string }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. &lt;code&gt;Record&amp;lt;K, V&amp;gt;&lt;/code&gt; — typed dictionaries
&lt;/h2&gt;

&lt;p&gt;Replaces the vague &lt;code&gt;{ [key: string]: V }&lt;/code&gt; with something precise, and can constrain the keys to a union.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Role&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;admin&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="s2"&gt;editor&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="s2"&gt;viewer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Role&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;&amp;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;admin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;read&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;write&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;editor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;read&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;write&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;viewer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;read&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="c1"&gt;// miss a role and the compiler tells you&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  7. &lt;code&gt;ReturnType&amp;lt;T&amp;gt;&lt;/code&gt; — infer what a function returns
&lt;/h2&gt;

&lt;p&gt;Invaluable when a function's return type is complex or inferred, and you want a variable typed to match without restating it.&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;function&lt;/span&gt; &lt;span class="nf"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&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="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="k"&gt;as&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="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;createStore&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  8. &lt;code&gt;Parameters&amp;lt;T&amp;gt;&lt;/code&gt; — capture an argument list as a tuple
&lt;/h2&gt;

&lt;p&gt;Great for wrappers, decorators, and higher-order functions that must forward arguments faithfully.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;LogArgs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Parameters&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;updateUser&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// [string, Partial&amp;lt;User&amp;gt;]&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;withLogging&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Parameters&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;updateUser&lt;/span&gt;&lt;span class="o"&gt;&amp;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;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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;calling updateUser&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;updateUser&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  9. &lt;code&gt;NonNullable&amp;lt;T&amp;gt;&lt;/code&gt; — strip &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;After a guard, narrow a type so the rest of the function doesn't have to keep checking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;MaybeUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DefiniteUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;NonNullable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MaybeUser&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// User&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  10. &lt;code&gt;Exclude&amp;lt;T, U&amp;gt;&lt;/code&gt; and &lt;code&gt;Extract&amp;lt;T, U&amp;gt;&lt;/code&gt; — filter unions
&lt;/h2&gt;

&lt;p&gt;Filter members out of (or into) a union type — the set operations of the type system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;active&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="s2"&gt;archived&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="s2"&gt;deleted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Visible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Exclude&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="s2"&gt;deleted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// "active" | "archived"&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Gone&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Extract&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&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="s2"&gt;deleted&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// "deleted"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Compose them for real power
&lt;/h2&gt;

&lt;p&gt;The utilities shine when combined. "A draft is a &lt;code&gt;User&lt;/code&gt; without its &lt;code&gt;id&lt;/code&gt;, with everything optional" is one expression that tracks &lt;code&gt;User&lt;/code&gt; forever:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UserDraft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Omit&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// A response type derived from the model, plus a server field:&lt;/span&gt;
&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;UserResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Readonly&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;createdAt&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;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once your derived types are &lt;em&gt;computed&lt;/em&gt; from a single source of truth, an edit to &lt;code&gt;User&lt;/code&gt; ripples through &lt;code&gt;UserDraft&lt;/code&gt;, &lt;code&gt;UserPreview&lt;/code&gt;, and &lt;code&gt;UserResponse&lt;/code&gt; automatically — and the compiler flags every place that needs attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  A working reference beats memorizing
&lt;/h2&gt;

&lt;p&gt;These ten cover the overwhelming majority of day-to-day needs, but the combinations (mapped types, conditional types, template literal types) are where teams reinvent the same helpers. If you'd rather pull from a vetted set, the &lt;a href="https://datanest-stores.pages.dev/frontend-developer/preview-typescript-utility-library.html" rel="noopener noreferrer"&gt;TypeScript Utility Library&lt;/a&gt; collects production-ready type helpers and patterns — deep-partial, strict-omit, branded types, and more — so you stop rewriting them per project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;The fastest way to a cleaner TypeScript codebase isn't more types — it's fewer &lt;em&gt;hand-written&lt;/em&gt; ones. Derive types from a single source with &lt;code&gt;Pick&lt;/code&gt;, &lt;code&gt;Omit&lt;/code&gt;, &lt;code&gt;Partial&lt;/code&gt;, and friends, and let the compiler keep them honest as your models evolve.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Hardening Docker Containers: A Practical 2026 Security Checklist</title>
      <dc:creator>DatanestDigital</dc:creator>
      <pubDate>Sat, 20 Jun 2026 11:18:24 +0000</pubDate>
      <link>https://dev.to/datanestdigital/hardening-docker-containers-a-practical-2026-security-checklist-jop</link>
      <guid>https://dev.to/datanestdigital/hardening-docker-containers-a-practical-2026-security-checklist-jop</guid>
      <description>&lt;p&gt;A container is not a security boundary by default. Out of the box, the typical image runs as root, ships a full distro's worth of attack surface, can write anywhere, and keeps every Linux capability it was born with. None of that is required to run your app — it's just the path of least resistance, and attackers love it.&lt;/p&gt;

&lt;p&gt;This is the checklist I run before anything goes to production. Every item is concrete, most are one or two lines, and together they turn a default container into a genuinely hard target. Copy the snippets, adapt, ship.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Don't run as root
&lt;/h2&gt;

&lt;p&gt;If your process is root inside the container and an attacker escapes the process, they're root in the container — and root in the container is one misconfiguration away from root on the host. Create and switch to an unprivileged user.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;--system&lt;/span&gt; app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; adduser &lt;span class="nt"&gt;--system&lt;/span&gt; &lt;span class="nt"&gt;--ingroup&lt;/span&gt; app app
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At runtime, enforce it so a bad image can't override the intent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# docker-compose / k8s securityContext&lt;/span&gt;
&lt;span class="na"&gt;user&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;10001:10001"&lt;/span&gt;
&lt;span class="na"&gt;read_only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;cap_drop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ALL"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;security_opt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;no-new-privileges:true"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;no-new-privileges&lt;/code&gt; blocks setuid binaries from escalating — a cheap, high-value flag.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Start from the smallest base you can
&lt;/h2&gt;

&lt;p&gt;Every package in your base image is attack surface and another CVE to patch. &lt;code&gt;python:3.12&lt;/code&gt; is ~1 GB; &lt;code&gt;python:3.12-slim&lt;/code&gt; is a fraction of that; distroless or Alpine smaller still. Fewer files means fewer vulnerabilities and a smaller blast radius.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# from this...&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.12&lt;/span&gt;
&lt;span class="c"&gt;# ...to this&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.12-slim&lt;/span&gt;
&lt;span class="c"&gt;# ...or, for compiled/static apps, distroless with no shell at all&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/static-debian12&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A distroless image has no shell and no package manager, so even after a breakout the attacker has almost nothing to work with.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Use multi-stage builds to leave the toolchain behind
&lt;/h2&gt;

&lt;p&gt;Build tools, compilers, and dev dependencies should never reach production. Build in one stage, copy only the artifact into a clean final stage.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;python:3.12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; requirements.txt .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/install &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.12-slim&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /install /usr/local&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /app&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final image carries your app and its runtime deps — and nothing you only needed to build it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Make the filesystem read-only
&lt;/h2&gt;

&lt;p&gt;A process that can't write can't drop a webshell, tamper with binaries, or persist. Run the root filesystem read-only and mount explicit, narrow &lt;code&gt;tmpfs&lt;/code&gt; volumes for the few paths that genuinely need writes.&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;read_only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;tmpfs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/tmp&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your app refuses to start read-only, that's a useful finding: it's writing somewhere it shouldn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Drop every capability, then add back only what you need
&lt;/h2&gt;

&lt;p&gt;Containers inherit a default set of Linux capabilities most apps never use. Drop them all and re-add the rare exceptions explicitly (a web app almost never needs any).&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;cap_drop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ALL"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# cap_add: ["NET_BIND_SERVICE"]   # only if you must bind to a port &amp;lt; 1024&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Better: don't bind to a privileged port at all — listen on 8080 and let the load balancer map 443.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Keep secrets out of the image and the environment
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ENV API_KEY=...&lt;/code&gt; and &lt;code&gt;COPY .env .&lt;/code&gt; bake secrets into image layers forever — anyone who pulls the image can &lt;code&gt;docker history&lt;/code&gt; them out. Inject secrets at runtime via your orchestrator's secret store or mounted files, and add &lt;code&gt;.env&lt;/code&gt;, keys, and &lt;code&gt;.git&lt;/code&gt; to &lt;code&gt;.dockerignore&lt;/code&gt; so they never enter the build context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# .dockerignore
&lt;/span&gt;.&lt;span class="n"&gt;env&lt;/span&gt;
*.&lt;span class="n"&gt;pem&lt;/span&gt;
.&lt;span class="n"&gt;git&lt;/span&gt;
**/&lt;span class="n"&gt;secrets&lt;/span&gt;*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Environment variables leak through crash dumps, child processes, and &lt;code&gt;/proc&lt;/code&gt;; prefer mounted secret files with tight permissions where you can.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Pin versions and scan every image
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;FROM node:latest&lt;/code&gt; means your build is non-reproducible and silently pulls tomorrow's unpatched surprise. Pin to a digest, and run a scanner in CI that &lt;strong&gt;fails the build&lt;/strong&gt; on high/critical CVEs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; python:3.12-slim@sha256:&amp;lt;digest&amp;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 shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# in CI — fail on serious findings&lt;/span&gt;
trivy image &lt;span class="nt"&gt;--severity&lt;/span&gt; HIGH,CRITICAL &lt;span class="nt"&gt;--exit-code&lt;/span&gt; 1 myapp:sha-1234
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scanning once is theater; scanning on every build is control. Rebuild regularly so base-image patches actually reach production.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Add a HEALTHCHECK and handle signals
&lt;/h2&gt;

&lt;p&gt;A container that can't report health hides failures; a process that ignores SIGTERM gets SIGKILLed on every deploy, dropping in-flight work. Both are reliability &lt;em&gt;and&lt;/em&gt; security concerns — zombie or wedged containers are where bad things hide.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;HEALTHCHECK&lt;/span&gt;&lt;span class="s"&gt; --interval=30s --timeout=3s \&lt;/span&gt;
  CMD curl -fsS http://localhost:8080/healthz || exit 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure your app traps SIGTERM and shuts down gracefully (or run it under an init like &lt;code&gt;tini&lt;/code&gt; to reap zombies).&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Constrain resources
&lt;/h2&gt;

&lt;p&gt;Set memory and CPU limits so a single compromised or buggy container can't starve its neighbors or the host — a trivial denial-of-service otherwise.&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;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&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;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;512M&lt;/span&gt;
      &lt;span class="na"&gt;cpus&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;0.5"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The quick audit
&lt;/h2&gt;

&lt;p&gt;Before you ship, ask: Is it &lt;strong&gt;non-root&lt;/strong&gt;? Is the base &lt;strong&gt;minimal and pinned&lt;/strong&gt;? Is the filesystem &lt;strong&gt;read-only&lt;/strong&gt;? Are capabilities &lt;strong&gt;dropped&lt;/strong&gt;? Are secrets &lt;strong&gt;out of the image&lt;/strong&gt;? Does CI &lt;strong&gt;scan and fail&lt;/strong&gt; on critical CVEs? Six yeses and you've eliminated the overwhelming majority of real-world container attacks.&lt;/p&gt;

&lt;p&gt;If you want this enforced as ready-to-use templates rather than a checklist you re-implement per project, the &lt;a href="https://datanest-stores.pages.dev/devops-toolkit/preview-container-security-toolkit.html" rel="noopener noreferrer"&gt;Container Security Toolkit&lt;/a&gt; ships hardened Dockerfiles, secure &lt;code&gt;securityContext&lt;/code&gt; presets, and a CI scanning pipeline you can drop into an existing repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;Container hardening isn't one big effort — it's a stack of small, boring defaults that each remove an attacker's option. Non-root, minimal, read-only, capability-dropped, secrets-externalized, and continuously scanned. Set them once, bake them into your base templates, and every service you ship inherits the security instead of relitigating it.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>security</category>
      <category>devops</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>9 Battle-Tested Tactics to Cut Your LLM API Bill (2026)</title>
      <dc:creator>DatanestDigital</dc:creator>
      <pubDate>Sat, 20 Jun 2026 11:17:20 +0000</pubDate>
      <link>https://dev.to/datanestdigital/9-battle-tested-tactics-to-cut-your-llm-api-bill-2026-4799</link>
      <guid>https://dev.to/datanestdigital/9-battle-tested-tactics-to-cut-your-llm-api-bill-2026-4799</guid>
      <description>&lt;p&gt;The demo was cheap. Then you shipped, traffic grew, and the monthly model bill quietly became one of your largest infrastructure line items. LLM spend scales linearly with usage, and most teams leave 50–90% of it on the table because the easy wins are invisible until you go looking.&lt;/p&gt;

&lt;p&gt;Here are nine tactics that actually move the number, ordered roughly from highest to lowest leverage. None of them require switching providers, and most are a few hours of work.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Stop paying twice for the same answer (exact-match caching)
&lt;/h2&gt;

&lt;p&gt;A surprising share of production traffic is duplicate prompts: the same FAQ, the same summarization of the same document, the same system-prompted classification. Hash the full request (model + messages + params) and cache the response.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;cached_completion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kw&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llm:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;msgs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kw&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hit&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;call_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;86400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;   &lt;span class="c1"&gt;# 24h TTL
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For deterministic calls (&lt;code&gt;temperature=0&lt;/code&gt;) this is free money. Cache hit = zero tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Catch near-duplicates too (semantic caching)
&lt;/h2&gt;

&lt;p&gt;Exact-match misses "What's your refund policy?" vs "How do refunds work?". Embed the query, search a vector store, and if the nearest cached question is above a similarity threshold (~0.95), return its answer. Embeddings cost a fraction of a completion, so the math works strongly in your favor at scale. Tune the threshold carefully — too loose and you'll serve wrong answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Route by difficulty (model cascades)
&lt;/h2&gt;

&lt;p&gt;You do not need your most expensive model for "is this sentiment positive or negative?". Send everything to a small/cheap model first; escalate to the frontier model only when the cheap one signals low confidence or the task is genuinely hard.&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;CHEAP_TASKS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;                       &lt;span class="c1"&gt;# classification, extraction, routing
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;call_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;small-cheap-model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;call_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;frontier-model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# reasoning, long-form, code
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A well-tuned cascade routinely cuts blended cost-per-request by 60–80% because the long tail of simple requests stops hitting the premium tier.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Compress the prompt, not the quality
&lt;/h2&gt;

&lt;p&gt;You pay for every input token, and most prompts are bloated. Three high-ROI trims:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shrink the system prompt.&lt;/strong&gt; A 1,500-token system prompt sent on every request is a tax on every call. Move static instructions into a fine-tune or a shorter canonical version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prune RAG context.&lt;/strong&gt; Retrieving 20 chunks "to be safe" when 4 answer the question multiplies input cost. Re-rank and keep the top few.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Summarize history.&lt;/strong&gt; In long chats, replace old turns with a running summary instead of resending the entire transcript every time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Cap and control output tokens
&lt;/h2&gt;

&lt;p&gt;Output tokens usually cost more than input tokens, and an unbounded &lt;code&gt;max_tokens&lt;/code&gt; invites rambling. Set a sensible ceiling, and ask for structured/terse output when you don't need prose:&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="nf"&gt;call_model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;256&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;# bound the worst case
&lt;/span&gt;           &lt;span class="n"&gt;response_format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;json_object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;  &lt;span class="c1"&gt;# no filler, easy to parse
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Answer in one sentence" or "return only JSON" is a real cost lever, not just a UX choice.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Batch when latency allows
&lt;/h2&gt;

&lt;p&gt;Many workloads — nightly enrichment, backfills, evals, bulk classification — don't need real-time responses. Most providers offer an asynchronous &lt;strong&gt;batch API at a steep discount (commonly ~50%)&lt;/strong&gt; for jobs you can wait hours on. Split your traffic: interactive requests go to the real-time endpoint, everything deferrable goes to the batch lane.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Use provider-side prompt caching
&lt;/h2&gt;

&lt;p&gt;Several providers now cache a static prompt prefix server-side and bill the cached portion at a large discount on subsequent calls. If you send the same long system prompt or document context repeatedly, order your messages so the stable part comes first and opt into prompt caching. This stacks with tactic #4.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Fine-tune to delete the prompt
&lt;/h2&gt;

&lt;p&gt;When a task is narrow and high-volume, a small fine-tuned model can match a big model's quality on that task — with a fraction of the prompt tokens, because the instructions and few-shot examples are baked into the weights. You trade a one-time training cost for a permanently smaller per-request bill. Run the break-even math: above some daily volume, fine-tuning a cheaper base model wins decisively.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Measure cost per request, or you're flying blind
&lt;/h2&gt;

&lt;p&gt;You can't cut what you don't see. Log tokens and dollar cost on &lt;strong&gt;every&lt;/strong&gt; call, tagged by feature, model, and user tier. The first time you do this you'll find one endpoint quietly burning a third of the budget.&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;log_cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;cost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prompt_tokens&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;PRICE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; \
           &lt;span class="n"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completion_tokens&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;PRICE&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;out&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;llm.cost_usd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;feature:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model:&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Watch cost-per-successful-request as your north-star metric — it normalizes for traffic and exposes regressions a raw total hides.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put it together
&lt;/h2&gt;

&lt;p&gt;Stack these and the savings compound: caching removes duplicate work, routing moves the bulk of traffic to cheap models, prompt compression and output caps shrink what's left, batching discounts the deferrable tail, and observability keeps it all honest. Teams that apply the top four typically see their bill drop by more than half without any user-visible quality loss.&lt;/p&gt;

&lt;p&gt;If you'd rather not build the caching layer, router, and cost-tracking dashboard from scratch, the &lt;a href="https://datanest-stores.pages.dev/ai-llm-toolkit/preview-llm-cost-optimizer.html" rel="noopener noreferrer"&gt;LLM Cost Optimizer&lt;/a&gt; bundles these patterns — semantic cache, model-routing logic, token accounting, and ready-to-wire dashboards — so you can start saving this week instead of next quarter.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mindset shift
&lt;/h2&gt;

&lt;p&gt;LLM cost optimization isn't a one-time cleanup; it's a habit. Treat tokens like you treat database queries — something you profile, budget, and watch. The cheapest token is the one you never send, and the second cheapest is the one a small model handles.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>machinelearning</category>
      <category>programming</category>
    </item>
    <item>
      <title>Background Jobs in Python: Celery vs RQ vs Dramatiq vs Arq (2026 Decision Guide)</title>
      <dc:creator>DatanestDigital</dc:creator>
      <pubDate>Sat, 20 Jun 2026 11:17:13 +0000</pubDate>
      <link>https://dev.to/datanestdigital/background-jobs-in-python-celery-vs-rq-vs-dramatiq-vs-arq-2026-decision-guide-37m6</link>
      <guid>https://dev.to/datanestdigital/background-jobs-in-python-celery-vs-rq-vs-dramatiq-vs-arq-2026-decision-guide-37m6</guid>
      <description>&lt;p&gt;Every Python app that does anything real eventually hits the same wall: a request needs to do slow work — send an email, render a PDF, call a third-party API, run an inference — and you can't make the user wait for it. The fix is a background job: hand the work to a worker process and return immediately.&lt;/p&gt;

&lt;p&gt;The hard part isn't the concept. It's choosing among the four libraries that dominate this space — &lt;strong&gt;Celery&lt;/strong&gt;, &lt;strong&gt;RQ&lt;/strong&gt;, &lt;strong&gt;Dramatiq&lt;/strong&gt;, and &lt;strong&gt;Arq&lt;/strong&gt; — and then not regretting the choice eighteen months later. This guide is the decision framework I wish I'd had: what each one is good at, a minimal working example of each, and the production details that actually bite.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a task queue actually buys you
&lt;/h2&gt;

&lt;p&gt;A task queue gives you three things: &lt;strong&gt;offloading&lt;/strong&gt; (slow work leaves the request cycle), &lt;strong&gt;durability&lt;/strong&gt; (jobs survive a process restart because they live in a broker like Redis or RabbitMQ), and &lt;strong&gt;scalability&lt;/strong&gt; (you add workers to add throughput). If you only need "run this after the response is sent" and never need durability, an in-process option like FastAPI's &lt;code&gt;BackgroundTasks&lt;/code&gt; may be enough. The moment you need retries, scheduling, or work that must not be lost, you want a real queue.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four contenders at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Celery&lt;/th&gt;
&lt;th&gt;RQ&lt;/th&gt;
&lt;th&gt;Dramatiq&lt;/th&gt;
&lt;th&gt;Arq&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Broker&lt;/td&gt;
&lt;td&gt;Redis, RabbitMQ, SQS&lt;/td&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;Redis, RabbitMQ&lt;/td&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async (asyncio) tasks&lt;/td&gt;
&lt;td&gt;partial / awkward&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no (threads/processes)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;native&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maturity&lt;/td&gt;
&lt;td&gt;very high&lt;/td&gt;
&lt;td&gt;high&lt;/td&gt;
&lt;td&gt;high&lt;/td&gt;
&lt;td&gt;medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ops complexity&lt;/td&gt;
&lt;td&gt;high&lt;/td&gt;
&lt;td&gt;low&lt;/td&gt;
&lt;td&gt;low–medium&lt;/td&gt;
&lt;td&gt;low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduling/cron&lt;/td&gt;
&lt;td&gt;Celery Beat&lt;/td&gt;
&lt;td&gt;rq-scheduler&lt;/td&gt;
&lt;td&gt;APScheduler/built-in&lt;/td&gt;
&lt;td&gt;built-in cron&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best for&lt;/td&gt;
&lt;td&gt;large, heterogeneous workloads&lt;/td&gt;
&lt;td&gt;simple sync jobs&lt;/td&gt;
&lt;td&gt;reliability with low ceremony&lt;/td&gt;
&lt;td&gt;asyncio-native apps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Celery — the default everyone reaches for
&lt;/h2&gt;

&lt;p&gt;Celery is the 800-pound gorilla: enormous feature set, every broker, routing, priorities, chords/groups, mature monitoring (Flower). The cost is operational weight and a config surface that can swallow a week.&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="c1"&gt;# tasks.py
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;celery&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Celery&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Celery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;myapp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;broker&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis://localhost:6379/0&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis://localhost:6379/1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bind&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_retries&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="n"&gt;default_retry_delay&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;def&lt;/span&gt; &lt;span class="nf"&gt;send_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;build_and_email_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;TransientError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;exc&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 python"&gt;&lt;code&gt;&lt;span class="n"&gt;send_report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;          &lt;span class="c1"&gt;# enqueue
# worker:  celery -A tasks worker --loglevel=info
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pick Celery when&lt;/strong&gt; you have many task types, need routing/priorities across queues, or want the deepest ecosystem. &lt;strong&gt;Avoid it&lt;/strong&gt; when your needs are modest — you'll pay the complexity tax for features you never use.&lt;/p&gt;

&lt;h2&gt;
  
  
  RQ — the one you can understand in an afternoon
&lt;/h2&gt;

&lt;p&gt;RQ (Redis Queue) is deliberately small. Redis only, plain functions as jobs, a readable dashboard. There's almost no magic.&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Redis&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;rq&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Queue&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;myapp.jobs&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;send_report&lt;/span&gt;

&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Redis&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;send_report&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;retry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;Retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;max&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;# worker:  rq worker
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pick RQ when&lt;/strong&gt; your jobs are synchronous, Redis is already in your stack, and you value being able to read the entire library's behavior in your head. &lt;strong&gt;Avoid it&lt;/strong&gt; for asyncio-heavy code or when you need RabbitMQ-grade routing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dramatiq — reliability without the ceremony
&lt;/h2&gt;

&lt;p&gt;Dramatiq is the "Celery did too much" answer: sane defaults, automatic retries with exponential backoff, message age limits, and a clean middleware system — with far less configuration.&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;dramatiq&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dramatiq.brokers.redis&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RedisBroker&lt;/span&gt;

&lt;span class="n"&gt;dramatiq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set_broker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RedisBroker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis://localhost:6379&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nd"&gt;@dramatiq.actor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_retries&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="n"&gt;min_backoff&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;build_and_email_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;send_report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# worker:  dramatiq myapp
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pick Dramatiq when&lt;/strong&gt; you want Celery-grade reliability semantics (retries, dead-letter handling) but hate Celery-grade config. It's my default recommendation for new synchronous projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arq — built for asyncio from the ground up
&lt;/h2&gt;

&lt;p&gt;If your app is async (FastAPI, aiohttp, async DB drivers), Celery's async story will frustrate you. Arq is asyncio-native: jobs are coroutines, the worker is an event loop, and a single worker handles high I/O concurrency without a thread per job.&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arq&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_pool&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;arq.connections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;RedisSettings&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;send_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;build_and_email_report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# real awaits
&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;WorkerSettings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;functions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;send_report&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;redis_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;RedisSettings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# enqueue from async code:
&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;create_pool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RedisSettings&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue_job&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;send_report&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# worker:  arq mymodule.WorkerSettings
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pick Arq when&lt;/strong&gt; your codebase is async end-to-end and your jobs are I/O-bound (HTTP calls, DB, queues). One worker can run hundreds of concurrent jobs. &lt;strong&gt;Avoid it&lt;/strong&gt; for CPU-bound work — you still need processes for that.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 30-second decision
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Async app, I/O-bound jobs →&lt;/strong&gt; Arq.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;New sync project, want reliability with minimal config →&lt;/strong&gt; Dramatiq.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small, simple, Redis already present →&lt;/strong&gt; RQ.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Complex routing, priorities, many task types, or an existing Celery shop →&lt;/strong&gt; Celery.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The production gotchas nobody warns you about
&lt;/h2&gt;

&lt;p&gt;These apply no matter which library you pick:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Make tasks idempotent.&lt;/strong&gt; At-least-once delivery means a task can run twice (worker dies after doing the work but before acking). Use a dedupe key or an &lt;code&gt;INSERT ... ON CONFLICT&lt;/code&gt; so a re-run is harmless.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set a visibility/ack timeout longer than your slowest task.&lt;/strong&gt; If the broker reclaims a message because the task ran long, you get duplicate execution. Tune it deliberately.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cap retries and route failures to a dead-letter queue.&lt;/strong&gt; Infinite retries on a poison message will saturate your workers. Bound them and inspect the DLQ.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle graceful shutdown.&lt;/strong&gt; A deploy that SIGKILLs a worker mid-task loses or double-runs it. Trap SIGTERM, stop pulling new work, finish in-flight jobs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep payloads small.&lt;/strong&gt; Enqueue an ID, not a 5 MB object. Fetch the data inside the task. Big payloads bloat the broker and slow everything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor queue depth, not just CPU.&lt;/strong&gt; A growing backlog is your earliest signal that workers can't keep up — alert on it before users notice.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Skip the boilerplate
&lt;/h2&gt;

&lt;p&gt;Wiring up a queue, retries, idempotency helpers, a scheduler, and Docker for the worker is the same 200 lines every time. If you'd rather start from a production-ready setup than assemble it by hand, the &lt;a href="https://datanest-stores.pages.dev/python-developer/preview-async-task-queue-toolkit.html" rel="noopener noreferrer"&gt;Async Task Queue Toolkit&lt;/a&gt; packages worker configs, retry/idempotency patterns, and deployment recipes for exactly these four libraries so you can ship the feature instead of the plumbing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bottom line
&lt;/h2&gt;

&lt;p&gt;There's no single "best" Python task queue — there's the right one for your concurrency model and operational appetite. Match the tool to your app: &lt;strong&gt;Arq&lt;/strong&gt; for async, &lt;strong&gt;Dramatiq&lt;/strong&gt; for low-ceremony reliability, &lt;strong&gt;RQ&lt;/strong&gt; for simple, &lt;strong&gt;Celery&lt;/strong&gt; when you genuinely need its depth. Then spend your real effort on the parts that bite everyone equally — idempotency, retries, and graceful shutdown.&lt;/p&gt;

</description>
      <category>python</category>
      <category>celery</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>LLM Prompt Engineering Kit</title>
      <dc:creator>DatanestDigital</dc:creator>
      <pubDate>Mon, 23 Mar 2026 15:13:47 +0000</pubDate>
      <link>https://dev.to/datanestdigital/llm-prompt-engineering-kit-2jhf</link>
      <guid>https://dev.to/datanestdigital/llm-prompt-engineering-kit-2jhf</guid>
      <description>&lt;h1&gt;
  
  
  LLM Prompt Engineering Kit
&lt;/h1&gt;

&lt;p&gt;Prompts are the new code — and they deserve the same rigor. This kit provides a structured prompt template library, proven chain-of-thought patterns, a version control system for prompts, and an A/B testing framework to measure which prompts actually perform better. Stop guessing and start engineering your prompts systematically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Template Library&lt;/strong&gt; — 50+ battle-tested templates for classification, extraction, summarization, code generation, and creative writing with variable interpolation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chain-of-Thought Patterns&lt;/strong&gt; — Ready-to-use CoT, Tree-of-Thought, and self-consistency templates that improve reasoning accuracy by 15-40%&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Few-Shot Example Management&lt;/strong&gt; — Dynamic example selection based on semantic similarity to the input query&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Versioning&lt;/strong&gt; — Git-like version control for prompts with diff, rollback, and deployment tagging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A/B Testing Framework&lt;/strong&gt; — Split traffic between prompt variants, measure performance metrics, and declare winners with statistical significance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt Optimization&lt;/strong&gt; — Automated prompt refinement using DSPy-style optimization loops&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output Format Enforcement&lt;/strong&gt; — JSON schema constraints, regex validation, and structured output parsing built into templates&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;prompt_kit&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PromptTemplate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PromptRegistry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FewShotSelector&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Define a prompt template with variables
&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PromptTemplate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_classifier&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;You are a customer intent classifier. Classify the customer message
into exactly one of these categories: {categories}.
Respond with JSON: {{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;category&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &amp;lt;0.0-1.0&amp;gt;}}&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{customer_message}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;variables&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;categories&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;billing&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;technical&lt;/span&gt;&lt;span class="sh"&gt;"&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&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cancellation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;general&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;output_schema&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;number&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&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="c1"&gt;# 2. Render and use
&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;customer_message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;I can&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;t log into my account&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;prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;system&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Fully interpolated system prompt
&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;prompt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# "I can't log into my account"
&lt;/span&gt;
&lt;span class="c1"&gt;# 3. Parse and validate output
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_output&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;intent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;technical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;: 0.92}&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# "technical"
&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 0.92
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────────────────────┐
│            Prompt Registry                  │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐   │
│  │Template A│ │Template B│ │Template C│   │
│  │ v1.0     │ │ v2.1     │ │ v1.3     │   │
│  └────┬─────┘ └────┬─────┘ └────┬─────┘   │
│       │            │            │          │
│  ┌────▼────────────▼────────────▼──────┐   │
│  │         Version Control             │   │
│  │  Diff │ Rollback │ Tags │ History   │   │
│  └────────────────┬────────────────────┘   │
│                   │                        │
│  ┌────────────────▼────────────────────┐   │
│  │         A/B Test Engine             │   │
│  │  Split │ Measure │ Significance     │   │
│  └─────────────────────────────────────┘   │
└────────────────────────────────────────────┘
        │
        ▼
┌────────────────┐    ┌────────────────┐
│ Few-Shot       │    │ Output         │
│ Selector       │    │ Parser         │
│ (semantic sim) │    │ (JSON schema)  │
└────────────────┘    └────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Usage Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Chain-of-Thought Prompting
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;prompt_kit.patterns&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChainOfThought&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;SelfConsistency&lt;/span&gt;

&lt;span class="c1"&gt;# Standard CoT
&lt;/span&gt;&lt;span class="n"&gt;cot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ChainOfThought&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Solve this math problem step by step.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;examples&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What is 15% of 240?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reasoning&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;15% = 0.15. 0.15 × 240 = 36.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;answer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;36&lt;/span&gt;&lt;span class="sh"&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="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A store has a 25% off sale. The original price is $80. What is the sale price?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Self-consistency: Run CoT N times, take majority vote
&lt;/span&gt;&lt;span class="n"&gt;sc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SelfConsistency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;cot&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;num_samples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;aggregation&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;majority_vote&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# majority_vote | weighted
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;input&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;If a train travels 60mph for 2.5 hours, how far does it go?&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;# "150 miles"
&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;consistency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# 1.0 (all 5 samples agreed)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dynamic Few-Shot Selection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;prompt_kit&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FewShotSelector&lt;/span&gt;

&lt;span class="n"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FewShotSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;examples_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;examples/classification_examples.jsonl&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;embedding_model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text-embedding-3-small&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;num_examples&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;# Select 3 most similar examples
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Automatically picks the most relevant examples for this input
&lt;/span&gt;&lt;span class="n"&gt;examples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;My payment was declined but the charge still shows up&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Returns 3 examples semantically closest to billing/payment issues
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Prompt Versioning
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;prompt_kit&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PromptRegistry&lt;/span&gt;

&lt;span class="n"&gt;registry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PromptRegistry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompts/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Save a version
&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;production&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# List versions
&lt;/span&gt;&lt;span class="n"&gt;versions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;history&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_classifier&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;versions&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;timestamp&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;untagged&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Roll back
&lt;/span&gt;&lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rollback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_classifier&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;to_version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Diff two versions
&lt;/span&gt;&lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_classifier&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.2&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;diff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Shows exact prompt text changes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  A/B Testing Prompts
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;prompt_kit.ab_test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ABTest&lt;/span&gt;

&lt;span class="n"&gt;test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ABTest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;classifier_v1_vs_v2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;variants&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;control&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_classifier&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;treatment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customer_classifier&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1.2&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;traffic_split&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;control&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;treatment&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;metric&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;min_samples&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;significance_level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;0.05&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Route each request through the test
&lt;/span&gt;&lt;span class="n"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_456&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# ... use prompt, collect result ...
&lt;/span&gt;&lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;record&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user_456&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metric_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Correct classification
&lt;/span&gt;
&lt;span class="c1"&gt;# Check results
&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;report&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Control: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ± &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;control&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ci&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Treatment: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;treatment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; ± &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;treatment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ci&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Winner: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;winner&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; (p=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;p_value&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# prompt_kit_config.yaml&lt;/span&gt;
&lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompts/"&lt;/span&gt;            &lt;span class="c1"&gt;# Local directory for prompt files&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yaml"&lt;/span&gt;                 &lt;span class="c1"&gt;# yaml | json&lt;/span&gt;
  &lt;span class="na"&gt;auto_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;             &lt;span class="c1"&gt;# Auto-increment version on save&lt;/span&gt;

&lt;span class="na"&gt;few_shot&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;examples_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;examples/"&lt;/span&gt;
  &lt;span class="na"&gt;embedding_model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text-embedding-3-small"&lt;/span&gt;
  &lt;span class="na"&gt;num_examples&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;similarity_metric&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cosine"&lt;/span&gt;    &lt;span class="c1"&gt;# cosine | euclidean&lt;/span&gt;

&lt;span class="na"&gt;ab_testing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sqlite:///ab_tests.db"&lt;/span&gt;
  &lt;span class="na"&gt;default_traffic_split&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.5&lt;/span&gt;
  &lt;span class="na"&gt;min_samples_per_variant&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;500&lt;/span&gt;
  &lt;span class="na"&gt;significance_level&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.05&lt;/span&gt;
  &lt;span class="na"&gt;auto_promote_winner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;     &lt;span class="c1"&gt;# Require manual promotion&lt;/span&gt;

&lt;span class="na"&gt;output_parsing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;strict_mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;              &lt;span class="c1"&gt;# Fail on schema violation&lt;/span&gt;
  &lt;span class="na"&gt;max_retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;                 &lt;span class="c1"&gt;# Retry with corrective prompt&lt;/span&gt;
  &lt;span class="na"&gt;retry_model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-4o-mini"&lt;/span&gt;    &lt;span class="c1"&gt;# Cheap model for format fixing&lt;/span&gt;

&lt;span class="na"&gt;templates_dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;templates/"&lt;/span&gt;      &lt;span class="c1"&gt;# Pre-built template library&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Version every prompt change&lt;/strong&gt; — A single word change in a system prompt can shift behavior dramatically. Track every edit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use few-shot examples over long instructions&lt;/strong&gt; — Models learn patterns from examples better than from verbose rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test with edge cases&lt;/strong&gt; — Your prompt works on happy paths. Test it with empty inputs, adversarial inputs, and multi-language content.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure before declaring winners&lt;/strong&gt; — Run A/B tests to statistical significance (p &amp;lt; 0.05). Gut feeling is not a metric.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate concerns in prompts&lt;/strong&gt; — Keep persona, task instructions, format requirements, and examples in distinct sections.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pin model versions&lt;/strong&gt; — A prompt optimized for &lt;code&gt;gpt-4o-2024-08-06&lt;/code&gt; may perform differently on the next model release.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Output doesn't match JSON schema&lt;/td&gt;
&lt;td&gt;Model ignores schema constraints&lt;/td&gt;
&lt;td&gt;Add "You MUST respond with valid JSON" to system prompt and enable &lt;code&gt;max_retries&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Few-shot selector returns irrelevant examples&lt;/td&gt;
&lt;td&gt;Embedding model mismatch or too few examples&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;text-embedding-3-small&lt;/code&gt; and add 50+ diverse examples to the pool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A/B test never reaches significance&lt;/td&gt;
&lt;td&gt;Effect size too small or too few samples&lt;/td&gt;
&lt;td&gt;Increase &lt;code&gt;min_samples&lt;/code&gt; or accept that the variants are equivalent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt version rollback doesn't change behavior&lt;/td&gt;
&lt;td&gt;Application caching old prompt&lt;/td&gt;
&lt;td&gt;Clear application-level prompt cache after rollback&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is 1 of 11 resources in the &lt;a href="https://datanest-stores.pages.dev/ai-llm-toolkit/" rel="noopener noreferrer"&gt;AI Builder Pro&lt;/a&gt; toolkit.&lt;/strong&gt; Get the complete [LLM Prompt Engineering Kit] with all files, templates, and documentation for $39.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://buy.stripe.com/5kQcN5ayX0qqeRI7uhgjG2H" rel="noopener noreferrer"&gt;Get the Full Kit →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Or grab the entire AI Builder Pro bundle (11 products) for $169 — save 30%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://buy.stripe.com/eVq00j8qP3CC390cOBgjG2R" rel="noopener noreferrer"&gt;Get the Complete Bundle →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/thesius_code_7a136ae718b7/ai-agent-framework-3jh4"&gt;AI Agent Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/thesius_code_7a136ae718b7/ai-api-gateway-5dbn"&gt;AI API Gateway&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/thesius_code_7a136ae718b7/ai-safety-guardrails-kit-1d92"&gt;AI Safety &amp;amp; Guardrails Kit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>machinelearning</category>
      <category>python</category>
    </item>
    <item>
      <title>Experiment Tracking Pack</title>
      <dc:creator>DatanestDigital</dc:creator>
      <pubDate>Mon, 23 Mar 2026 15:13:34 +0000</pubDate>
      <link>https://dev.to/datanestdigital/experiment-tracking-pack-5bp9</link>
      <guid>https://dev.to/datanestdigital/experiment-tracking-pack-5bp9</guid>
      <description>&lt;h1&gt;
  
  
  Experiment Tracking Pack
&lt;/h1&gt;

&lt;p&gt;Production-ready experiment tracking with Weights &amp;amp; Biases and MLflow. Stop losing track of what you tried — log every hyperparameter, metric, and artifact automatically. Compare runs side-by-side, reproduce any experiment, and share results with your team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dual-backend tracking&lt;/strong&gt; — log to W&amp;amp;B and MLflow simultaneously with a unified API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom comparison dashboards&lt;/strong&gt; — pre-built templates for metric visualization across runs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hyperparameter sweep tracking&lt;/strong&gt; — structured logging for grid, random, and Bayesian searches&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artifact versioning&lt;/strong&gt; — automatically version model checkpoints, datasets, and configs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reproducibility configs&lt;/strong&gt; — capture environment, git hash, and random seeds per experiment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Team collaboration&lt;/strong&gt; — shared project dashboards with role-based access patterns&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alerting on metric regression&lt;/strong&gt; — configurable thresholds that flag degraded runs early&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Export and reporting&lt;/strong&gt; — generate PDF/HTML reports from tracked experiments&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Copy and edit the config&lt;/span&gt;
&lt;span class="nb"&gt;cp &lt;/span&gt;config.example.yaml config.yaml

&lt;span class="c"&gt;# 2. Set credentials&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;WANDB_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;YOUR_API_KEY_HERE
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MLFLOW_TRACKING_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:5000

&lt;span class="c"&gt;# 3. Run your first tracked experiment&lt;/span&gt;
python examples/tracked_experiment.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Minimal tracked training loop.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;wandb&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mlflow&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ExperimentTracker&lt;/span&gt;

&lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;learning_rate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;epochs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;batch_size&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;resnet18&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ExperimentTracker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image-classification&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wandb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mlflow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;run_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;baseline-v1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;epoch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;epochs&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
        &lt;span class="n"&gt;train_loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;train_one_epoch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataloader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;val_acc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val_loader&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;tracker&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train/loss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;train_loss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;val/accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;val_acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;epoch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;epoch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_artifact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model.pt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;artifact_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;model&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log_artifact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config.yaml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;artifact_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;experiment-tracking-pack/
├── config.example.yaml          # Tracking backend configuration
├── templates/
│   ├── tracker.py               # Unified ExperimentTracker class
│   ├── callbacks.py             # Training framework callbacks (PyTorch, sklearn)
│   ├── dashboards/              # Pre-built W&amp;amp;B dashboard JSON exports
│   │   ├── training_overview.json
│   │   └── hyperparam_comparison.json
│   └── reports/                 # Report generation templates
├── docs/
│   ├── overview.md              # Full architecture walkthrough
│   ├── patterns/                # Tracking patterns for common scenarios
│   └── checklists/
│       └── pre-deployment.md    # Go-live checklist
└── examples/
    ├── tracked_experiment.py    # Basic usage
    └── sweep_tracking.py        # Hyperparameter sweep logging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ExperimentTracker&lt;/code&gt; wraps both W&amp;amp;B and MLflow behind a single interface. You call &lt;code&gt;tracker.log()&lt;/code&gt; once and metrics flow to both backends. Switch backends by editing &lt;code&gt;config.yaml&lt;/code&gt; — zero code changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  PyTorch Lightning Callback
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ExperimentTracker&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytorch_lightning&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;pl&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TrackingCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Callback&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ExperimentTracker&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;on_train_epoch_end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pl_module&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;metrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;callback_metrics&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tracker&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train/loss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;train_loss&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;item&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;val/accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;val_acc&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;epoch&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;trainer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;current_epoch&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;h3&gt;
  
  
  Comparing Runs Programmatically
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ExperimentTracker&lt;/span&gt;

&lt;span class="n"&gt;tracker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ExperimentTracker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;image-classification&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;runs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_runs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filters&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tag&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;baseline&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;val/accuracy&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="mi"&gt;5&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;: acc=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;val/accuracy&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lr=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;learning_rate&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config.example.yaml&lt;/span&gt;
&lt;span class="na"&gt;project_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;my-ml-project"&lt;/span&gt;

&lt;span class="na"&gt;backends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;wandb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;entity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;your-team"&lt;/span&gt;         &lt;span class="c1"&gt;# W&amp;amp;B team or username&lt;/span&gt;
    &lt;span class="na"&gt;log_model&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;             &lt;span class="c1"&gt;# Upload model artifacts&lt;/span&gt;
    &lt;span class="na"&gt;log_code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;              &lt;span class="c1"&gt;# Snapshot source code&lt;/span&gt;

  &lt;span class="na"&gt;mlflow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;tracking_uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:5000"&lt;/span&gt;
    &lt;span class="na"&gt;registry_uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sqlite:///mlflow.db"&lt;/span&gt;
    &lt;span class="na"&gt;auto_log&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;              &lt;span class="c1"&gt;# Enable MLflow autologging&lt;/span&gt;

&lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;log_frequency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;             &lt;span class="c1"&gt;# Log every N steps&lt;/span&gt;
  &lt;span class="na"&gt;log_system_metrics&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;      &lt;span class="c1"&gt;# GPU utilization, memory&lt;/span&gt;
  &lt;span class="na"&gt;capture_git_hash&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;        &lt;span class="c1"&gt;# Record git commit&lt;/span&gt;
  &lt;span class="na"&gt;capture_env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;             &lt;span class="c1"&gt;# Record pip freeze&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Log config at run start&lt;/strong&gt; — always pass your full hyperparameter dict to the tracker before training begins&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use tags, not names, for filtering&lt;/strong&gt; — run names should be human-readable; use tags like &lt;code&gt;["baseline", "v2", "augmented"]&lt;/code&gt; for programmatic queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set metric summary modes&lt;/strong&gt; — configure W&amp;amp;B to track &lt;code&gt;min(val/loss)&lt;/code&gt; and &lt;code&gt;max(val/accuracy)&lt;/code&gt; for leaderboard views&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version your tracking config&lt;/strong&gt; — commit &lt;code&gt;config.yaml&lt;/code&gt; to git so experiment setup is reproducible&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use run groups for sweeps&lt;/strong&gt; — group related hyperparameter search runs for cleaner dashboards&lt;/li&gt;
&lt;/ol&gt;

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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;wandb: ERROR Run initialization failed&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Invalid API key or network issue&lt;/td&gt;
&lt;td&gt;Verify &lt;code&gt;WANDB_API_KEY&lt;/code&gt; with &lt;code&gt;wandb login --verify&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Metrics not appearing in MLflow UI&lt;/td&gt;
&lt;td&gt;Wrong &lt;code&gt;tracking_uri&lt;/code&gt; or MLflow server down&lt;/td&gt;
&lt;td&gt;Check &lt;code&gt;mlflow server&lt;/code&gt; is running; test with &lt;code&gt;curl $MLFLOW_TRACKING_URI/api/2.0/mlflow/experiments/list&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Duplicate runs on resume&lt;/td&gt;
&lt;td&gt;Missing &lt;code&gt;resume&lt;/code&gt; flag&lt;/td&gt;
&lt;td&gt;Set &lt;code&gt;tracker.start_run(resume="must")&lt;/code&gt; for resumed training&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Slow logging with large artifacts&lt;/td&gt;
&lt;td&gt;Synchronous upload blocking training&lt;/td&gt;
&lt;td&gt;Enable &lt;code&gt;async_upload: true&lt;/code&gt; in config or log artifacts only at end of run&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;This is 1 of 10 resources in the &lt;a href="https://datanest-stores.pages.dev/ai-ml-starter/" rel="noopener noreferrer"&gt;ML Starter Kit&lt;/a&gt; toolkit.&lt;/strong&gt; Get the complete [Experiment Tracking Pack] with all files, templates, and documentation for $29.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://buy.stripe.com/aFadR9gXl1uueRIaGtgjG2V" rel="noopener noreferrer"&gt;Get the Full Kit →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Or grab the entire ML Starter Kit bundle (10 products) for $149 — save 30%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://buy.stripe.com/28EdR9dL93CC9xocOBgjG32" rel="noopener noreferrer"&gt;Get the Complete Bundle →&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Related Articles
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/thesius_code_7a136ae718b7/feature-store-bootstrap-25ci"&gt;Feature Store Bootstrap&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/thesius_code_7a136ae718b7/gpu-training-toolkit-478n"&gt;GPU Training Toolkit&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/thesius_code_7a136ae718b7/hyperparameter-tuning-kit-4ioa"&gt;Hyperparameter Tuning Kit&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>machinelearning</category>
      <category>ai</category>
      <category>python</category>
      <category>datascience</category>
    </item>
  </channel>
</rss>
