<?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: Egor</title>
    <description>The latest articles on DEV Community by Egor (@coffee1337).</description>
    <link>https://dev.to/coffee1337</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%2F4179627%2F03e9e6fb-69b2-49f5-b890-5dda2f827732.png</url>
      <title>DEV Community: Egor</title>
      <link>https://dev.to/coffee1337</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/coffee1337"/>
    <language>en</language>
    <item>
      <title>Claude vs Gemini vs GPT: What a 2026 Study of 26 AI Models Reveals About Long Context</title>
      <dc:creator>Egor</dc:creator>
      <pubDate>Wed, 14 Oct 2026 14:28:14 +0000</pubDate>
      <link>https://dev.to/coffee1337/claude-vs-gemini-vs-gpt-what-a-2026-study-of-26-ai-models-reveals-about-long-context-57i</link>
      <guid>https://dev.to/coffee1337/claude-vs-gemini-vs-gpt-what-a-2026-study-of-26-ai-models-reveals-about-long-context-57i</guid>
      <description>&lt;p&gt;&lt;em&gt;26 AI models. 6,438 evaluation instances. Contexts up to 1 million tokens. The results challenge how we compare modern language models.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Imagine giving an AI assistant an entire software repository.&lt;/p&gt;

&lt;p&gt;Thousands of files. Hundreds of modules. API documentation, database schemas, and years of development history.&lt;/p&gt;

&lt;p&gt;The model accepts everything without complaining.&lt;/p&gt;

&lt;p&gt;You ask it to identify a subtle architectural problem.&lt;/p&gt;

&lt;p&gt;It confidently provides an answer.&lt;/p&gt;

&lt;p&gt;But is that answer actually reliable?&lt;/p&gt;

&lt;p&gt;In 2026, the question is no longer simply whether a language model can accept a million tokens.&lt;/p&gt;

&lt;p&gt;The more interesting question is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much of that context can the model actually use correctly?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A recent research paper, &lt;em&gt;ATLAS: All-round Testing of Long-context Abilities across Scales&lt;/em&gt;, provides some surprising answers.&lt;/p&gt;

&lt;p&gt;The researchers evaluated 26 models on 6,438 instances across eight capability dimensions, considering input lengths from 8K to 1M tokens.&lt;/p&gt;

&lt;p&gt;One finding stands out:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemini 3.1 Pro Preview led at 128K, but Claude Opus 4.6 led at 1M.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The best model depends on how you measure it.&lt;/p&gt;

&lt;p&gt;Let's examine what the researchers tested, what their results actually mean, and why these findings matter for developers building real AI systems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Research note: This article analyzes published 2026 research. It is not an independent benchmark, and it does not claim that the author personally tested all 26 models.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  1. The Problem With AI Benchmarks
&lt;/h2&gt;

&lt;p&gt;When comparing language models, we often see a familiar table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Context window&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Model A&lt;/td&gt;
&lt;td&gt;128K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model B&lt;/td&gt;
&lt;td&gt;256K&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Model C&lt;/td&gt;
&lt;td&gt;1M&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The implication seems obvious.&lt;/p&gt;

&lt;p&gt;Model C can process more information, so it must be better at large-document tasks.&lt;/p&gt;

&lt;p&gt;But this reasoning contains a hidden assumption:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That accepting more information means using that information effectively.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider two different tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Task A: Simple retrieval&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A document contains the sentence:&lt;/p&gt;

&lt;p&gt;"The deployment identifier is QZ-7194."&lt;/p&gt;

&lt;p&gt;The model is asked to retrieve that identifier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Task B: Complex reasoning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A repository contains dozens of service definitions, configuration files, and dependency relationships.&lt;/p&gt;

&lt;p&gt;The model must identify which changes could introduce a security regression.&lt;/p&gt;

&lt;p&gt;The second task requires much more than finding one string.&lt;/p&gt;

&lt;p&gt;It may involve combining evidence from many locations, resolving contradictions, and understanding dependencies.&lt;/p&gt;

&lt;p&gt;A model that performs well on Task A might struggle on Task B.&lt;/p&gt;

&lt;p&gt;That's exactly why evaluating long-context AI requires more than a single benchmark number.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. ATLAS: A Different Way to Measure Long-Context Intelligence
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Research:&lt;/strong&gt; &lt;em&gt;ATLAS: All-round Testing of Long-context Abilities across Scales&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authors:&lt;/strong&gt; Deli Huang and colleagues&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Published:&lt;/strong&gt; May 27, 2026&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Paper:&lt;/strong&gt; &lt;a href="https://arxiv.org/abs/2605.28079" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2605.28079&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The ATLAS researchers investigated two major weaknesses in traditional long-context evaluation.&lt;/p&gt;

&lt;p&gt;First, model performance can deteriorate as input length increases.&lt;/p&gt;

&lt;p&gt;Second, success on foundational tasks such as retrieval does not necessarily transfer to more complex application workloads.&lt;/p&gt;

&lt;p&gt;Their framework introduces several methodological improvements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Evaluating across context lengths
&lt;/h3&gt;

&lt;p&gt;Instead of measuring performance at only one input size, ATLAS considers scores across a predefined context-length grid, from 8K to 1M tokens.&lt;/p&gt;

&lt;p&gt;This makes it possible to analyze how performance changes as more information enters the prompt.&lt;/p&gt;

&lt;p&gt;A model that performs exceptionally well at 128K may deteriorate more sharply than another model at longer lengths.&lt;/p&gt;

&lt;h3&gt;
  
  
  Separating foundational abilities from applications
&lt;/h3&gt;

&lt;p&gt;ATLAS distinguishes foundational operations from downstream workloads.&lt;/p&gt;

&lt;p&gt;This matters because retrieving information and applying it correctly are different capabilities.&lt;/p&gt;

&lt;p&gt;For instance, a model may identify relevant functions in a repository without understanding how they interact.&lt;/p&gt;

&lt;h3&gt;
  
  
  Measuring balanced performance
&lt;/h3&gt;

&lt;p&gt;The researchers introduce ATLAScore, a harmonic-mean-based aggregate across capability categories.&lt;/p&gt;

&lt;p&gt;This is intended to penalize highly imbalanced capability profiles.&lt;/p&gt;

&lt;p&gt;A model that dominates one task but struggles on others should not automatically receive the strongest overall evaluation.&lt;/p&gt;

&lt;p&gt;The framework also includes uncertainty propagation when computing its final aggregate scores.&lt;/p&gt;

&lt;p&gt;Together, these changes provide a more nuanced picture than a single retrieval accuracy number.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Result: The Leader Changes at 1 Million Tokens
&lt;/h2&gt;

&lt;p&gt;One of the most interesting findings concerns model rankings.&lt;/p&gt;

&lt;p&gt;According to the ATLAS paper:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Evaluation scope&lt;/th&gt;
&lt;th&gt;Leading model&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Up to 128K tokens&lt;/td&gt;
&lt;td&gt;Gemini 3.1 Pro Preview&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Up to 1M tokens&lt;/td&gt;
&lt;td&gt;Claude Opus 4.6&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7130pcshtryuxn2a7cjh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7130pcshtryuxn2a7cjh.png" alt="ATLAS 2026: Claude vs Gemini long-context benchmark" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This does not mean Claude is universally better than Gemini.&lt;/p&gt;

&lt;p&gt;Nor does it mean Gemini is inherently unreliable.&lt;/p&gt;

&lt;p&gt;It means the relative ranking changes under the specific evaluation conditions used by ATLAS.&lt;/p&gt;

&lt;p&gt;The researchers also reported that &lt;strong&gt;seven models changed position by at least two ranking places&lt;/strong&gt; when the evaluation expanded from the 8K–128K range to the 8K–1M range.&lt;/p&gt;

&lt;p&gt;That's important.&lt;/p&gt;

&lt;p&gt;Imagine selecting a model for your application after reviewing results obtained only at 128K.&lt;/p&gt;

&lt;p&gt;Your production workload, however, frequently processes 600K-token documents.&lt;/p&gt;

&lt;p&gt;The benchmark you used may not reflect your actual operating conditions.&lt;/p&gt;

&lt;p&gt;And if models deteriorate at different rates, their relative performance at 600K may differ from their relative performance at 128K.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The key insight: model quality is not independent of context length.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A useful benchmark should measure the range your application actually uses.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Why Context Length Changes the Difficulty
&lt;/h2&gt;

&lt;p&gt;Let's examine a simplified example.&lt;/p&gt;

&lt;p&gt;Imagine this information appears inside a long repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;service_a.py:
Service A sends requests to Service B.

service_b.py:
Service B uses Database C.

config.yaml:
Database C has public network access enabled.

security_policy.md:
Production databases must not expose public access.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now ask:&lt;/p&gt;

&lt;p&gt;"Does Service A indirectly depend on a database with a potentially unsafe network configuration?"&lt;/p&gt;

&lt;p&gt;Answering correctly requires several steps.&lt;/p&gt;

&lt;p&gt;First, identify the dependency between Service A and Service B.&lt;/p&gt;

&lt;p&gt;Second, identify Service B's database.&lt;/p&gt;

&lt;p&gt;Third, find Database C's configuration.&lt;/p&gt;

&lt;p&gt;Finally, compare that configuration with the security policy.&lt;/p&gt;

&lt;p&gt;No single sentence contains the entire answer.&lt;/p&gt;

&lt;p&gt;As the context grows, the model must identify relevant information among more irrelevant material while preserving the connections between different pieces of evidence.&lt;/p&gt;

&lt;p&gt;This creates distinct possible failure modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing one of the necessary pieces of evidence&lt;/li&gt;
&lt;li&gt;Retrieving a similar but incorrect detail&lt;/li&gt;
&lt;li&gt;Correctly retrieving the evidence but connecting it incorrectly&lt;/li&gt;
&lt;li&gt;Producing a plausible conclusion unsupported by the source material&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The example is illustrative, not an ATLAS test case.&lt;/p&gt;

&lt;p&gt;But it demonstrates why longer context can increase difficulty even when every relevant document fits into the model's advertised context window.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Another 2026 Study Reveals a Deeper Problem
&lt;/h2&gt;

&lt;p&gt;ATLAS evaluates long-context capabilities across models and scales.&lt;/p&gt;

&lt;p&gt;A separate 2026 study, &lt;em&gt;CorpusQA: A 10 Million Token Benchmark for Corpus-Level Analysis and Reasoning&lt;/em&gt;, investigates a different question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can AI systems analyze entire collections of documents when the evidence is distributed throughout the collection?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Authors:&lt;/strong&gt; Zhiyuan Lu and colleagues&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Originally published:&lt;/strong&gt; January 21, 2026&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Revised:&lt;/strong&gt; April 25, 2026&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Paper:&lt;/strong&gt; &lt;a href="https://arxiv.org/abs/2601.14952" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2601.14952&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CorpusQA includes tasks involving financial, educational, and real-estate documents.&lt;/p&gt;

&lt;p&gt;Some questions require aggregating numerical information from many documents.&lt;/p&gt;

&lt;p&gt;For example, imagine analyzing hundreds of financial reports to identify every company satisfying several accounting conditions.&lt;/p&gt;

&lt;p&gt;A traditional retrieval system might find a few relevant documents.&lt;/p&gt;

&lt;p&gt;But if answering the question requires analyzing every company, retrieving only five documents is insufficient.&lt;/p&gt;

&lt;p&gt;This is a fundamentally different problem from finding one answer-bearing passage.&lt;/p&gt;

&lt;h3&gt;
  
  
  The published results
&lt;/h3&gt;

&lt;p&gt;CorpusQA evaluated several models at different context lengths.&lt;/p&gt;

&lt;p&gt;For Gemini 2.5 Pro, the authors reported:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Context length&lt;/th&gt;
&lt;th&gt;Overall accuracy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;128K&lt;/td&gt;
&lt;td&gt;80.19%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1M&lt;/td&gt;
&lt;td&gt;50.73%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5z0yyj2ny1jxtfw2x5tq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5z0yyj2ny1jxtfw2x5tq.png" alt="CorpusQA 2026: Gemini 2.5 Pro accuracy decline from 128K to 1M tokens" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These results show a decline of 29.46 percentage points under the study's conditions.&lt;/p&gt;

&lt;p&gt;Importantly, CorpusQA evaluated Gemini 2.5 Pro, not Gemini 3.1 Pro.&lt;/p&gt;

&lt;p&gt;Its results cannot be directly substituted for the newer model's performance.&lt;/p&gt;

&lt;p&gt;But they illustrate how difficult corpus-level analysis can remain, even when a model supports very long inputs.&lt;/p&gt;

&lt;p&gt;The study also found that conventional retrieval-augmented generation performed poorly on tasks requiring globally distributed evidence.&lt;/p&gt;

&lt;p&gt;The lesson isn't that RAG is useless.&lt;/p&gt;

&lt;p&gt;It's that &lt;strong&gt;retrieving a handful of relevant chunks is not the same as analyzing an entire document collection.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Why Traditional RAG Can Fail on Global Questions
&lt;/h2&gt;

&lt;p&gt;A typical RAG pipeline looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User question
      |
      v
Vector search
      |
      v
Top-K relevant chunks
      |
      v
LLM
      |
      v
Generated answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works well when a question can be answered using a relatively small number of relevant passages.&lt;/p&gt;

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

&lt;p&gt;"What is the API rate limit?"&lt;/p&gt;

&lt;p&gt;A good retrieval system can locate the appropriate documentation paragraph and pass it to the model.&lt;/p&gt;

&lt;p&gt;Now consider another question:&lt;/p&gt;

&lt;p&gt;"Across all customer reports from the last year, which failure category increased the most?"&lt;/p&gt;

&lt;p&gt;The answer requires a global aggregation.&lt;/p&gt;

&lt;p&gt;Retrieving only the most semantically similar documents may exclude a large part of the dataset.&lt;/p&gt;

&lt;p&gt;No matter how capable the LLM is, it cannot correctly aggregate information it never receives.&lt;/p&gt;

&lt;p&gt;This is an information-coverage problem, not merely a language-model problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft0ymoxqlhpeclumpzyu0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ft0ymoxqlhpeclumpzyu0.png" alt="CorpusQA 2026: RAG vs Memory Agent performance at 1M tokens" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the CorpusQA evaluation at 1M tokens, standard RAG achieved 17.38% accuracy, compared with 44.95% for the Memory Agent approach using Gemini 2.5 Pro.&lt;/p&gt;

&lt;p&gt;This illustrates how different approaches to managing large document collections can significantly affect performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  A better architecture for some analytical tasks
&lt;/h3&gt;

&lt;p&gt;For questions requiring exact aggregation, a structured processing pipeline may be more appropriate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Document collection
        |
        v
Information extraction
        |
        v
Structured records
        |
        v
SQL / analytical computation
        |
        v
Evidence verification
        |
        v
LLM-generated explanation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This design separates factual computation from language generation.&lt;/p&gt;

&lt;p&gt;The LLM can interpret the user's question and explain the result, while deterministic code handles calculations.&lt;/p&gt;

&lt;p&gt;Of course, the approach has its own limitations.&lt;/p&gt;

&lt;p&gt;Extraction errors, incomplete schemas, and stale data can still produce incorrect answers.&lt;/p&gt;

&lt;p&gt;But it creates opportunities to validate individual steps instead of relying entirely on one enormous prompt.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. What About Vendor Benchmarks?
&lt;/h2&gt;

&lt;p&gt;Independent research is only one source of evidence.&lt;/p&gt;

&lt;p&gt;Model developers also publish technical evaluations.&lt;/p&gt;

&lt;p&gt;For example, Anthropic's &lt;em&gt;Claude Opus 4.6 System Card&lt;/em&gt; discusses long-context benchmarks such as MRCR and GraphWalks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://www.anthropic.com/system-cards" rel="noopener noreferrer"&gt;https://www.anthropic.com/system-cards&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;MRCR evaluates the ability to locate and distinguish between similar pieces of information across long inputs.&lt;/p&gt;

&lt;p&gt;GraphWalks examines structured reasoning over graph-like information represented within the context.&lt;/p&gt;

&lt;p&gt;These evaluations are useful because they test capabilities beyond ordinary question answering.&lt;/p&gt;

&lt;p&gt;However, developer-published benchmark results need careful interpretation.&lt;/p&gt;

&lt;p&gt;Providers may use different inference settings, reasoning budgets, input limits, filtering rules, or evaluation configurations.&lt;/p&gt;

&lt;p&gt;A benchmark score obtained under one provider's setup should not automatically be treated as directly comparable with an independent benchmark.&lt;/p&gt;

&lt;p&gt;In particular, the ability to answer a question using a long prompt depends on more than the model checkpoint.&lt;/p&gt;

&lt;p&gt;It can also depend on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reasoning budget&lt;/li&gt;
&lt;li&gt;Tool usage&lt;/li&gt;
&lt;li&gt;Prompt construction&lt;/li&gt;
&lt;li&gt;Context truncation&lt;/li&gt;
&lt;li&gt;Sampling configuration&lt;/li&gt;
&lt;li&gt;Dataset filtering&lt;/li&gt;
&lt;li&gt;Evaluation metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These differences can materially affect observed results.&lt;/p&gt;

&lt;p&gt;That's one reason ATLAS is interesting: its evaluation explicitly attempts to compare long-context capabilities across multiple tasks and lengths.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. What This Means for AI Coding Assistants
&lt;/h2&gt;

&lt;p&gt;Consider an AI coding assistant with access to a large repository.&lt;/p&gt;

&lt;p&gt;There are two possible designs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach A: Put everything in the prompt
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Entire repository
       |
       v
Large context window
       |
       v
LLM response
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is conceptually simple.&lt;/p&gt;

&lt;p&gt;But it can be expensive and may introduce large amounts of irrelevant information.&lt;/p&gt;

&lt;p&gt;It also doesn't guarantee that the model will correctly connect all important details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach B: Search, inspect, and verify
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User question
      |
      v
Repository search
      |
      v
Relevant files
      |
      v
Dependency analysis
      |
      v
LLM reasoning
      |
      v
Tool-based verification
      |
      v
Final answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach can use search tools, static analysis, tests, and targeted context retrieval.&lt;/p&gt;

&lt;p&gt;For some software engineering tasks, those tools may provide more reliable evidence than simply expanding the prompt.&lt;/p&gt;

&lt;p&gt;For instance, when checking whether a function is actually called, repository search can identify references.&lt;/p&gt;

&lt;p&gt;When checking whether a proposed patch works, an automated test can provide execution evidence.&lt;/p&gt;

&lt;p&gt;An LLM can assist with interpretation, but it doesn't have to perform every operation internally.&lt;/p&gt;

&lt;p&gt;The broader principle is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use the context window as one resource in an engineering system, not as a replacement for the system itself.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  9. How I Would Benchmark Modern AI Models
&lt;/h2&gt;

&lt;p&gt;The published results suggest a practical evaluation protocol for comparing models on real development workflows.&lt;/p&gt;

&lt;p&gt;I would test each model across four dimensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test A: Context length
&lt;/h3&gt;

&lt;p&gt;Evaluate the same task family at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8K
32K
128K
256K
512K
1M
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only include lengths genuinely supported by each tested model and API configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test B: Evidence position
&lt;/h3&gt;

&lt;p&gt;Place answer-relevant information near the beginning, middle, and end of the input.&lt;/p&gt;

&lt;p&gt;Measure whether accuracy changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test C: Reasoning complexity
&lt;/h3&gt;

&lt;p&gt;Use several difficulty levels:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 1:&lt;/strong&gt; Retrieve one explicitly stated fact.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 2:&lt;/strong&gt; Connect information from two files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 3:&lt;/strong&gt; Combine evidence from several documents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 4:&lt;/strong&gt; Perform global analysis across an entire corpus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test D: Tool-assisted reasoning
&lt;/h3&gt;

&lt;p&gt;Compare direct long-context prompting against workflows using retrieval, code execution, or other verification tools.&lt;/p&gt;

&lt;p&gt;For every experiment, record:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Answer accuracy&lt;/td&gt;
&lt;td&gt;Measures correctness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context length&lt;/td&gt;
&lt;td&gt;Reveals scale sensitivity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input/output tokens&lt;/td&gt;
&lt;td&gt;Measures resource usage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Latency&lt;/td&gt;
&lt;td&gt;Measures responsiveness&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;Measures practical efficiency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evidence completeness&lt;/td&gt;
&lt;td&gt;Checks whether necessary information was found&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Model versions and inference settings should be fixed and documented.&lt;/p&gt;

&lt;p&gt;Tasks should be repeated using multiple independent examples, with uncertainty estimates reported alongside aggregate scores.&lt;/p&gt;

&lt;p&gt;This is a proposed experimental design, not a claim that these tests have already been run for this article.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Limitations and Research Caveats
&lt;/h2&gt;

&lt;p&gt;There are several reasons to interpret these findings carefully.&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;ATLAS is a May 2026 preprint&lt;/strong&gt;. Its results are useful research evidence, but they should not be mistaken for a universally accepted model ranking.&lt;/p&gt;

&lt;p&gt;Second, the comparison covers a specific collection of 26 models and evaluation settings. It does not establish the performance of every model released later in 2026.&lt;/p&gt;

&lt;p&gt;Third, CorpusQA evaluates a different set of models and tasks. Its numerical results cannot be directly compared with ATLAS scores.&lt;/p&gt;

&lt;p&gt;Fourth, performance at 1M tokens may involve implementation-specific constraints, including inference settings and context handling.&lt;/p&gt;

&lt;p&gt;Finally, benchmark results do not automatically predict real-world performance.&lt;/p&gt;

&lt;p&gt;An AI system answering legal questions, writing code, analyzing scientific papers, or processing financial records may require very different capabilities.&lt;/p&gt;

&lt;p&gt;The correct model and architecture should be selected through task-specific evaluation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: We Need Better Questions Than "How Many Tokens?"
&lt;/h2&gt;

&lt;p&gt;The rapid growth of context windows is an important technological achievement.&lt;/p&gt;

&lt;p&gt;But the 2026 research tells a more complicated story.&lt;/p&gt;

&lt;p&gt;The ATLAS study found that model rankings change when evaluation extends toward one million tokens.&lt;/p&gt;

&lt;p&gt;CorpusQA demonstrated the difficulty of analyzing large document collections when relevant evidence is dispersed across many sources.&lt;/p&gt;

&lt;p&gt;Together, these studies suggest that increasing context capacity alone is not enough to guarantee reliable information processing.&lt;/p&gt;

&lt;p&gt;For developers, the practical implications are straightforward:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Don't select an LLM based only on advertised context size.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Evaluate it at the lengths your application actually uses.&lt;/p&gt;

&lt;p&gt;Test retrieval and reasoning separately.&lt;/p&gt;

&lt;p&gt;Measure the quality of evidence, not only the fluency of the generated answer.&lt;/p&gt;

&lt;p&gt;And consider whether structured tools, retrieval systems, or deterministic computation can make your application more reliable.&lt;/p&gt;

&lt;p&gt;The future of long-context AI may not be about putting everything into one prompt.&lt;/p&gt;

&lt;p&gt;It may be about building systems that know &lt;strong&gt;what to retrieve, what to remember, what to compute, and what to verify.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A million-token context window is a capability.&lt;/p&gt;

&lt;p&gt;Reliable reasoning over a million tokens is a different achievement.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;[1] ATLAS — All-round Testing of Long-context Abilities across Scales (2026)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deli Huang et al.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://arxiv.org/abs/2605.28079" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2605.28079&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[2] CorpusQA — A 10 Million Token Benchmark for Corpus-Level Analysis and Reasoning (2026)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Zhiyuan Lu et al.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://arxiv.org/abs/2601.14952" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2601.14952&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[3] Claude Opus 4.6 System Card (2026)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Anthropic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.anthropic.com/system-cards" rel="noopener noreferrer"&gt;https://www.anthropic.com/system-cards&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;Do you think future AI systems will rely primarily on larger context windows, or will retrieval, memory, and tool-assisted reasoning become more important?&lt;/p&gt;

&lt;p&gt;And have you encountered cases where an LLM missed important information even though it was present in the prompt?&lt;/p&gt;

&lt;p&gt;I'd be interested in hearing about real-world experiences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/coffee1337" rel="noopener noreferrer"&gt;https://github.com/coffee1337&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Portfolio:&lt;/strong&gt; &lt;a href="https://coffee1337.github.io/" rel="noopener noreferrer"&gt;https://coffee1337.github.io/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>machinelearning</category>
      <category>openai</category>
    </item>
    <item>
      <title>I Made Python 2,000x Faster by Changing One Line of Code</title>
      <dc:creator>Egor</dc:creator>
      <pubDate>Wed, 14 Oct 2026 08:33:28 +0000</pubDate>
      <link>https://dev.to/coffee1337/i-made-python-2000x-faster-by-changing-one-line-of-code-4gm0</link>
      <guid>https://dev.to/coffee1337/i-made-python-2000x-faster-by-changing-one-line-of-code-4gm0</guid>
      <description>&lt;p&gt;&lt;em&gt;No caching. No multiprocessing. No C extensions. Just choosing the right data structure.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Python has a reputation for being slow. But sometimes the problem isn't Python itself: it's the data structure we choose.&lt;/p&gt;

&lt;p&gt;I compared two ways to answer one question: &lt;strong&gt;is this ID already in the collection?&lt;/strong&gt; In one benchmark run, changing the collection from a &lt;code&gt;list&lt;/code&gt; to a &lt;code&gt;set&lt;/code&gt; made repeated unsuccessful lookups about &lt;strong&gt;2,378 times faster&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This isn't a claim about general API performance. It's a specific, reproducible experiment. Let's see why it happens.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Imagine a service that holds 10,000 integer IDs and needs to check thousands of incoming IDs.&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;user_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10_000&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;user_exists&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;return&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;user_ids&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is correct code. But how does it behave when most lookups miss?&lt;/p&gt;

&lt;h2&gt;
  
  
  The benchmark
&lt;/h2&gt;

&lt;p&gt;Here's the complete benchmark (also available as a separate &lt;code&gt;.py&lt;/code&gt; 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;platform&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;statistics&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;

&lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10_000&lt;/span&gt;
&lt;span class="n"&gt;Q&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5_000&lt;/span&gt;

&lt;span class="n"&gt;ids_list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&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;N&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;ids_set&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;queries&lt;/span&gt; &lt;span class="o"&gt;=&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;N&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Q&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# All queries are misses.
&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;using_list&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;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ids_list&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;queries&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;using_set&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;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ids_set&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;q&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;queries&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;using_list&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nf"&gt;using_set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;benchmark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;timings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;timeit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;repeat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;7&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;statistics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;median&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="n"&gt;list_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;benchmark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;using_list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;set_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;benchmark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;using_set&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;Python: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;python_version&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;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;List: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;list_time&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&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; ms&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;Set:  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;set_time&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&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; ms&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;Speedup: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;list_time&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;set_time&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;,.&lt;/span&gt;&lt;span class="mi"&gt;0&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;x&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;The collection contains &lt;strong&gt;10,000 integer IDs&lt;/strong&gt;. Every one of the &lt;strong&gt;5,000 queries is absent&lt;/strong&gt;, so this tests a particularly unfavorable case for a list. The structures are built before timing begins. Each function is run seven times, and the median is reported.&lt;/p&gt;

&lt;h2&gt;
  
  
  The results
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa6vr62cpx5i9wj2ljz3v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa6vr62cpx5i9wj2ljz3v.png" alt="Log-scale chart of list versus set membership timings for absent integer IDs" width="800" height="484"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the test environment (Python &lt;strong&gt;3.13.5&lt;/strong&gt;), I measured:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Median time&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;list&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;350.413 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;set&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0.147 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ratio&lt;/td&gt;
&lt;td&gt;~2,378×&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are measurements from &lt;strong&gt;one execution environment&lt;/strong&gt;, not universal performance constants. Rerunning the test may produce different values.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does &lt;code&gt;list&lt;/code&gt; take longer?
&lt;/h2&gt;

&lt;p&gt;For a list, &lt;code&gt;x in values&lt;/code&gt; scans its elements until it finds &lt;code&gt;x&lt;/code&gt; or reaches the end. Since all our test queries miss, each check requires a full scan: &lt;strong&gt;O(n)&lt;/strong&gt; per lookup.&lt;/p&gt;

&lt;p&gt;By contrast, a set uses hashing. For ordinary integer keys and typical hashing behavior, its membership check is &lt;strong&gt;O(1) on average&lt;/strong&gt;. That does not mean sets can never slow down; collisions and unusual inputs matter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2x8ia249if81kodo7x9y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2x8ia249if81kodo7x9y.png" alt="Simplified diagram comparing linear list search with hash-based set lookup" width="799" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The one-line change
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Before
&lt;/span&gt;&lt;span class="n"&gt;user_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="c1"&gt;# After
&lt;/span&gt;&lt;span class="n"&gt;user_ids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The membership expression stays identical:&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;if&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;user_ids&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;p&gt;The algorithmic cost, however, is different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't replace every list with a set
&lt;/h2&gt;

&lt;p&gt;Sets have trade-offs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They don't keep duplicate elements.&lt;/li&gt;
&lt;li&gt;They don't support positional indexing.&lt;/li&gt;
&lt;li&gt;Their elements must be hashable.&lt;/li&gt;
&lt;li&gt;They typically use more memory.&lt;/li&gt;
&lt;li&gt;Constructing a set takes time, so the conversion may not pay off for a single lookup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, the test deliberately uses &lt;strong&gt;only missing IDs&lt;/strong&gt;. A list can find an element at the beginning quickly, and on tiny collections a list may be perfectly adequate. Ordering, duplicate values, and memory use may matter more than lookup speed.&lt;/p&gt;

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

&lt;p&gt;Before adding threads, caches, or new infrastructure, ask whether your hot path uses the right data structure.&lt;/p&gt;

&lt;p&gt;This benchmark does &lt;strong&gt;not&lt;/strong&gt; show that a FastAPI endpoint will become 2,000× faster. It shows that repeated in-memory membership checks can differ dramatically depending on the collection type and workload.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Measure your actual bottleneck, then optimize it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What is the biggest speedup you've achieved by changing a surprisingly small amount of code?&lt;/p&gt;




&lt;p&gt;&lt;a href="https://github.com/coffee1337" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://coffee1337.github.io/" rel="noopener noreferrer"&gt;Portfolio&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>performance</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>This 3-Line Python Mistake Can Disable Your Entire API Authentication</title>
      <dc:creator>Egor</dc:creator>
      <pubDate>Tue, 13 Oct 2026 18:24:18 +0000</pubDate>
      <link>https://dev.to/coffee1337/this-3-line-python-mistake-can-disable-your-entire-api-authentication-5eip</link>
      <guid>https://dev.to/coffee1337/this-3-line-python-mistake-can-disable-your-entire-api-authentication-5eip</guid>
      <description>&lt;p&gt;Ever written a security check that looked perfectly reasonable... until you realized it could let &lt;strong&gt;everyone in&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Here's a tiny Python mistake that can turn an API key check into an authentication bypass.&lt;/p&gt;

&lt;p&gt;No complicated exploit. No special tools.&lt;/p&gt;

&lt;p&gt;Just a missing environment variable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The innocent-looking code
&lt;/h2&gt;

&lt;p&gt;Imagine a FastAPI application protecting an internal endpoint with an API key.&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="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTPException&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;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;API_KEY&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API_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="p"&gt;)&lt;/span&gt;


&lt;span class="nd"&gt;@app.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;/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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;""&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;API_KEY&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;x_api_key&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;401&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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;Welcome, admin!&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;Looks reasonable, right?&lt;/p&gt;

&lt;p&gt;We read the secret from an environment variable, compare it against the incoming header, and reject unauthorized requests.&lt;/p&gt;

&lt;p&gt;Except there's one pretty serious problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens if &lt;code&gt;API_KEY&lt;/code&gt; isn't configured?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug hiding in plain sight
&lt;/h2&gt;

&lt;p&gt;Look closely at this condition:&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;if&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;x_api_key&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Python's &lt;code&gt;and&lt;/code&gt; operator short-circuits.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;API_KEY&lt;/code&gt; is an empty string, the first condition is false.&lt;/p&gt;

&lt;p&gt;So Python never evaluates the comparison.&lt;/p&gt;

&lt;p&gt;And the application never raises the authentication error.&lt;/p&gt;

&lt;p&gt;That means this request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:8000/admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can return:&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;"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;"Welcome, admin!"&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;No API key required.&lt;/p&gt;

&lt;p&gt;The authentication mechanism has silently failed open.&lt;/p&gt;

&lt;p&gt;And the scariest part?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The application can start successfully, pass basic health checks, and expose the protected endpoint without an obvious error.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fail-open vs. fail-closed
&lt;/h2&gt;

&lt;p&gt;This is the difference between two important security concepts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fail-open:&lt;/strong&gt; When something goes wrong, access is allowed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fail-closed:&lt;/strong&gt; When something goes wrong, access is denied.&lt;/p&gt;

&lt;p&gt;Authentication should generally fail closed.&lt;/p&gt;

&lt;p&gt;If your application cannot load its authentication secret, it should not simply allow requests through.&lt;/p&gt;

&lt;p&gt;The absence of a security configuration should be treated as an error, not permission.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix #1: Reject missing configuration
&lt;/h2&gt;

&lt;p&gt;Instead of silently providing an empty default:&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;API_KEY&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="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API_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="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Require the environment variable:&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;API_KEY&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;API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API_KEY must not be empty&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;Now the application fails during startup if the API key is missing or blank.&lt;/p&gt;

&lt;p&gt;This is a much safer failure mode.&lt;/p&gt;

&lt;p&gt;An unavailable service is inconvenient.&lt;/p&gt;

&lt;p&gt;An accidentally unprotected service can be disastrous.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix #2: Compare secrets safely
&lt;/h2&gt;

&lt;p&gt;For secret comparisons, Python provides &lt;code&gt;hmac.compare_digest()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's a better implementation:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HTTPException&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;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;API_KEY&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;API_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;API_KEY must not be empty&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.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;/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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;admin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_api_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&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;x_api_key&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare_digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;x_api_key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;API_KEY&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;HTTPException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Unauthorized&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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;Welcome, admin!&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;This version improves several things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Missing configuration prevents successful startup.&lt;/li&gt;
&lt;li&gt;Missing credentials are rejected.&lt;/li&gt;
&lt;li&gt;Incorrect credentials are rejected.&lt;/li&gt;
&lt;li&gt;Secret comparison uses a function designed to avoid content-dependent short-circuit comparisons.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Constant-time comparison is useful defense in depth, but it doesn't replace TLS, proper secret management, access controls, or rate limiting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix #3: Test the failure, not just the success
&lt;/h2&gt;

&lt;p&gt;Developers often test authentication with a valid API key.&lt;/p&gt;

&lt;p&gt;Maybe they also test an incorrect key.&lt;/p&gt;

&lt;p&gt;But how often do they test a missing configuration?&lt;/p&gt;

&lt;p&gt;Here's a minimal example of a regression test for the original logic:&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;vulnerable_check&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;provided&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;bool&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;expected&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;provided&lt;/span&gt; &lt;span class="o"&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;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_missing_secret_must_not_grant_access&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;vulnerable_check&lt;/span&gt;&lt;span class="p"&gt;(&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="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test &lt;strong&gt;fails&lt;/strong&gt; against the vulnerable implementation.&lt;/p&gt;

&lt;p&gt;And that's exactly what we want it to reveal.&lt;/p&gt;

&lt;p&gt;The corrected standalone version:&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;hmac&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;secure_check&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="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;provided&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;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hmac&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compare_digest&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="n"&gt;provided&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_missing_secret&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;secure_check&lt;/span&gt;&lt;span class="p"&gt;(&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="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_wrong_secret&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;secure_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secret123&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;wrong&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_correct_secret&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;secure_check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;secret123&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;secret123&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These tests cover the comparison logic. An actual FastAPI application should also have integration tests proving that missing configuration prevents startup and unauthenticated requests return HTTP 401.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bigger lesson
&lt;/h2&gt;

&lt;p&gt;This isn't really about FastAPI.&lt;/p&gt;

&lt;p&gt;The same mistake can appear in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Django applications&lt;/li&gt;
&lt;li&gt;Flask APIs&lt;/li&gt;
&lt;li&gt;Microservices&lt;/li&gt;
&lt;li&gt;Webhook authentication&lt;/li&gt;
&lt;li&gt;Internal admin dashboards&lt;/li&gt;
&lt;li&gt;Background worker APIs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it's not limited to API keys.&lt;/p&gt;

&lt;p&gt;Any security feature that depends on optional configuration deserves a careful review.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;SECRET&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;validate_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&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;p&gt;Or:&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;if&lt;/span&gt; &lt;span class="n"&gt;ENABLE_AUTH&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;check_permissions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens when those values are missing, empty, or incorrectly configured?&lt;/p&gt;

&lt;p&gt;The important question isn't just:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Does authentication work when everything is configured correctly?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's also:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What happens when the security configuration breaks?"&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Not every serious security bug requires complex cryptography or sophisticated attacks.&lt;/p&gt;

&lt;p&gt;Sometimes all it takes is an empty string and one &lt;code&gt;if&lt;/code&gt; statement.&lt;/p&gt;

&lt;p&gt;Three things worth remembering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Never silently disable authentication because a secret is missing.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validate critical security configuration during application startup.&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Write tests for failure conditions, not only successful requests.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Secure software isn't only about handling the expected path correctly.&lt;/p&gt;

&lt;p&gt;It's also about making sure the unexpected path doesn't become an open door.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Have you ever discovered a security bug caused by a seemingly harmless default value?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Share it in the comments. I'd love to hear about it.&lt;/p&gt;

&lt;p&gt;If you enjoy Python, backend architecture, and application security, feel free to connect:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/coffee1337" rel="noopener noreferrer"&gt;https://github.com/coffee1337&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Portfolio:&lt;/strong&gt; &lt;a href="https://coffee1337.github.io/" rel="noopener noreferrer"&gt;https://coffee1337.github.io/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>security</category>
      <category>backend</category>
      <category>fastapi</category>
    </item>
    <item>
      <title>Your FastAPI App Is Async. So Why Is It Still Slow?</title>
      <dc:creator>Egor</dc:creator>
      <pubDate>Tue, 13 Oct 2026 10:03:12 +0000</pubDate>
      <link>https://dev.to/coffee1337/your-fastapi-app-is-async-so-why-is-it-still-slow-3338</link>
      <guid>https://dev.to/coffee1337/your-fastapi-app-is-async-so-why-is-it-still-slow-3338</guid>
      <description>&lt;h2&gt;
  
  
  Your FastAPI App Is Async. So Why Is It Still Slow? 🐍
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Understanding the difference between writing &lt;code&gt;async&lt;/code&gt; code and building an actually concurrent Python backend.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You built your API with FastAPI.&lt;/p&gt;

&lt;p&gt;You used &lt;code&gt;async def&lt;/code&gt; everywhere.&lt;/p&gt;

&lt;p&gt;You even added &lt;code&gt;await&lt;/code&gt; to your database calls.&lt;/p&gt;

&lt;p&gt;So why does one slow request still seem to affect everything else?&lt;/p&gt;

&lt;p&gt;Here's something that surprises a lot of Python developers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;async&lt;/code&gt; doesn't automatically make your application non-blocking.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And in some cases, a perfectly valid asynchronous endpoint can make your application perform worse than a regular synchronous one.&lt;/p&gt;

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




&lt;h2&gt;
  
  
  1. The &lt;code&gt;async def&lt;/code&gt; Illusion
&lt;/h2&gt;

&lt;p&gt;Consider this FastAPI endpoint:&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;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;time&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;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.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;/slow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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;slow_endpoint&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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;Done&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;At first glance, everything looks fine.&lt;/p&gt;

&lt;p&gt;The endpoint uses &lt;code&gt;async def&lt;/code&gt;, just like many FastAPI examples.&lt;/p&gt;

&lt;p&gt;But there's a problem:&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;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This call blocks the thread running the event loop.&lt;/p&gt;

&lt;p&gt;While it executes, the event loop cannot make progress on other coroutines running on that same thread.&lt;/p&gt;

&lt;p&gt;If multiple requests arrive, they can end up waiting for each other.&lt;/p&gt;

&lt;p&gt;The endpoint is declared asynchronous, but its implementation blocks asynchronous execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  The fix
&lt;/h3&gt;

&lt;p&gt;Replace the blocking call with an asynchronous 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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&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;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.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;/slow&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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;slow_endpoint&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;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&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;Done&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;Now something important changes.&lt;/p&gt;

&lt;p&gt;When Python reaches:&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;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The coroutine suspends and gives control back to the event loop.&lt;/p&gt;

&lt;p&gt;Other requests can make progress while the first one waits.&lt;/p&gt;

&lt;p&gt;This is the foundation of asynchronous programming.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Concurrency Is Not Parallelism
&lt;/h2&gt;

&lt;p&gt;This distinction matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrency&lt;/strong&gt; means multiple tasks can make progress during overlapping periods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Parallelism&lt;/strong&gt; means multiple tasks actually execute at the same time, typically on different CPU cores.&lt;/p&gt;

&lt;p&gt;AsyncIO is primarily about cooperative concurrency.&lt;/p&gt;

&lt;p&gt;Imagine three API requests, each waiting two seconds for an external service.&lt;/p&gt;

&lt;p&gt;With blocking operations, a single-threaded execution model might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request A: [----2s----]
Request B:             [----2s----]
Request C:                         [----2s----]

Total: ~6 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With asynchronous waiting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request A: [----2s----]
Request B: [----2s----]
Request C: [----2s----]

Total: ~2 seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are simplified timelines, not benchmark results. Real application performance also depends on worker configuration, connection pools, infrastructure, and external services.&lt;/p&gt;

&lt;p&gt;The key idea is that asynchronous tasks can overlap their waiting time.&lt;/p&gt;

&lt;p&gt;But what happens when the work is CPU-intensive?&lt;/p&gt;

&lt;p&gt;That's where things get interesting.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. AsyncIO Won't Magically Speed Up CPU-Heavy Code
&lt;/h2&gt;

&lt;p&gt;Imagine your API needs to perform an expensive calculation.&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;expensive_calculation&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&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="mi"&gt;30_000_000&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now put it inside an async endpoint:&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;@app.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;/calculate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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;calculate&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="nf"&gt;expensive_calculation&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is still blocking.&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;expensive_calculation()&lt;/code&gt; is regular Python code that continuously uses CPU time.&lt;/p&gt;

&lt;p&gt;There is no asynchronous waiting point where the coroutine can yield control.&lt;/p&gt;

&lt;p&gt;Adding &lt;code&gt;async&lt;/code&gt; to the endpoint doesn't transform CPU-bound work into non-blocking work.&lt;/p&gt;

&lt;h3&gt;
  
  
  A better approach
&lt;/h3&gt;

&lt;p&gt;For CPU-intensive operations, you may need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A separate process&lt;/li&gt;
&lt;li&gt;A process pool&lt;/li&gt;
&lt;li&gt;A background job system&lt;/li&gt;
&lt;li&gt;A dedicated worker service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, Python's &lt;code&gt;ProcessPoolExecutor&lt;/code&gt; can execute work in separate processes.&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;asyncio&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;concurrent.futures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ProcessPoolExecutor&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&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;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ProcessPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;expensive_calculation&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;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&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="mi"&gt;30_000_000&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;


&lt;span class="nd"&gt;@app.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;/calculate&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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;calculate&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;loop&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_running_loop&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_in_executor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;expensive_calculation&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;result&lt;/span&gt;&lt;span class="sh"&gt;"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CPU-heavy function runs outside the event-loop process, allowing the event loop to continue serving other requests.&lt;/p&gt;

&lt;p&gt;This is a simplified example. In a real deployment, the process pool should be managed as part of the application's lifecycle, and worker counts should be chosen based on available resources.&lt;/p&gt;

&lt;p&gt;For expensive or long-running jobs, returning a task ID and processing work asynchronously in a dedicated worker is often a better API design.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. The Hidden Problem: Synchronous HTTP Clients
&lt;/h2&gt;

&lt;p&gt;Here's another common mistake.&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;requests&lt;/span&gt;

&lt;span class="nd"&gt;@app.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;/users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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;get_users&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&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;https://jsonplaceholder.typicode.com/users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;timeout&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="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;requests&lt;/code&gt; library performs synchronous HTTP operations.&lt;/p&gt;

&lt;p&gt;So even though the endpoint is asynchronous, the outgoing request blocks the event-loop thread until it finishes.&lt;/p&gt;

&lt;p&gt;A better choice for async endpoints is an asynchronous HTTP client.&lt;/p&gt;

&lt;p&gt;For example, HTTPX:&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;httpx&lt;/span&gt;

&lt;span class="nd"&gt;@app.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;/users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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;get_users&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;httpx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;AsyncClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;10.0&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&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;https://jsonplaceholder.typicode.com/users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the endpoint can yield control while waiting for the external API.&lt;/p&gt;

&lt;p&gt;For production applications, it's usually better to reuse an &lt;code&gt;AsyncClient&lt;/code&gt; rather than create a new one for every request.&lt;/p&gt;

&lt;p&gt;Reusing the client allows connection pooling and reduces unnecessary connection overhead.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. What About Database Queries?
&lt;/h2&gt;

&lt;p&gt;The same principle applies to databases.&lt;/p&gt;

&lt;p&gt;An asynchronous endpoint doesn't automatically make database access asynchronous.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.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;/users/{user_id}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&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;get_user&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="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sync_db_query&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;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;sync_db_query()&lt;/code&gt; blocks, your event loop is blocked.&lt;/p&gt;

&lt;p&gt;With SQLAlchemy's asynchronous API, database operations can look like this:&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;sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;select&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlalchemy.ext.asyncio&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncSession&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;get_user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AsyncSession&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="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="k"&gt;await&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;execute&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="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;where&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="nb"&gt;id&lt;/span&gt; &lt;span class="o"&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="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalar_one_or_none&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example assumes that &lt;code&gt;User&lt;/code&gt; is a defined SQLAlchemy model and the session uses an asynchronous database driver.&lt;/p&gt;

&lt;p&gt;However, there's another important detail:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Async database access doesn't make slow SQL queries fast.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If a query takes three seconds because of missing indexes, inefficient joins, or a poor execution plan, adding &lt;code&gt;await&lt;/code&gt; won't fix that.&lt;/p&gt;

&lt;p&gt;Async improves how your application handles waiting.&lt;/p&gt;

&lt;p&gt;Database optimization improves how quickly that waiting ends.&lt;/p&gt;

&lt;p&gt;Both matter.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. More Concurrency Can Actually Make Things Worse
&lt;/h2&gt;

&lt;p&gt;Suppose your API suddenly receives 1,000 requests.&lt;/p&gt;

&lt;p&gt;Each request needs a database connection.&lt;/p&gt;

&lt;p&gt;Your PostgreSQL connection pool supports 20 simultaneous connections.&lt;/p&gt;

&lt;p&gt;What happens?&lt;/p&gt;

&lt;p&gt;The application cannot magically execute 1,000 database queries concurrently using only 20 available connections.&lt;/p&gt;

&lt;p&gt;Some requests must wait for a connection to become available.&lt;/p&gt;

&lt;p&gt;And increasing the pool size isn't always the right answer.&lt;/p&gt;

&lt;p&gt;Too many connections can overload the database and reduce overall throughput.&lt;/p&gt;

&lt;p&gt;This is where concepts like backpressure, rate limiting, bounded concurrency, and connection pooling become important.&lt;/p&gt;

&lt;p&gt;For example, you can limit concurrent operations using an &lt;code&gt;asyncio.Semaphore&lt;/code&gt;.&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;asyncio&lt;/span&gt;

&lt;span class="n"&gt;semaphore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Semaphore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_external_data&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="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;semaphore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&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;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raise_for_status&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example limits calls using that semaphore to ten concurrent operations within the process.&lt;/p&gt;

&lt;p&gt;It doesn't create a global limit across multiple application workers or servers.&lt;/p&gt;

&lt;p&gt;That's an important difference in distributed deployments.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. How to Know If Your API Is Actually Fast
&lt;/h2&gt;

&lt;p&gt;One of the biggest mistakes in backend development is optimizing code without measuring anything.&lt;/p&gt;

&lt;p&gt;An endpoint taking 100 milliseconds doesn't tell you much by itself.&lt;/p&gt;

&lt;p&gt;You should also consider:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Latency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;How long does an individual request take?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Throughput&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;How many requests can your application handle per second?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P95 and P99 latency&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;How long do the slowest 5% and 1% of requests take?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Error rate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Does your application start returning errors under load?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resource utilization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Are you constrained by CPU, memory, database connections, or an external API?&lt;/p&gt;

&lt;p&gt;Tools such as Locust, k6, and Prometheus can help you investigate these questions.&lt;/p&gt;

&lt;p&gt;The important part is testing under realistic workloads.&lt;/p&gt;

&lt;p&gt;A backend that responds quickly to one request may behave completely differently when hundreds of users arrive simultaneously.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. A Practical Rule for FastAPI Developers
&lt;/h2&gt;

&lt;p&gt;Here's a mental model worth remembering:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Workload&lt;/th&gt;
&lt;th&gt;Common approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Async HTTP requests&lt;/td&gt;
&lt;td&gt;&lt;code&gt;httpx.AsyncClient&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Async database operations&lt;/td&gt;
&lt;td&gt;Async driver + SQLAlchemy AsyncSession&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Waiting for timers&lt;/td&gt;
&lt;td&gt;&lt;code&gt;asyncio.sleep()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Blocking I/O library&lt;/td&gt;
&lt;td&gt;Sync endpoint or thread pool&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU-intensive calculation&lt;/td&gt;
&lt;td&gt;Process pool or dedicated worker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Long-running background job&lt;/td&gt;
&lt;td&gt;Job queue + worker&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;High-volume external API calls&lt;/td&gt;
&lt;td&gt;Connection pooling + bounded concurrency&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are guidelines, not universal rules.&lt;/p&gt;

&lt;p&gt;For example, FastAPI can execute regular &lt;code&gt;def&lt;/code&gt; path operations in a thread pool, which is useful when integrating synchronous libraries.&lt;/p&gt;

&lt;p&gt;But thread pools have limits, too.&lt;/p&gt;

&lt;p&gt;The goal isn't to use &lt;code&gt;async&lt;/code&gt; everywhere.&lt;/p&gt;

&lt;p&gt;The goal is to choose the right execution model for your workload.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;AsyncIO is powerful, but it's easy to misunderstand.&lt;/p&gt;

&lt;p&gt;A Python application doesn't become scalable simply because its functions contain &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The real performance improvements come from understanding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What blocks the event loop&lt;/li&gt;
&lt;li&gt;Where your application spends time waiting&lt;/li&gt;
&lt;li&gt;Which operations are CPU-bound&lt;/li&gt;
&lt;li&gt;How database and HTTP connection pools behave&lt;/li&gt;
&lt;li&gt;When concurrency needs to be limited&lt;/li&gt;
&lt;li&gt;How your application performs under realistic load&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The best backend architecture isn't the one with the most asynchronous code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's the one that understands where the bottlenecks are.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And sometimes, replacing a single blocking function can make a bigger difference than rewriting an entire service.&lt;/p&gt;




&lt;h3&gt;
  
  
  💬 Let's Discuss
&lt;/h3&gt;

&lt;p&gt;What's the most unexpected performance bottleneck you've encountered in a Python backend?&lt;/p&gt;

&lt;p&gt;Was it a blocking library, a database query, an external API, or something completely different?&lt;/p&gt;

&lt;p&gt;I'd love to hear your experience in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Interested in Python backend development, FastAPI, and building scalable applications?&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/coffee1337" rel="noopener noreferrer"&gt;https://github.com/coffee1337&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Portfolio:&lt;/strong&gt; &lt;a href="https://coffee1337.github.io/" rel="noopener noreferrer"&gt;https://coffee1337.github.io/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Thanks for reading! 🚀&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>backend</category>
      <category>performance</category>
    </item>
    <item>
      <title>AI Python Mentor</title>
      <dc:creator>Egor</dc:creator>
      <pubDate>Mon, 12 Oct 2026 15:01:18 +0000</pubDate>
      <link>https://dev.to/coffee1337/ai-python-mentor-aop</link>
      <guid>https://dev.to/coffee1337/ai-python-mentor-aop</guid>
      <description>&lt;h1&gt;
  
  
  Building an AI-Powered Python Learning Platform with FastAPI, Next.js, and RAG
&lt;/h1&gt;

&lt;p&gt;Building an AI-powered application is relatively easy when all you need is a chat interface connected to an LLM API.&lt;/p&gt;

&lt;p&gt;Building a complete learning platform around that AI is a different challenge.&lt;/p&gt;

&lt;p&gt;I wanted to explore how to combine a structured curriculum, practical programming exercises, personalized learning, and an AI tutor into a single full-stack system.&lt;/p&gt;

&lt;p&gt;That's how &lt;strong&gt;AI Python Mentor&lt;/strong&gt; started.&lt;/p&gt;

&lt;p&gt;In this article, I'll walk through the architecture, technology choices, AI integration, and some of the engineering challenges behind the project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/coffee1337/ai-python-mentor" rel="noopener noreferrer"&gt;https://github.com/coffee1337/ai-python-mentor&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;AI Python Mentor is a full-stack platform designed to help users learn Python and backend development.&lt;/p&gt;

&lt;p&gt;Instead of simply providing AI-generated explanations, the application combines several learning mechanisms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A structured curriculum covering Python and backend concepts&lt;/li&gt;
&lt;li&gt;Programming exercises and knowledge checks&lt;/li&gt;
&lt;li&gt;An AI tutor with access to lesson context&lt;/li&gt;
&lt;li&gt;Personalized learning roadmaps&lt;/li&gt;
&lt;li&gt;Progress tracking and spaced repetition&lt;/li&gt;
&lt;li&gt;Career-oriented skill gap analysis&lt;/li&gt;
&lt;li&gt;Practical portfolio projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is to build an environment where AI supports structured learning rather than replacing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technology Stack
&lt;/h2&gt;

&lt;p&gt;The application uses:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Python 3.12&lt;/li&gt;
&lt;li&gt;FastAPI&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;SQLAlchemy and Alembic&lt;/li&gt;
&lt;li&gt;Pydantic&lt;/li&gt;
&lt;li&gt;AsyncIO&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 15&lt;/li&gt;
&lt;li&gt;React 19&lt;/li&gt;
&lt;li&gt;TypeScript&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;LLM Gateway&lt;/li&gt;
&lt;li&gt;Retrieval-Augmented Generation (RAG)&lt;/li&gt;
&lt;li&gt;Lesson-based knowledge indexing&lt;/li&gt;
&lt;li&gt;Token usage and cost accounting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Infrastructure&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker and Docker Compose&lt;/li&gt;
&lt;li&gt;GitHub Actions&lt;/li&gt;
&lt;li&gt;Background workers&lt;/li&gt;
&lt;li&gt;Database migrations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I chose FastAPI because it provides a solid foundation for asynchronous API development, explicit validation, and modular backend design.&lt;/p&gt;

&lt;p&gt;PostgreSQL handles relational data, while SQLAlchemy and Alembic manage database access and schema evolution.&lt;/p&gt;

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

&lt;p&gt;The application follows a modular monolith approach.&lt;/p&gt;

&lt;p&gt;The core API is responsible for authentication, learning progress, lessons, exercises, and integrations.&lt;/p&gt;

&lt;p&gt;Specialized tasks can be handled by separate processes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    Browser
                       |
                       v
                 Next.js Frontend
                       |
                       v
                  FastAPI API
                       |
        +--------------+--------------+
        |              |              |
        v              v              v
    PostgreSQL     AI Gateway     Background
                       + RAG        Workers
        |
        +---- Learning Progress
        +---- User Sessions
        +---- Exercise Attempts
        +---- AI Usage Records

Separate execution architecture:
    Execution Dispatcher
              |
              v
       Isolated Python Runner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of my architectural goals was to keep core learning features independent of the availability of external AI services.&lt;/p&gt;

&lt;p&gt;For example, if the AI Gateway is unavailable, users should still be able to access lessons and other non-AI functionality.&lt;/p&gt;

&lt;p&gt;This separation makes the application easier to maintain and evolve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building an AI Tutor with RAG
&lt;/h2&gt;

&lt;p&gt;A general-purpose LLM can explain Python concepts, but it doesn't automatically know which lesson a student is studying or which version of the material is currently published.&lt;/p&gt;

&lt;p&gt;That's where Retrieval-Augmented Generation becomes useful.&lt;/p&gt;

&lt;p&gt;AI Python Mentor uses a local knowledge index built from immutable snapshots of published lesson content.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Student asks a question
          |
          v
Identify current lesson
          |
          v
Retrieve relevant lesson context
          |
          v
Build LLM request
          |
          v
Generate contextual explanation
          |
          v
Track usage and cost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By retrieving context from the learning materials, the AI tutor can ground its explanations in the content the student is actually studying.&lt;/p&gt;

&lt;p&gt;This is particularly important for educational applications, where a technically correct answer may still be inappropriate for the learner's current level.&lt;/p&gt;

&lt;p&gt;The platform also tracks AI usage and costs.&lt;/p&gt;

&lt;p&gt;This is an important consideration when building products that rely on paid LLM APIs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Python Code Execution
&lt;/h2&gt;

&lt;p&gt;Another challenge is supporting practical programming exercises.&lt;/p&gt;

&lt;p&gt;Users submit Python solutions following a simple contract:&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;solve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# User implementation
&lt;/span&gt;    &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exercises can define public examples and hidden tests, while the platform stores submissions and attempt history.&lt;/p&gt;

&lt;p&gt;However, executing untrusted Python code directly inside the main API would introduce significant security risks.&lt;/p&gt;

&lt;p&gt;For that reason, the system separates execution dispatching from the primary application and defines a dedicated Python Runner architecture.&lt;/p&gt;

&lt;p&gt;The production execution model is designed around isolation and resource restrictions, including container or sandbox boundaries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Untrusted code execution is disabled by default. The production Runner still requires the appropriate isolated deployment and security configuration.&lt;/p&gt;

&lt;p&gt;The key design decision is simple: the main web API should never become the execution environment for arbitrary user code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Authentication and Security
&lt;/h2&gt;

&lt;p&gt;The backend includes several account security features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cookie-based sessions&lt;/li&gt;
&lt;li&gt;Argon2 password hashing&lt;/li&gt;
&lt;li&gt;CSRF protection&lt;/li&gt;
&lt;li&gt;Email confirmation and password recovery&lt;/li&gt;
&lt;li&gt;Session revocation&lt;/li&gt;
&lt;li&gt;Account export and deletion&lt;/li&gt;
&lt;li&gt;Administrative audit logging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These features are important because real applications require much more than a working login endpoint.&lt;/p&gt;

&lt;p&gt;For production deployments, secure cookies and HTTPS are also required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background Processing
&lt;/h2&gt;

&lt;p&gt;Not every operation should run directly inside an HTTP request.&lt;/p&gt;

&lt;p&gt;The architecture includes background processing for workloads such as notifications and execution dispatching.&lt;/p&gt;

&lt;p&gt;Separating those workloads helps keep request handling responsive and makes longer-running operations easier to manage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing and CI
&lt;/h2&gt;

&lt;p&gt;The repository includes automated checks for backend functionality, database migrations, frontend builds, worker logic, and browser flows.&lt;/p&gt;

&lt;p&gt;GitHub Actions helps validate changes across these areas.&lt;/p&gt;

&lt;p&gt;I also added support for native Windows development without requiring Docker for every local workflow.&lt;/p&gt;

&lt;p&gt;That was useful because it made the development environment more accessible while keeping Docker-based deployment options available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Status
&lt;/h2&gt;

&lt;p&gt;AI Python Mentor is still under active development.&lt;/p&gt;

&lt;p&gt;The core learning platform, backend, AI integration, database layer, and CI infrastructure have been implemented.&lt;/p&gt;

&lt;p&gt;Current work focuses on product polish, learning content quality, AI evaluation, and production deployment.&lt;/p&gt;

&lt;p&gt;I prefer being transparent about this: having a production-oriented architecture does not mean every planned production component has already been deployed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;This project reinforced several engineering lessons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. AI should be a component, not the entire architecture.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A useful AI product still needs reliable authentication, data models, application logic, and operational safeguards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Structured context matters.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;RAG is particularly valuable when answers should reflect specific, versioned learning materials.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Security boundaries should be designed early.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is especially important when an application accepts executable code from users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Operational concerns are product features.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Usage quotas, cost tracking, background jobs, and testing all contribute to the long-term viability of an AI application.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;My next priorities include improving AI evaluation, polishing the user experience, refining educational content, and preparing the infrastructure for production deployment.&lt;/p&gt;

&lt;p&gt;You can explore the code and architecture here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/coffee1337/ai-python-mentor" rel="noopener noreferrer"&gt;https://github.com/coffee1337/ai-python-mentor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Portfolio:&lt;/strong&gt; &lt;a href="https://coffee1337.github.io" rel="noopener noreferrer"&gt;https://coffee1337.github.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm also open to freelance and contract projects involving Python backends, FastAPI, AI/LLM integrations, RAG systems, and SaaS MVP development.&lt;/p&gt;

&lt;p&gt;If you're building something similar, feel free to reach out!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Tags: #python #ai #fastapi #webdev&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>fastapi</category>
      <category>python</category>
      <category>rag</category>
    </item>
  </channel>
</rss>
