<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.2">Jekyll</generator><link href="https://www.cesarsotovalero.net/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.cesarsotovalero.net/" rel="alternate" type="text/html" /><updated>2026-04-02T06:00:24-07:00</updated><id>https://www.cesarsotovalero.net/feed.xml</id><title type="html">César Soto Valero</title><subtitle>César - Computer Scientist</subtitle><author><name>César Soto Valero</name><email>NaN</email></author><entry><title type="html">SDD and the Future of Software Development</title><link href="https://www.cesarsotovalero.net/blog/sdd-and-the-future-of-software-development.html" rel="alternate" type="text/html" title="SDD and the Future of Software Development" /><published>2026-02-01T00:00:00-08:00</published><updated>2026-04-02T05:57:52-07:00</updated><id>https://www.cesarsotovalero.net/blog/sdd-and-the-future-of-software-development</id><content type="html" xml:base="https://www.cesarsotovalero.net/blog/sdd-and-the-future-of-software-development.html"><![CDATA[<aside class="youtube">
        <a href="https://www.youtube.com/watch?v=maIBlxGubeI"><div class="box">
        <img src="https://i.ytimg.com/vi/maIBlxGubeI/mqdefault.jpg" alt="YouTube video #maIBlxGubeI" />
        <div class="play">
          <img src="/img/icons/youtube-play20px.svg" alt="Play Video" class="youtube-logo" />
        </div>
        </div></a>
        <div>Is Spec-Driven Development the Future of Software Development?; 17 January 2026.</div></aside>

<p>The next massive software failure will probably not come from a missing line of code.
It will come from a missing sentence.</p>

<p>Right now, developers are using AI agents to write code faster than they can actually read it.
I’m doing it too!
The latest models can generate 1,000+ LoC in seconds.
But that’s not the real problem.
The problem is that speed removes <em>friction</em>.
And friction used to be where <em>thinking</em> happened.</p>

<p>Recently, GitHub ran controlled experiments where developers using Copilot finished tasks faster (with numbers often quoted around “up to 55%”).<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>
And that sounds like a dream. Until you realize that speed without direction is just a faster way to reach the wrong place.
In other studies, experienced developers got slower when AI increased review load, coordination cost, and rework.<sup id="fnref:11"><a href="#fn:11" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>
So the real question is not “Can AI write production-ready code?”
It definitely can!</p>

<p>The question is:</p>

<blockquote>
  <p>“Can we keep AI speed without turning our systems into a high-throughput confusion factory?”</p>
</blockquote>

<p>That is where <a href="https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html">Spec-Driven Development (SDD)</a> shows up.
The idea is simple: <strong>write a clear, structured specification (“spec”) before the code</strong>, so that AI gets directed in the right way and remain withing well defined constraints instead of “vibes.”</p>

<p>You many wonder:</p>

<blockquote>
  <p>Are we just reinventing <a href="https://en.wikipedia.org/wiki/Waterfall_model">1990s Waterfall</a>, but this time with an AI-powered engine on top?</p>
</blockquote>

<p>In this post, I’ll cover 3 things:</p>

<ol>
  <li><strong>What SDD actually is</strong> and why vague thinking is now your biggest technical liability.</li>
  <li><strong>The three levels of SDD</strong> and the specific one that quietly traps most teams.</li>
  <li><strong>A practical checklist</strong> to decide which features deserve a real spec and which ones you should just build fast and move on.</li>
</ol>

<p>By the end, you’ll know whether SDD is a real shift in how software gets built, or just another heavyweight idea that sounds smart and wastes everyone’s time.</p>

<h1 id="what-is-sdd">What is SDD?</h1>

<p>In SDD, the <em>specification is the primary artifact</em>.</p>

<p>Not the code. Not the framework. Not the Jira tickets.
The spec is the source of truth and everything else is downstream.<sup id="fnref:3"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">3</a></sup></p>

<p>And no, the spec is not a vague paragraph or a wish list.
A real SDD spec is a structured, testable description of behavior.</p>

<p>The spec defines:</p>

<ul>
  <li>What the system must do (and what it must not do)</li>
  <li>Edge cases</li>
  <li>Business rules</li>
  <li>Failure modes</li>
  <li>An explicit <a href="https://en.wikipedia.org/wiki/Scrum_(project_management)#Definition_of_Done_(DoD)">definition of done (DoD)</a></li>
</ul>

<p>The goal is to guarantee unambiguous behavior from the AI coding agent.</p>

<h1 id="why-is-sdd-relevant-now">Why is SDD Relevant Now?</h1>

<p>SDD is not new.
What’s new is the environment it now lives in.</p>

<p>In the past, we used to say “code is the bottleneck.”
Research in requirements engineering has been blunt for years: a big chunk of effort ends up as rework, and unclear or shifting requirements are a major driver.<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">4</a></sup></p>

<aside class="quote">
    <em>“In a world of infinite code, clarity is the scarce resource.”</em>
</aside>

<p>But now code is cheap, and ambiguity is expensive.
AI raises the stakes because it will happily translate fuzzy intent into crisp code without asking clarifying questions.
Think of an <a href="https://en.wikipedia.org/wiki/AI_agent">AI agent</a> like a brilliant intern who never says “I don’t understand.”
If your requirements are fuzzy, it won’t stop.
It will confidently generate a clean, professional-looking solution that could be catastrophically wrong.</p>

<p>And there’s a second issue that feels almost philosophical: Humans transmit information slowly.
We type maybe 40 words per minute on a good day.
That speed forces reflection.
But AI can generate 2,000 lines in minutes.
It removes the pause where thinking used to happen.</p>

<p>When thinking disappears, ambiguity leaks straight into production.
This is why some researchers now treat prompts as a form of requirements, and argue that classic requirements methods will become even more valuable in the generative era.<sup id="fnref:4"><a href="#fn:4" class="footnote" rel="footnote" role="doc-noteref">5</a></sup></p>

<p>Same problem → Higher speed → Bigger blast radius.</p>

<h1 id="sdd-vs-waterfall">SDD vs Waterfall</h1>

<p>Yes, SDD feels like Waterfall, you are not crazy.
They both say <em>“think before you build.”</em>
But they diverge on the most important thing:
Waterfall assumes you can predict the future.
SDD assumes you cannot, so it builds a fast loop where the spec, tests, and code evolve together.
Modern SDD discussions emphasize tight iteration and executable checks, not month-long requirements phases.<sup id="fnref:9"><a href="#fn:9" class="footnote" rel="footnote" role="doc-noteref">6</a></sup>
So yes, it looks like Waterfall if you only look at the order of steps.
It behaves like something else if you look at the feedback loop.</p>

<h1 id="a-matter-of-intent">A Matter of Intent</h1>

<p>Let’s be honest: most bugs are not code bugs.
They are argument bugs.</p>

<p>You did not write incorrect logic.
You postponed a decision until it exploded in production.</p>

<p>Example: a payment gateway requirement says:</p>

<blockquote>
  <p>“The system must block <em>risky</em> transactions.”</p>
</blockquote>

<p>One person interprets “risky” as “high-risk country.”
Another interprets it as “amount above $1,000.”
Both are reasonable.
But both can be wrong in different ways.</p>

<p>Research shows this is not just anecdotal.
Practitioners interpret conditionals in requirements inconsistently, even when they believe they are being precise.<sup id="fnref:10"><a href="#fn:10" class="footnote" rel="footnote" role="doc-noteref">7</a></sup></p>

<p>SDD forces that decision to happen early.
When stakeholders are calm, when assumptions are visible, and when the codebase still has exactly zero lines of code.</p>

<h1 id="sdd-as-a-pipeline">SDD as a Pipeline</h1>

<p>Spec-Driven Development is not about writing more documentation.
It’s about removing ambiguity before the agent starts making decisions for you.</p>

<p>A practical workflow looks like this:</p>

<pre><code class="language-mermaid">%%{init: {'theme':'base'}}%%
flowchart TB;

S["Spec (requirements + constraints + edge cases)"] --&gt; P["Plan (tasks)"]
P --&gt; I["Implementation (AI agent + humans)"]
I --&gt; T["Verification (tests, properties, gates)"]
T --&gt;|pass| M["Merge + Deploy"]
T --&gt;|fail| S

subgraph "What changes with AI"
S
P
T
end
</code></pre>

<h2 id="requirements">Requirements</h2>

<p>Describe what the user experiences from the outside (not how the system works).</p>

<p>Example:</p>

<blockquote>
  <p>“When a user submits a support ticket, they receive a clear and empathetic response within five seconds. The response must reference their issue and suggest a concrete next step.”</p>
</blockquote>

<h2 id="design">Design</h2>

<p>Define structure and boundaries.
What information can the agent see?
What tools can it use?
What is it not allowed to do?</p>

<p>Example:</p>

<blockquote>
  <p>“The agent can read the ticket text and query the internal FAQ. It cannot invent policies, promise refunds, or escalate issues on its own.”</p>
</blockquote>

<h2 id="tasks">Tasks</h2>

<p>Tasks must be explicit, sequential, and boring.</p>

<p>Example:</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">1.</span> Extract the user’s core problem in one sentence.
<span class="p">2.</span> Match it against FAQ categories.
<span class="p">3.</span> Select the most relevant solution.
<span class="p">4.</span> Generate a response using approved tone guidelines.
</code></pre></div></div>

<p>No “think carefully.”
No “use best judgment.”
Agents do exactly what you specify.
Nothing more.</p>

<h2 id="build">Build</h2>

<p>Now you turn the spec into checks.
Does the response reference the user’s issue?
Is the response under 120 words?
If it fails, the build fails.
That’s the whole loop.
It is intentionally simple.
Because when working with non-deterministic tools, simplicity is not a style choice.
It’s a reliability strategy.</p>

<h1 id="the-three-levels-of-sdd">The Three Levels of SDD</h1>

<p>Not all SDD looks the same.
In practice, there are three levels:</p>

<h2 id="level-1-spec-first">Level 1: Spec-First</h2>

<p>You write a spec to clarify your own thinking, use it to guide the agent, then move on.
Perfect for MVPs and experiments.
The spec is useful even if it dies tomorrow.</p>

<h2 id="level-2-spec-anchored">Level 2: Spec-Anchored</h2>

<p>The spec is a living artifact.
If the code changes, the spec must change.
This is where high-performing teams tend to land because it balances flexibility with discipline.
GitHub’s Spec Kit explicitly pushes the idea of specs that can drive implementation and verification, not just explanation.<sup id="fnref:7"><a href="#fn:7" class="footnote" rel="footnote" role="doc-noteref">8</a></sup>
This is also where most teams quietly fail.
Not because they cannot write specs.
Because they cannot keep them alive.</p>

<h2 id="level-3-spec-as-source">Level 3: Spec-as-Source</h2>

<p>You edit the spec, and tooling regenerates the implementation.
This is the low-code dream.
Some newer tools and platforms point in this direction by making the spec the center of gravity.<sup id="fnref:8"><a href="#fn:8" class="footnote" rel="footnote" role="doc-noteref">9</a></sup></p>

<blockquote>
  <p>⚠️ My take: Level 3 is dangerous right now for complex systems. When the spec becomes the source of truth, the spec becomes the codebase.
Meaning if you cannot express intent precisely (and natural language is notoriously ambiguous), you are moving bugs upstream and making them look like prose. At that point, you reinvented a programming language. But fuzzier. And some of the loudest skepticism you’ll hear about SDD is basically this argument, stated more rudely.<sup id="fnref:12"><a href="#fn:12" class="footnote" rel="footnote" role="doc-noteref">10</a></sup></p>
</blockquote>

<h1 id="reality-check">Reality Check</h1>

<h2 id="-green-light">🟢 Green light</h2>

<p>SDD excels at:</p>

<ul>
  <li>New features</li>
  <li>Greenfield systems</li>
  <li>Modernization efforts</li>
</ul>

<p>When the slate is clean, a clear spec is a superpower.</p>

<h2 id="-red-light">🔴 Red light</h2>

<p>SDD struggles with deeply entangled legacy systems.
If logic is spread across five services, three cron jobs, and one senior engineer’s memory, a “clean spec” is not a document problem.
It’s an archaeology problem.
AI agents will suggest elegant refactors that ignore the decade of hacks quietly keeping the business alive.</p>

<p>Also, a personal confession:</p>

<blockquote>
  <p>Sometimes I can understand code faster than I can understand a folder full of Markdown.
That might be because we have decades of tooling to navigate code, and far less mature tooling to navigate living specs.</p>
</blockquote>

<p>So yes, SDD needs tooling support, not moral superiority.
And there’s another reason SDD is having a moment:
AI-assisted development can increase duplication, churn, and architectural drift when you do not constrain the agent with explicit rules and boundaries.<sup id="fnref:5"><a href="#fn:5" class="footnote" rel="footnote" role="doc-noteref">11</a></sup><sup id="fnref:6"><a href="#fn:6" class="footnote" rel="footnote" role="doc-noteref">12</a></sup></p>

<p>Here is an example of a simple spec verification in Python:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
</pre></td><td class="code"><pre><span class="kn">import</span> <span class="n">re</span>

<span class="k">def</span> <span class="nf">validate_support_reply</span><span class="p">(</span><span class="n">ticket_text</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">reply</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="bp">None</span><span class="p">:</span>
<span class="sh">"""</span><span class="s">
Spec constraint: keep responses short and reference the user</span><span class="sh">'</span><span class="s">s issue.
This is not </span><span class="sh">"</span><span class="s">AI evaluation.</span><span class="sh">"</span><span class="s"> This is build gating.
</span><span class="sh">"""</span>
<span class="k">if</span> <span class="ow">not</span> <span class="n">reply</span> <span class="ow">or</span> <span class="ow">not</span> <span class="n">reply</span><span class="p">.</span><span class="nf">strip</span><span class="p">():</span>
<span class="k">raise</span> <span class="nc">ValueError</span><span class="p">(</span><span class="sh">"</span><span class="s">Reply must not be empty</span><span class="sh">"</span><span class="p">)</span>

    <span class="n">word_count</span> <span class="o">=</span> <span class="nf">len</span><span class="p">(</span><span class="n">reply</span><span class="p">.</span><span class="nf">split</span><span class="p">())</span>
    <span class="k">if</span> <span class="n">word_count</span> <span class="o">&gt;</span> <span class="mi">120</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nc">ValueError</span><span class="p">(</span><span class="sa">f</span><span class="sh">"</span><span class="s">Reply must be &lt;= 120 words (was </span><span class="si">{</span><span class="n">word_count</span><span class="si">}</span><span class="s">)</span><span class="sh">"</span><span class="p">)</span>

    <span class="c1"># Naive overlap check (good enough to catch obvious failures).
</span>    <span class="n">ticket_words</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="nf">split</span><span class="p">(</span><span class="sa">r</span><span class="sh">'</span><span class="s">\W+</span><span class="sh">'</span><span class="p">,</span> <span class="n">ticket_text</span><span class="p">.</span><span class="nf">lower</span><span class="p">())</span>
    <span class="n">reply_lower</span> <span class="o">=</span> <span class="n">reply</span><span class="p">.</span><span class="nf">lower</span><span class="p">()</span>

    <span class="n">overlaps</span> <span class="o">=</span> <span class="nf">sum</span><span class="p">(</span><span class="mi">1</span> <span class="k">for</span> <span class="n">w</span> <span class="ow">in</span> <span class="n">ticket_words</span> <span class="k">if</span> <span class="nf">len</span><span class="p">(</span><span class="n">w</span><span class="p">)</span> <span class="o">&gt;=</span> <span class="mi">5</span> <span class="ow">and</span> <span class="n">w</span> <span class="ow">in</span> <span class="n">reply_lower</span><span class="p">)</span>

    <span class="k">if</span> <span class="n">overlaps</span> <span class="o">&lt;</span> <span class="mi">2</span><span class="p">:</span>
        <span class="k">raise</span> <span class="nc">ValueError</span><span class="p">(</span><span class="sh">"</span><span class="s">Reply does not reference the user</span><span class="sh">'</span><span class="s">s issue enough</span><span class="sh">"</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></figure>

<h1 id="when-to-use-sdd">When to Use SDD</h1>

<p>Run this three-point check:</p>

<ol>
  <li><strong>Risk:</strong> Does this touch money, security, or permissions? Use SDD.</li>
  <li><strong>Longevity:</strong> Will someone else maintain this in a year? Use SDD.</li>
  <li><strong>Exploration:</strong> Are you probing an API or testing an idea? Skip SDD. Just code.</li>
</ol>

<p>In my opinion, the future isn’t “always spec-driven.”
The future is spec-driven when being wrong is expensive.</p>

<h1 id="footnotes">Footnotes</h1>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>GitHub, <a href="https://github.blog/news-insights/research/research-quantifying-github-copilots-impact-on-developer-productivity-and-happiness/"><em>Research: quantifying GitHub Copilot’s impact on developer productivity and happiness</em></a> (2022). <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:11">
      <p>METR, <a href="https://www.metr.org/blog/2025-02-27-measuring-the-impact-of-ai-on-experienced-developers/"><em>Measuring the impact of AI on experienced developers</em></a> (2025). <a href="#fnref:11" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3">
      <p>Thoughtworks, <a href="https://www.thoughtworks.com/radar/techniques/spec-driven-development"><em>Spec-driven development</em></a> (Technology Radar technique entry, 5 Nov 2025). <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>Lars-Ola Damm, Lars Lundberg, and Claes Wohlin, <a href="https://fileadmin.cs.lth.se/cs/Personal/Lars_Ola_Damm/Lars-OlaDamm_JSS2008.pdf"><em>A Model for Software Rework Reduction through Improved Requirements Engineering Practice</em></a> (Journal of Systems and Software, 2008). <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4">
      <p>Andreas Vogelsang, <a href="https://arxiv.org/abs/2408.09127"><em>From Specifications to Prompts: On the Future of Generative LLMs in Requirements Engineering</em></a> (IEEE Software column preprint, 2024). <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:9">
      <p>Thoughtworks, <a href="https://www.thoughtworks.com/insights/blog/agile-engineering-practices/spec-driven-development-unpacking-2025-new-engineering-practices"><em>Spec-driven development: Unpacking one of 2025’s key new AI-assisted engineering practices</em></a> (4 Dec 2025). <a href="#fnref:9" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:10">
      <p>Jannik Fischbach et al., <a href="https://arxiv.org/abs/2109.02063"><em>How Do Practitioners Interpret Conditionals in Requirements?</em></a> (arXiv, 2021). <a href="#fnref:10" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:7">
      <p>GitHub, <a href="https://github.blog/ai-and-ml/generative-ai/spec-driven-development-with-ai-get-started-with-a-new-open-source-toolkit/"><em>Spec-driven development with AI: Get started with a new open source toolkit</em></a> (2 Sept 2025). <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:8">
      <p>Tessl, <a href="https://tessl.io/blog/tessl-launches-spec-driven-framework-and-registry/"><em>Tessl launches spec-driven framework and registry</em></a> (23 Sept 2025). <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:12">
      <p>Discussion thread capturing practitioner skepticism about SDD-as-source and ambiguity in natural language, <a href="https://news.ycombinator.com/item?id=45610996"><em>Hacker News thread: “Spec-Driven Development (SDD)”</em></a> (Sept 2025). <a href="#fnref:12" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5">
      <p>GitClear, <a href="https://gitclear-public.s3.us-west-2.amazonaws.com/GitClear-AI-Copilot-Code-Quality-2025.pdf"><em>AI Copilot Code Quality: 2025 Look Back at 12 Months of Data</em></a> (report, 2025). <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6">
      <p>Thoughtworks, <a href="https://www.thoughtworks.com/en-gb/radar/techniques/complacency-with-ai-generated-code"><em>Complacency with AI-generated code</em></a> (Technology Radar technique, 23 Oct 2024). <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>César Soto Valero</name><email>cesarsotovalero@gmail.com</email></author><category term="coding" /><summary type="html"><![CDATA[AI can now generate code faster than you can humanly read it. But that is not the problem. The problem is ambiguity moving at machine speed. Spec-Driven Development (SDD) makes the specification (a.k.a, the "spec") the primary artifact so AI agents get constraints instead of vague requirements. This post explains what SDD is, why it is rising in popularity now, the three levels of SDD, where it works, where it breaks, and a practical checklist you can use right away.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.cesarsotovalero.net/img/posts/2026/2026-02-01/red-letters-plus-engine-machine.jpg" /><media:content medium="image" url="https://www.cesarsotovalero.net/img/posts/2026/2026-02-01/red-letters-plus-engine-machine.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Projections for Data-Related Roles in The AI Era</title><link href="https://www.cesarsotovalero.net/blog/projections-for-data-related-roles-in-the-ai-era.html" rel="alternate" type="text/html" title="Projections for Data-Related Roles in The AI Era" /><published>2026-01-15T00:00:00-08:00</published><updated>2026-04-02T05:57:52-07:00</updated><id>https://www.cesarsotovalero.net/blog/projections-for-data-related-roles-in-the-ai-era</id><content type="html" xml:base="https://www.cesarsotovalero.net/blog/projections-for-data-related-roles-in-the-ai-era.html"><![CDATA[<aside class="youtube">
        <a href="https://www.youtube.com/watch?v=U06hExVnVbw"><div class="box">
        <img src="https://i.ytimg.com/vi/U06hExVnVbw/mqdefault.jpg" alt="YouTube video #U06hExVnVbw" />
        <div class="play">
          <img src="/img/icons/youtube-play20px.svg" alt="Play Video" class="youtube-logo" />
        </div>
        </div></a>
        <div>Data Science in 2026: From Building Models to Owning Systems; 11 January 2026.</div></aside>

<p>I worked as a data scientist in 2025.
From what I can see, data-related roles might quietly mutate this year without an official memo.</p>

<p>Today, ML models are faster to build and easier to replicate than ever before.
We have cloud infrastructure, mature libraries, and powerful AI code assistants.
However, keeping models running in production (and tethered to real decisions) is harder than ever.
Which means that these are the skills companies will pay for in 2026: owning production systems end to end.</p>

<p>The shift is already underway.</p>

<p>So, my aim with this post is threefold:</p>

<ol>
  <li>To map how data roles are drifting from “model builder” to “system owner.”</li>
  <li>To define this kind of ownership in practical terms (pipelines, monitoring, governance, and business outcomes).</li>
  <li>To outline a career path that increases leverage for data professionals without chasing shiny tools.</li>
</ol>

<p>Let’s dive in!</p>

<h1 id="from-artifacts-to-outcomes">From Artifacts to Outcomes</h1>

<p>You’ll probably not gonna get a email this year with something like “<em>Tell me what system do you own or start packing your desk</em>,” but you might already feel the shift in expectations.
The interview questions are changing, the the job descriptions are changing, and performance metrics are also not the same as before.</p>

<p>Today, you deliver a model, then get asked about latency, drift, real-world performance, and whether the metric moved or misled.</p>

<h1 id="from-models-to-systems">From Models to Systems</h1>

<aside class="quote">
    <em>Eventually, you realize the model artifact was never the deliverable.”</em>
</aside>

<p>In real-world ML, the model is a small part of the system.
Most production cost comes from the surrounding machinery.<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>
The first version ships fast, but the subsequent versions became a maintenance job.
The scoreboard is not the model’s accuracy, but whether <em>business outcomes</em> improved or not.</p>

<p>So the winning move have changed.
Impact beats elegance and reliability beats cleverness.
From artifacts to outcomes.
A model you can’t operate is just a well-formatted opinion.
A outcome that you deliver is a career booster.</p>

<h1 id="which-model-is-the-best-now-matters-the-less">Which Model Is The Best Now Matters The Less</h1>

<p>Model training is no longer the bottleneck.
Better defaults, <a href="https://www.automl.org/automl/">AutoML</a>, <a href="https://github.com/lukasmasuch/best-of-ml-python">awesome ML Python libraries</a>, and cheaper compute make “getting a model working” the easy part.
Even when modeling is actually hard, the path is increasingly well-paved.</p>

<p>But the truth is: most real failures aren’t from weak models but from systems that lie because the input data shifted or the business process changed.</p>

<p>Production systems often see performance drops not because the architecture was wrong, but because the system around it failed to react to distribution changes.<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup></p>

<p>So the job will evolve:</p>

<ul>
  <li>From “<em>Do you know the latest DL architecture?</em>” to “<em>Can you run this in production, from end to end?</em>”</li>
  <li>From “<em>Do you know the newest library?</em>” to “<em>Do you know what breaks first?</em>”</li>
  <li>From “<em>Do you know how to tune hyperparameters?</em>” to “<em>Do you know how to monitor drift?</em>”</li>
</ul>

<p>The most valuable skill is the ability to own the whole chain. That’s ownership.</p>

<h1 id="production-system-ownership">Production System Ownership</h1>

<p>Owning a system means you run a decision-making business process, not a Jupyter notebook.</p>

<p>Ownership forces you to think beyond code and offline metrics.
It forces you to care about failure modes, feedback loops, and incentives.
To accept that data is not stable and users are not rational.
That acceptance is a career booster.</p>

<p>In practice, ownership spans five surfaces:</p>

<ol>
  <li><strong>Data:</strong> quality, contracts, lineage.</li>
  <li><strong>Model:</strong> evaluation, documentation, intended use.</li>
  <li><strong>Delivery:</strong> deployment, latency, rollback.</li>
  <li><strong>Monitoring:</strong> drift, performance, cost.</li>
  <li><strong>Governance:</strong> risk, logs, compliance.</li>
</ol>

<p>If you ignore any one of them, the system will eventually ignore you back.
Because for most real-world sytems the model is a component, not the whole product.</p>

<p>For example, if you build <a href="./blog/from-classical-ml-to-dnns-and-gnns-for-real-time-financial-fraud-detection">a financial fraud detection model</a> but ignore data quality, you’ll get garbage in and garbage out.
If you ignore monitoring, you’ll miss drift and degrade <a href="./blog/evaluation-metrics-for-real-time-financial-fraud-detection-ml-models">key evaluation metrics</a> silently.
This could lead to lost revenue, regulatory fines, or customer churn.</p>

<p>So the overall system is the product.
The business outcome is the scoreboard.
And it’s my believe that the best way to stay relevant in the coming years is <em>to own that scoreboard</em>.</p>

<h2 id="skills-metrics-and-problem-framing">Skills, Metrics and Problem Framing</h2>

<p>You don’t need to learn everything.
You need to learn the parts that keep working when tools change (i.e.,the parts that compound across projects and years).</p>

<p>For example, if you’re tasked with improving customer churn prediction, you dig in and find:</p>

<ul>
  <li>The training data excluded users who churned in the first week (survivorship bias).</li>
  <li>The model predicts churn probability, but no one defined what action to take at which threshold.</li>
  <li>Predictions aren’t logged, so you can’t measure whether high-risk users were actually contacted.</li>
  <li>The feature pipeline breaks silently when a new product launches, because no schema validation exists.</li>
</ul>

<p>You fix the system:</p>

<ol>
  <li>Add schema checks and drift monitoring on input features.</li>
  <li>Work with the product team to define intervention thresholds (e.g., contact users with &gt;70% churn risk).</li>
  <li>Log predictions and outcomes to measure intervention effectiveness.</li>
  <li>Set up a weekly dashboard tracking both model performance and business impact (retention lift).</li>
</ol>

<p>Six months later, retention improves by 8%, and you can prove causality. That’s ownership. The model didn’t change, the system did!</p>

<h2 id="success-definition">Success Definition</h2>

<p>A metric that is observable and attributable is worth more than a sophisticated loss function.
If you can’t articulate how your metric ties to a decision, you’re optimizing a shadow.
Make the metric boring, then make it true.</p>

<p>Here’s how:</p>

<ul>
  <li>Ask this question relentlessly: “<em>What decision will this change?</em>”</li>
  <li>Then ask the second question: <em>“How will we know it changed for the right reason?</em>”</li>
</ul>

<p>That is problem framing!</p>

<h2 id="data-as-a-product">Data as a Product</h2>

<p>In most large companies, data is treated like an interface with clear ownership, contracts, and checks.</p>

<p>Modern <a href="https://en.wikipedia.org/wiki/Data_mesh">data mesh thinking</a> emphasizes delivering analytical data as a product with interfaces and ownership, not as a byproduct of engineering work.[^3]
In my experience, better engineering around data often beats endless model tweaking.</p>

<p>For me, it works well starting small: add schema contracts, simple checks that fail loudly, and lineage so “what changed?” becomes a quick answer, not a week-long hunt.
That’s what professionals call <em>reliability</em>, and it’s a career booster.</p>

<h2 id="testing-and-production-readiness">Testing and Production Readiness</h2>

<p>Many ML teams under-test because they over-believe the capacity of their models.</p>

<p>A principle from production ML readiness: <a href="https://en.wikipedia.org/wiki/List_of_publications_in_data_science">“it works locally” is not a phase of software development</a>.
Production-ready models have tests, monitoring, rollback plans, and observability.</p>

<p>In the eyes of stakeholders, a model without monitoring is a demo, a pipeline without tests is a riss, and a system without rollback is an incident waiting for its calendar invite.
As a data professional, be the person who prevents that invite.</p>

<h2 id="real-world-ml">Real World ML</h2>

<p>Reality changes, then your model follows, then your dashboard lies and the business gets a hit.
<a href="https://en.wikipedia.org/wiki/Concept_drift">Concept drift</a> is the default state of deployed systems.
You need monitoring for input distributions, outputs, and business KPIs—and a playbook for what to do when drift hits.</p>

<p>Here’s a tiny practical pattern: log predictions and the features that drove them.
Track a drift signal per feature (e.g., <a href="https://www.geeksforgeeks.org/data-science/population-stability-index-psi/">PSI</a>) with thresholds.
Alert on change and define a response.</p>

<p>Here’s a simple PSI implementation in Python:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="n">numpy</span> <span class="k">as</span> <span class="n">np</span>

<span class="k">def</span> <span class="nf">psi</span><span class="p">(</span><span class="n">expected</span><span class="p">,</span> <span class="n">actual</span><span class="p">,</span> <span class="n">bins</span><span class="o">=</span><span class="mi">10</span><span class="p">,</span> <span class="n">eps</span><span class="o">=</span><span class="mf">1e-6</span><span class="p">):</span>
    <span class="n">quantiles</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">quantile</span><span class="p">(</span><span class="n">expected</span><span class="p">,</span> <span class="n">np</span><span class="p">.</span><span class="nf">linspace</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="n">bins</span> <span class="o">+</span> <span class="mi">1</span><span class="p">))</span>
    <span class="n">quantiles</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">quantiles</span><span class="p">[</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">=</span> <span class="o">-</span><span class="n">np</span><span class="p">.</span><span class="n">inf</span><span class="p">,</span> <span class="n">np</span><span class="p">.</span><span class="n">inf</span>

    <span class="k">def</span> <span class="nf">hist</span><span class="p">(</span><span class="n">x</span><span class="p">):</span>
        <span class="n">counts</span><span class="p">,</span> <span class="n">_</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="nf">histogram</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">bins</span><span class="o">=</span><span class="n">quantiles</span><span class="p">)</span>
        <span class="n">p</span> <span class="o">=</span> <span class="n">counts</span> <span class="o">/</span> <span class="nf">max</span><span class="p">(</span><span class="n">counts</span><span class="p">.</span><span class="nf">sum</span><span class="p">(),</span> <span class="mi">1</span><span class="p">)</span>
        <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="nf">clip</span><span class="p">(</span><span class="n">p</span><span class="p">,</span> <span class="n">eps</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>

    <span class="n">e</span> <span class="o">=</span> <span class="nf">hist</span><span class="p">(</span><span class="n">expected</span><span class="p">)</span>
    <span class="n">a</span> <span class="o">=</span> <span class="nf">hist</span><span class="p">(</span><span class="n">actual</span><span class="p">)</span>

    <span class="k">return</span> <span class="n">np</span><span class="p">.</span><span class="nf">sum</span><span class="p">((</span><span class="n">a</span> <span class="o">-</span> <span class="n">e</span><span class="p">)</span> <span class="o">*</span> <span class="n">np</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="n">a</span> <span class="o">/</span> <span class="n">e</span><span class="p">))</span>
</code></pre></div></div>

<p>The point: measurable signal, threshold, and response, so that when drift hits (and will do), you’re ready to act.</p>

<h2 id="llms-add-a-new-failure-mode-non-determinism">LLMs Add a New Failure Mode (Non-Determinism)</h2>

<p>LLMs make capability cheap and behavior slippery.
Nondeterminism is baked into the architecture: sampling settings like <code class="language-plaintext highlighter-rouge">temperature</code> exist because outputs vary across runs, even with identical inputs.
Your system becomes probabilistic even when your code isn’t.</p>

<p>Once integrated, new failure modes appear:</p>

<ul>
  <li><strong>Prompt regressions:</strong> A small wording change can flip the output.</li>
  <li><strong>Silent degradation:</strong> The model changes under you (version updates, temperature drift), and you don’t notice until users complain.</li>
  <li><strong>Cost spikes:</strong> Token counts explode because the model got chattier or a prompt was rewritten poorly.</li>
</ul>

<p>Here’s what helps:</p>

<ol>
  <li><strong>Version prompts like code.</strong> Store them in Git (not a Notion doc). Track changes, review diffs, and tie versions to deployments.</li>
  <li><strong>Test prompts systematically.</strong> Use a fixed eval set with known inputs and expected outputs. Measure pass rate, not vibes.</li>
  <li><strong>Monitor output distributions.</strong> Track length, sentiment, keyword frequency, any signal that catches behavioral drift before it cascades.</li>
  <li><strong>Track cost per call.</strong> Set alerts when average token counts spike. LLMs are elastic infrastructure; cost is a signal, not just a bill.</li>
</ol>

<p>The pattern is the same as classic ML: make behavior observable, drift detectable, and regressions catchable.
The difference is that LLMs fail in new ways, silent, stochastic, and expensive.
So you add new defenses: prompt tests, output monitoring, and cost tracking.</p>

<p>That’s ownership in the LLM era.</p>

<h2 id="governance-is-part-of-the-job">Governance Is Part of the Job</h2>

<p>If your system influences people, money, or risk, governance shows up.</p>

<p>The <a href="https://en.wikipedia.org/wiki/Artificial_Intelligence_Act">European Union’s AI Act</a> mandates quality, transparency, human oversight, and safety obligations for high-risk AI systems.
Deployers are expected to monitor and log behavior, assign human oversight, and ensure input data relevance.<sup id="fnref:5"><a href="#fn:5" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>
Compliance is insurance: auditability, explainability of intent, and clarity when things go wrong.</p>

<h1 id="role-shifts-to-expect">Role Shifts To Expect</h1>

<p>Here’s how I see data-related roles evolving in 2026:</p>

<ul>
  <li>
    <p><strong>Analytics roles</strong> are becoming more decision-shaped. Dashboards are cheap once data is available, so value moves to interpretation and “what should we do next?” If you can’t recommend action, you’re doing archaeology.</p>
  </li>
  <li>
    <p><strong>Applied ML roles</strong> are becoming more end-to-end. The center of gravity shifts from model building to operating the full loop: definition, deployment, monitoring, and iteration. Accountability becomes the differentiator.</p>
  </li>
  <li>
    <p><strong>Research roles</strong> still exist, but they aren’t the default. Only a handful of organizations push the frontier; most companies are trying to turn capabilities into reliable products. That gap is widening, so pick your lane intentionally.</p>
  </li>
</ul>

<h1 id="career-advice">Career Advice</h1>

<p>Optimize for ownership.
Pick one production system and own the loop: define the metric, ship the pipeline, add monitoring, write a runbook, and measure impact.
Ownership is the fastest teacher.</p>

<p>Learn “just enough engineering.”
You don’t need to become a backend polyglot.
You do need deployment basics, monitoring, versioning, and incident response.
Knowing how work ships turns you from “the data person” into “the person who makes things happen.”</p>

<p>Translate your work into business outcomes.
Accuracy is interesting; outcomes are persuasive.
Speak in revenue, cost, risk, retention, and customer experience.
Then decision-makers stop seeing you as technical support, and start seeing you as a partner.</p>

<h1 id="summary">Summary</h1>

<p>In 2026, the folks who struggle in data roles aren’t the ones missing the newest tool.
They’re the ones who stop at analysis and hand off reality.
They ship a model, deliver a slide deck, and say “I’m done.”
But reality laughs and shifts the distribution.</p>

<p>The people who thrive do something simpler and rarer.
They stay curious when systems get messy.
They lean in when production contradicts the notebook.
They care about outcomes, not artifacts.</p>

<p>When building models become easier, shifting towards complexity becomes the right move.
And it’s my belief that owning systems end to end is that move.</p>

<h1 id="footnotes">Footnotes</h1>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>D. Sculley et al., <a href="https://papers.neurips.cc/paper/5656-hidden-technical-debt-in-machine-learning-systems.pdf"><em>Hidden Technical Debt in Machine Learning Systems</em></a>, NeurIPS 2015. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>Aria Khademi et al., <a href="https://arxiv.org/abs/2302.00775"><em>Model Monitoring and Robustness of In-Use ML Models: Quantifying Data Distribution Shifts Using Population Stability Index</em></a>, arXiv 2023. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5">
      <p>Article 26, <a href="https://artificialintelligenceact.eu/article/26/"><em>Obligations of Deployers of High-Risk AI Systems</em></a> (AI Act detail). <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>César Soto Valero</name><email>cesarsotovalero@gmail.com</email></author><category term="career" /><summary type="html"><![CDATA[Models are getting easier to train and easier to copy. The scarce skill is owning the whole system: data, deployment, monitoring, governance, and the business outcomes that fall out of it.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.cesarsotovalero.net/img/posts/2026/2026-01-15/fatbursparken.jpg" /><media:content medium="image" url="https://www.cesarsotovalero.net/img/posts/2026/2026-01-15/fatbursparken.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Why Creativity Just Became the Most Practical Skill</title><link href="https://www.cesarsotovalero.net/blog/creativity-just-became-the-most-practical-skill.html" rel="alternate" type="text/html" title="Why Creativity Just Became the Most Practical Skill" /><published>2026-01-10T00:00:00-08:00</published><updated>2026-04-02T05:57:52-07:00</updated><id>https://www.cesarsotovalero.net/blog/creativity-just-became-the-most-practical-skill</id><content type="html" xml:base="https://www.cesarsotovalero.net/blog/creativity-just-became-the-most-practical-skill.html"><![CDATA[<aside class="youtube">
        <a href="https://www.youtube.com/watch?v=pHqoIPAlOcw"><div class="box">
        <img src="https://i.ytimg.com/vi/pHqoIPAlOcw/mqdefault.jpg" alt="YouTube video #pHqoIPAlOcw" />
        <div class="play">
          <img src="/img/icons/youtube-play20px.svg" alt="Play Video" class="youtube-logo" />
        </div>
        </div></a>
        <div>Why Creativity Just Became the Most Practical Skill; 8 January 2026.</div></aside>

<p>In 1996, Larry Page was a PhD student at Stanford.
Just the right time and place to be a protagonist on the rise of the nacient internet.
Back then, most search engines were trying to figure it out how to provide better ranking results automatically.
They were all reading the content of webpages, using keywords, text parsing, in-page signals, and so on.
But Larry tried something different.</p>

<p>He asked a different question:</p>

<p>“<em>What if a page’s value is not about the information it contains, but in how many other pages point to it?</em>”</p>

<p>That reframing became the <a href="https://en.wikipedia.org/wiki/PageRank">PageRank algorithm</a>, a ranking system built on the web’s hyperlink graph (not just its text).<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>
The rest is hitory, PageRank helped shape what later on became the dominat search engine and one of the most prominet companies in the wrold: Google.</p>

<p>But this post is not about the history of web or search engines (I wrote <a href="./blog/the-evolution-of-the-web-from-html-to-webassembly">another post</a> about it).
It’s about another kind of engine.
The engine behind human breakthroughs, something we call: creativity as perspective-shift.</p>

<p>We are now in AI era.
Right now, LLMs can generate thousands of <del>surprisingly</del> “good” outputs.
However, they still struggles to choose what is worth building, what is actually relevant (and <em>why</em>).
It’s my believe that this selection function (taste, judgment, values) is where humans still keep an edge over AI.</p>

<p>So, in this post, I’ll cover three things:</p>

<ol>
  <li>Why creativity just became the most practical skill for humans.</li>
  <li>How creativity works according to what research suggests is happens in our minds.</li>
  <li>How you can grow your creativity with simple, repeatable practices.</li>
</ol>

<p>Let’s dive in!</p>

<h1 id="creativity--productivity">Creativity ≠ Productivity</h1>

<p>Recently, I put a lot effort to bully my brain into being creative.
I treated creativity like productivity and assumed intensity would eventually pay off.
So I tried to focus harder, longer, and with more discipline than ever.
The result was more effort and fewer breakthroughs.</p>

<p>Creativity doesn’t reward effort the way execution does.</p>

<p>Creativity prefers slack, not strain.
Neuroscientists describe a relevant system called the <em>Default Mode Network</em> (DMN), which becomes more active when you’re at rest and thinking internally.<sup id="fnref:3"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">2</a></sup>
The DMN is linked to mind-wandering, memory, and our ability to recombining unrelated things into new patterns.<sup id="fnref:4"><a href="#fn:4" class="footnote" rel="footnote" role="doc-noteref">3</a></sup></p>

<p>In plain terms, the DMN is brain’s background “merge request” process.
When you never give it idle time, nothing new gets merged.</p>

<p>So instead of focus harder, what I do is to switch states.</p>

<p>One well-known study found that an undemanding task that allows the mind to wander can improve creative incubation compared to staying locked in.<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">4</a></sup></p>

<p>But this doesn’t mean boredom is magical.
It means boredom is permissive, and your brain takes the permission seriously.
If you want originality, you the best way is to stop supervising every thought.</p>

<figure class="badge"><a href="https://a.co/d/28tGFRS"><img src="/img/badges/daniel.png" style="width:140px;max-width:100%;" alt="badge" /></a></figure>

<p>Now when I’m stuck, I stop trying harder and start making space.</p>

<ul>
  <li>I walk without inputs (no phone, no podcast).</li>
  <li>I do dishes, clean the floor, or do something else.</li>
  <li>I take the boring route home.</li>
</ul>

<p>Then, I let the brain do what it does when nobody is watching: wandering about things.</p>

<h1 id="feed-the-blender">Feed the Blender</h1>

<p>Here’s the thing.
Your brain is not a laser.
It’s more like a blender.
What comes out depends on what goes in.
If your inputs are all the same creators, the same feeds, the same opinions, your creative output becomes high-resolution <em>déjà vu</em>.</p>

<p>A smarter strategy is to diversify the ingredients, not increase the volume.</p>

<figure class="badge"><a href="https://a.co/d/hgrcMMN"><img src="/img/badges/good-ideas.png" style="width:140px;max-width:100%;" alt="badge" /></a></figure>

<p><a href="https://en.wikipedia.org/wiki/Steven_Johnson_(author)">Steven Johnson</a> describes breakthrough environments as <em>liquid networks</em>: idea ecosystems where half-formed hunches collide, combine, and evolve.<sup id="fnref:5"><a href="#fn:5" class="footnote" rel="footnote" role="doc-noteref">5</a></sup></p>

<p>This matters because creativity is often the product of collisions of new ideas, not contemplation of what’s alrady known.
If you only consume polished conclusions, you train your mind to reproduce conclusions.
If you collect fragments, you give your mind raw material.
This is how random insights become repeatable.</p>

<p>So build a liquid network on purpose.</p>

<p>For example:</p>

<ul>
  <li>Watch a YouTube video with under 1K views (there are many of then in my <a href="https://youtube.com/@cesarsotovalero">YouTube channel</a>)</li>
  <li>Read an old essay from last century.</li>
  <li>Open a weird old book from another field (history, biology, architecture).</li>
  <li>Talk to someone outside your network and ask what they’re obsessing over.</li>
</ul>

<p>Most of it will be useless (that’s the price of finding the one useful oddity).
But one strange idea can spark ten better ones.</p>

<p><a href="https://www.highagency.com/">George Mack</a> argues for hunting “anti-social proof” inputs (i.e., good ideas that haven’t been blessed by the algorithm yet).<sup id="fnref:7"><a href="#fn:7" class="footnote" rel="footnote" role="doc-noteref">6</a></sup></p>

<p>I believe the post-2022 internet is increasingly saturated with synthetic sameness, so new is not the same thing as novel.</p>

<p>So the goal is definitely not consume more, the goal is consume with rarity as the intent.</p>

<p>Cause sometimes the best way to think differently is to stop drinking from the same firehose.</p>

<h1 id="translate-aggressively">Translate Aggressively</h1>

<p>If you want a new idea, change the format.</p>

<ul>
  <li>If your idea is written, read it out loud.</li>
  <li>If your idea is visual, write it as prose.</li>
  <li>If your idea is very technical, explain it to a five-year-old.</li>
</ul>

<p>Each translation forces the brain to rebuild the idea, not just rephrase it.</p>

<p>Cognitive scientists call this <em>conceptual blending</em>:
Combining elements from different mental spaces into a new structure with emergent meaning.<sup id="fnref:8"><a href="#fn:8" class="footnote" rel="footnote" role="doc-noteref">7</a></sup></p>

<p>This is why changing the medium often changes the idea.
With this technique, you’re not just changing representation, you’re changing which associations become available.</p>

<p>So when you’re stuck, don’t think harder.
Think sideways.
Switch the communication channel.
Say it, sketch it, storyboard it, teach it.
Your brain will do the connecting if you give it a new surface to connect on.</p>

<h1 id="walk-away-to-win">Walk Away to Win</h1>

<p>Some solutions only show up after you stop chasing them.</p>

<p>The <em>incubation effect</em> is the benefit you can get by setting a problem aside and returning later with improved insight or originality.<sup id="fnref:9"><a href="#fn:9" class="footnote" rel="footnote" role="doc-noteref">8</a></sup>
Stepping away can help because attention relaxes and alternative associations have room to form.</p>

<p>This connects to the earlier undemanding task finding.
Mind-wandering can facilitate creative incubation, especially after you’ve already done focused work.</p>

<p>So yes, “shower thoughts” are a meme, but the mechanism is real enough to take it seriously.</p>

<p>Your best ideas often arrive when your brain stops auditioning for them:</p>

<ul>
  <li>Take short walks.</li>
  <li>Do idle chores.</li>
  <li>Create small pockets of low-stimulation downtime.</li>
</ul>

<p>Then come back and do one concrete next step.</p>

<p>Incubation is not procrastination if you return with intent.</p>

<h1 id="fight-your-evil-twin">Fight Your Evil Twin</h1>

<p>Here’s an exercise that is both effective and mildly ridiculous.</p>

<p>Imagine an evil twin version of you.
Same skills, same knowledge.
Their only job is to outthink you.
What would they try that you’re too cautious to attempt?</p>

<p>This is <em>counterfactual thinking</em> with a costume on.
By blaming risky ideas on your “evil twin,” you remove your ego from the room.
No embarrassment, no fear of looking stupid, just experiments.</p>

<p>George Mack describes this “evil twin” technique as a way to bypass your internal status police.<sup id="fnref:7:1"><a href="#fn:7" class="footnote" rel="footnote" role="doc-noteref">6</a></sup>
And once ideas start flowing, you can switch back to being a responsible adult.</p>

<p>You can try to:</p>

<ul>
  <li>Write down ten things your twin would do.</li>
  <li>Steal the best two.</li>
  <li>Run tiny experiments instead of grand commitments.</li>
</ul>

<p>Creativity hates drama and loves iterations.</p>

<p>If you can make it small, you can make it real.</p>

<h1 id="turn-the-faucet-on">Turn the Faucet On</h1>

<p>People kill creativity by demanding quality too early.</p>

<p>At the start, the output is supposed to be bad, and that’s OK.</p>

<p>This is <em>divergent thinking</em>: generating many possibilities (including obvious and flawed ones) to escape the first ideas your brain serves you.<sup id="fnref:10"><a href="#fn:10" class="footnote" rel="footnote" role="doc-noteref">9</a></sup></p>

<p>Quantity is not the goal, it’s the tool.</p>

<p>A practical execution order is the following:</p>

<ol>
  <li>Volume.</li>
  <li>Selection.</li>
  <li>Refinement.</li>
</ol>

<p>If you judge too early, you stop the faucet before the clean water arrives.</p>

<p>Try this once, seriously.</p>

<p>Set a timer for ten minutes.
Generate twenty ideas and do not evaluate.
Circle the three least stupid ideas.
Improve one of them.</p>

<p>Creativity is less a light bulb and more a faucet, and the first water coming out is always dirty.</p>

<h1 id="constraints-are-rocket-fuel">Constraints Are Rocket Fuel</h1>

<p>Unlimited freedom is overrated.</p>

<p>Constraints do two useful things: they exclude unhelpful options and they focus attention where novelty can happen.</p>

<p><a href="https://en.wikipedia.org/wiki/Patrick_Stokes_(businessman)">Patricia Stokes</a> argues that constraints can be a reliable source of breakthrough because they force you out of default patterns.<sup id="fnref:11"><a href="#fn:11" class="footnote" rel="footnote" role="doc-noteref">10</a></sup></p>

<p>Limits are not cages, they are search spaces:</p>

<ul>
  <li>Use constraints deliberately</li>
  <li>Write a full outline using only twelve sentences.</li>
  <li>Explain your idea without using your favorite buzzwords.</li>
  <li>Build the concept with one example, not ten.</li>
  <li>Draft in twenty minutes, then stop.</li>
</ul>

<p>Constraints make you choose, and choice is where originality starts.</p>

<p>AI makes it tempting to expand forever.
But expansion is not invention.
When every option is cheap, commitment becomes the differentiator.
Constraints create commitment by design.</p>

<p>If you want a sharper mind, impose sharper edges.</p>

<h1 id="creative-fasting">Creative Fasting</h1>

<p>Your mind can’t have its own thoughts if it’s always renting someone else’s.</p>

<p>Once or twice a year, consider a creative fast.
No news, no social media, no podcasts, minimal algorithmic feeds.
Just enough quiet to notice what your brain does when it isn’t being constantly recruited.
Think of it as intermittent fasting for attention.</p>

<p>People often cite Japan’s <a href="https://en.wikipedia.org/wiki/Sakoku">sakoku</a> policy as a metaphor for creative isolation (even though the historical reality was more nuanced than total isolation).
The point of the metaphor is not isolation is good, but silence reveals signal.</p>

<p>There’s also a motivation angle: evaluation pressure and extrinsic incentives can undermine originality.</p>

<p>Teresa Amabile’s classic piece explains how environments accidentally kill creativity by squeezing intrinsic motivation.<sup id="fnref:13"><a href="#fn:13" class="footnote" rel="footnote" role="doc-noteref">11</a></sup>
When nobody’s watching, your inner voice gets louder.</p>

<p>Creative fastinf is not anti-technology mindset.</p>

<p>It’s pro-agency.</p>

<p>It’s how you stop the algorithm from drafting your personality.</p>

<p>If you feel behind when you unplug, good.
That sensation is your dependency showing itself.
Detach long enough to remember what you actually care about.</p>

<h1 id="a-15-minutes-protocol">A 15 Minutes Protocol</h1>

<p>Creativity becomes practical when it becomes scheduled:</p>

<ul>
  <li>Write the problem as a question (2 minutes).</li>
  <li>Generate ten bad ideas fast (5 minutes).</li>
  <li>Translate the best idea into a different medium (3 minutes).</li>
  <li>Stop and do nothing useful (5 minutes).</li>
  <li>Return and choose one next action.</li>
</ul>

<p>This way, you’re training two muscles: generation and judgment.
AI can help with generation, but you must own the judgment.</p>

<p>Do this daily for a week.
So your brain learns the pattern: create, step away, return, choose.
And over time, the “creative state” will start becomina a practiced state.</p>

<h1 id="final-thoughts">Final Thoughts</h1>

<p>Creativity is not a talent.</p>

<p>It’s a <em>state</em> you can reliably enter by managing your inputs, your boredom, and your fear of bad ideas.</p>

<p>AI can now produce what is statistically likely.
But creativity often means producing what is structurally and fundamentally new.</p>

<p>Margaret Boden’s call this <em>transformational creativity</em> (changing the rules of the game, not just the pieces).<sup id="fnref:15"><a href="#fn:15" class="footnote" rel="footnote" role="doc-noteref">12</a></sup></p>

<p>For now, that gap between “likely” and “new” is where humans still have an edge.</p>

<p>So when everyone else asks:</p>

<p>“<em>What can I automate?</em>”</p>

<p>Ask instead:</p>

<p>“<em>What can I create that is worth automating toward?</em>”</p>

<p>Because in an AI-saturated world, relevance does not come from output.
It comes from taste, direction, and the courage to reframe the question again, and again, and again.</p>

<h1 id="footnotes">Footnotes</h1>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Lawrence Page et al., <a href="https://ilpubs.stanford.edu/422/"><em>The PageRank Citation Ranking: Bringing Order to the Web</em> (Stanford)</a>, and Brin &amp; Page, <a href="https://research.google/pubs/the-anatomy-of-a-large-scale-hypertextual-web-search-engine/"><em>The Anatomy of a Large-Scale Hypertextual Web Search Engine</em> (Google Research page)</a> (PDF mirror: <a href="https://snap.stanford.edu/class/cs224w-readings/Brin98Anatomy.pdf">PDF</a>). <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3">
      <p>Raichle et al. (2001), <a href="https://www.pnas.org/doi/10.1073/pnas.98.2.676"><em>A Default Mode of Brain Function</em> (PNAS)</a>. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4">
      <p>Buckner, Andrews-Hanna, &amp; Schacter (2008), <a href="https://nyaspubs.onlinelibrary.wiley.com/doi/abs/10.1196/annals.1440.011"><em>The Brain’s Default Network: Anatomy, Function, and Relevance to Disease</em> (Annals of the NY Academy of Sciences, abstract)</a>. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>Baird et al. (2012), <a href="https://www.cmhp.ucsb.edu/sites/default/files/2018-12/Baird%20et%20al.%20%282012%29%20Inspired%20by%20distraction.pdf"><em>Inspired by Distraction: Mind Wandering Facilitates Creative Incubation</em> (PDF)</a> (PubMed record: <a href="https://pubmed.ncbi.nlm.nih.gov/22941876/">PubMed</a>). <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5">
      <p>Steven Johnson, <a href="https://ed.ted.com/best_of_web/EaMi6IZF">“<em>Where Good Ideas Come From</em>” (TED Talk)</a>. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:7">
      <p>George Mack, <a href="https://essays.highagency.com/p/how-to-be-creative-without-taking"><em>How to be creative (without taking drugs)</em></a>. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:7:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a></p>
    </li>
    <li id="fn:8">
      <p>Fauconnier &amp; Turner (2003), <a href="https://tecfa.unige.ch/tecfa/maltt/cofor-1/textes/Fauconnier-Turner03.pdf"><em>Conceptual Blending, Form and Meaning</em> (PDF)</a>. <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:9">
      <p>Sio &amp; Ormerod (2009), <a href="https://gwern.net/doc/psychology/writing/2009-sio.pdf"><em>Does incubation enhance problem solving? A meta-analytic review</em> (PDF mirror)</a>. <a href="#fnref:9" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:10">
      <p>APA Monitor, <a href="https://www.apa.org/monitor/2022/04/cover-science-creativity">“The science behind creativity” (includes Guilford’s role and divergent thinking context)</a>. <a href="#fnref:10" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:11">
      <p>Patricia D. Stokes, <a href="https://api.pageplace.de/preview/DT0400.9780826197856_A26559201/preview-9780826197856_A26559201.pdf"><em>Creativity from Constraints: The Psychology of Breakthrough</em> (preview PDF)</a>. <a href="#fnref:11" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:13">
      <p>Teresa Amabile, <a href="https://hbr.org/1998/09/how-to-kill-creativity"><em>How to Kill Creativity</em> (Harvard Business Review)</a> (PDF copy: <a href="https://www.pickardlaws.com/myleadership/myfiles/rtdocs/hbr/HowToKillCreativityHBR0998.pdf">PDF</a>). <a href="#fnref:13" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:15">
      <p>Margaret A. Boden, <a href="https://www.taylorfrancis.com/books/mono/10.4324/9780203508527/creative-mind-margaret-boden"><em>The Creative Mind: Myths and Mechanisms</em> (Taylor &amp; Francis page)</a>. <a href="#fnref:15" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>César Soto Valero</name><email>cesarsotovalero@gmail.com</email></author><category term="career" /><summary type="html"><![CDATA[In an AI-saturated world where execution is cheap, the scarce resource is original framing. This blog post explores how to treat creativity as a practical, trainable skill and how to build it with simple daily habits.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.cesarsotovalero.net/img/posts/2026/2026-01-10/art-ribbon.jpg" /><media:content medium="image" url="https://www.cesarsotovalero.net/img/posts/2026/2026-01-10/art-ribbon.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">When Answers Get Cheaper, Questions Are Gold</title><link href="https://www.cesarsotovalero.net/blog/when-answers-get-cheaper-questions-are-gold.html" rel="alternate" type="text/html" title="When Answers Get Cheaper, Questions Are Gold" /><published>2025-08-14T00:00:00-07:00</published><updated>2026-04-02T05:57:52-07:00</updated><id>https://www.cesarsotovalero.net/blog/when-answers-get-cheaper-questions-are-gold</id><content type="html" xml:base="https://www.cesarsotovalero.net/blog/when-answers-get-cheaper-questions-are-gold.html"><![CDATA[<p>I remember, in my infancy, I had access to only a few old books at home.
The new ones either weren’t available or were too expensive to acquire.
If I wanted to read good content back then, I had to walk to the local library and do it on-site.</p>

<p>Later on at university, I observed that professors were constantly reading research papers.<sup id="fnref:3"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>
They were respected for the sheer amount of knowledge they had accumulated over a lifetime.
We valued them because their knowledge and expertise were targeted and scarce.</p>

<p>Now, in my 30s, reading books is not as trendy as before, knowledge and papers are available from anywhere.
Indeed, getting access to information is cheaper than ever, and even the latest scientific advances move at lightspeed.
A curious 10-year-old today can access cutting-edge tools and learn from the latest scientific discoveries, right from home!</p>

<p>More remarkably, AI has drastically changed the way we value knowledge in the last few years.
Everyone can now prompt an AI assistant (like ChatGPT) for free and get almost instant answers to practically any imaginable question.
I believe AI represents an optimization milestone in the way we search for human knowledge.
The impact could be even more than what the Google search engine did in the early days of the internet.</p>

<p>AI has made answers abundant and <a href="https://en.wikipedia.org/wiki/General_knowledge">general knowledge</a> intuitively accessible.
However, current AI models still struggle to figure out how to apply, synthesize, and adapt existing knowledge to create something genuinely new.
This limitation is a consequence of the way current Large Language Models (LLMs) operate.</p>

<p>With the rise of more advanced reasoning models, the ability to formulate the right questions to drive AI in the right direction is a skill we need to incorporate into our everyday lives.
AI has shifted our focus from trying to find answers to thinking about <em>what</em> and <em>how</em> to formulate the right questions.</p>

<p>I’d argue that as the value of getting answers goes down, the value of formulating new original questions that drive actual action should go up.
So, coming up with original and relevant questions represents a real competitive edge for everyday tasks, i.e., a real differentiator.<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup></p>

<p>What’s more, I believe that the perceived value of those who have mastered the “art of asking questions” will increase accordingly.
Just look at the rise of professions that rely heavily on questioning skills such as podcasters and interviewers.
These guys have mastered the ability to ask good questions and extract value for their audience.</p>

<p>Today, I see myself doing a conscious effort at becoming better at the art of asking questions.
This post is about techniques, patterns and anti-patterns I’ve learned over time from this practice.
Let’s dive in!</p>

<p>👉 Check out my compilation of <a href="../files/posts/2025/2025-08-14/tech-interview-questions.md">Job Interview Questions</a>.<sup id="fnref:4"><a href="#fn:4" class="footnote" rel="footnote" role="doc-noteref">3</a></sup></p>

<h1 id="when-to-ask-questions">When to Ask Questions?</h1>

<!-- Video from Veritasium: "The Blender Question Everyone Gets Wrong"  -->

<aside class="youtube">
        <a href="https://www.youtube.com/watch?v=dFVrncgIvos"><div class="box">
        <img src="https://i.ytimg.com/vi/dFVrncgIvos/mqdefault.jpg" alt="YouTube video #dFVrncgIvos" />
        <div class="play">
          <img src="/img/icons/youtube-play20px.svg" alt="Play Video" class="youtube-logo" />
        </div>
        </div></a>
        <div>The Blender Question Everyone Gets Wrong; 18 February 2025.</div></aside>

<p>Short answer: every time you can!</p>

<p>Back in the days, asking frequent and awkward questions was considered an annoying practice in many cases.
Why?
I’d argue that our inherent human laziness had something to do with it.
Leaving a question hanging around meant somebody had to squeeze his brain to find an answer (i.e., to do mental work).
Otherwise, an uncomfortable void of uncertainty threatened to emerge.</p>

<p>But this is no longer the case.</p>

<p>AI has completely removed the fear of “feeling stupid” for both sides: the asker and the responder.
The more we use AI, the more comfortable we become with the idea of questioning everything.
In meetings or events, asking is a signal of attention, and in an era where AI can respond fast, it also signals the ability to challenge the <em>status quo</em> and express a personal opinion, which are human traits with increasing value.</p>

<p>The more questions you ask, the better prepared you will be for whatever is coming next.</p>

<h1 id="techniques">Techniques</h1>

<aside class="quote">
    <em>“A problem well stated is a problem half-resolved.”</em>
    <cite><br /> ― <a href="https://en.wikipedia.org/wiki/Charles_F._Kettering">CF Kettering</a></cite>
</aside>

<p>Good questions have purpose and “make sense” (in general).<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">4</a></sup>
They are unambiguous, give just enough context, and set limits.</p>

<p>Scope is reduced so that answer feel smaller.
The question itself must strip away everything irrelevant.
As a rule of thumb, if a the question doesn’t make any difference to the argument, it needs to be sharpened.</p>

<p>There are three techniques to improve question formulation:</p>

<ul>
  <li>Aim, scope, and payoff (ASP)</li>
  <li>Clarity, context, and constraints (3C)</li>
  <li>Falsifiability and measurability (FM)</li>
</ul>

<h2 id="aim-scope-payoff-asp">Aim, Scope, Payoff (ASP)</h2>

<p>Without aim you wander.
Without scope you boil the ocean.
Without payoff you can’t act.
A good question invokes change.
The faster the change, the more effective the question.
Ideally, you want to change something in the next 10 minutes.</p>

<p>So, the core idea of this technique is to state what you’re trying to achieve (aim), how far you’ll look (scope), and what you’ll do with the answer (payoff).</p>

<table>
  <thead>
    <tr>
      <th>Element</th>
      <th>Description</th>
      <th>Template</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Aim</strong></td>
      <td>What you want to achieve.</td>
      <td>“We want to __.”</td>
    </tr>
    <tr>
      <td><strong>Scope</strong></td>
      <td>Boundaries of your inquiry (data, time, users, tools).</td>
      <td>“Within __ (data/time/users/tools).”</td>
    </tr>
    <tr>
      <td><strong>Payoff</strong></td>
      <td>What you’ll do with the answer.</td>
      <td>“So we can __ (decision/action/experiment).”</td>
    </tr>
  </tbody>
</table>

<p>Examples:</p>

<table>
  <thead>
    <tr>
      <th>❌ Before (Vague)</th>
      <th>✅ After (ASP Applied)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Product:</strong> “How do we improve onboarding?”</td>
      <td><strong>Aim:</strong> Increase week-1 activation.<br /><strong>Scope:</strong> Mobile iOS, signup flow only.<br /><strong>Payoff:</strong> Decide which of 3 changes to A/B test.<br /><strong>Question:</strong> “What single change in the iOS signup flow would most increase week-1 activation, and which 3 options should we A/B test first?”</td>
    </tr>
    <tr>
      <td><strong>Research:</strong> “Is our model good?”</td>
      <td><strong>Aim:</strong> Decide to ship or retrain.<br /><strong>Scope:</strong> Fraud model v2, last 30 days.<br /><strong>Payoff:</strong> Go/no-go.<br /><strong>Question:</strong> “Given the last 30 days, does <code class="language-plaintext highlighter-rouge">fraud-model-v2</code> beat <code class="language-plaintext highlighter-rouge">fraud-model-v1</code> on precision by ≥2pp at equal recall, so we ship or retrain?”</td>
    </tr>
    <tr>
      <td><strong>Personal:</strong> “How can I get healthier?”</td>
      <td><strong>Aim:</strong> Improve sleep.<br /><strong>Scope:</strong> Next 14 days, bedtime routine only.<br /><strong>Payoff:</strong> Adopt one habit.<br /><strong>Question:</strong> “Which single bedtime habit should I try for 14 days to raise average sleep by 30 minutes?”</td>
    </tr>
  </tbody>
</table>

<h2 id="clarity-context-constraints-3c">Clarity, Context, Constraints (3C)</h2>

<p>Clarity prevents misreads, context prevents wheel-reinvention, constraints prevent infinite “it depends.”
You want to add just enough context to the question to make it sound enough, at the same time that you force a direct answer to it.</p>

<p>The core idea of the 3C tecnique is to make the question unambiguous, include the background that matters, and set limits that force trade-offs.</p>

<table>
  <thead>
    <tr>
      <th>Element</th>
      <th>Description</th>
      <th>Example</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Clarity</td>
      <td>Define terms, units, and success. Prefer nouns/verbs over adjectives.</td>
      <td>“Write-heavy database workload (~5k writes/sec), 99.9% latency &lt;10ms”</td>
    </tr>
    <tr>
      <td>Context</td>
      <td>Minimum viable backstory: objective, prior attempts, relevant data.</td>
      <td>“Client churn up 3% in SMB last quarter”</td>
    </tr>
    <tr>
      <td>Constraints</td>
      <td>Time, budget, tools, risk tolerance, guardrails.</td>
      <td>“EU-only, managed service, must decide by Friday”</td>
    </tr>
  </tbody>
</table>

<p>Quick rewrites:</p>

<table>
  <thead>
    <tr>
      <th>❌ Before (Vague)</th>
      <th>✅ After (3C Applied)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>“What’s the best database?”</td>
      <td>“For a write-heavy workload (~5k writes/sec), 99.9% latency &lt;10ms, EU-only, managed service, what database should we evaluate first and why?”</td>
    </tr>
    <tr>
      <td>“How should I learn LLMs?”</td>
      <td>“With 5 hours/week for 6 weeks and access to GCP, what learning plan gets me from zero to fine-tuning a small model on our support tickets?”</td>
    </tr>
    <tr>
      <td>“Can we migrate quickly?”</td>
      <td>“With a 2-month deadline, 3 engineers, $30k budget, and zero downtime tolerance, can we migrate the existing pipeline from on-prem to BigQuery?”</td>
    </tr>
  </tbody>
</table>

<h2 id="falsifiability-and-measurability-fm">Falsifiability and Measurability (FM)</h2>

<p>Decisions stick when they survive attempts to disprove them.
Good questions drive rapid decisions.
It’s always easier to give a boolean answer when enough context is provided.
Measurements is one of the best ways to give such a context.
With measures, one can turn opinion into actionable feedback.</p>

<p>The core idea of the FM technique is to phrase questions so answers can be tested. If it can’t be wrong, it can’t be right.</p>

<table>
  <thead>
    <tr>
      <th>Pattern</th>
      <th>Description</th>
      <th>Example</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Hypothesis form</td>
      <td>State belief, expected outcome, magnitude, audience, and timeframe.</td>
      <td>“We believe shortening the signup form from 7 to 4 fields will raise iOS activation by 3–5% for new users within 14 days.”</td>
    </tr>
    <tr>
      <td>Acceptance criteria</td>
      <td>Define clear pass/fail metrics.</td>
      <td>“Ship the model to production if precision ≥ 0.92 with recall ≥ 0.55 on June data.”</td>
    </tr>
    <tr>
      <td>Disconfirmers first</td>
      <td>Identify what would prove the belief false.</td>
      <td>“If activation does not increase by ≥1% after 7 days with 95% CI, abandon the change.”</td>
    </tr>
  </tbody>
</table>

<p>Quick Rewrite:</p>

<table>
  <thead>
    <tr>
      <th>❌ Before (Vague)</th>
      <th>✅ After (FM Applied)</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>“Will the new pricing work?”</td>
      <td>“In a 50/50 geo split for 21 days, does new pricing increase gross margin per user by ≥4% with no more than a 1pp drop in conversion?”</td>
    </tr>
    <tr>
      <td>“Is the model good enough?”</td>
      <td>“On the August dataset, does the model achieve F1 ≥ 0.82 and AUC ≥ 0.9, with inference latency ≤ 120ms on 95% of requests?”</td>
    </tr>
    <tr>
      <td>“Should we improve onboarding?”</td>
      <td>“For new users signing up in September, does reducing onboarding steps from 5 to 3 increase 7-day retention by ≥6% without lowering NPS?”</td>
    </tr>
  </tbody>
</table>

<h1 id="patterns">Patterns</h1>

<p>Reusable question patterns help you think more clearly, spot blind spots, and get better answers - faster.</p>

<p>Here are eight patters you can borrow and use everyday.</p>

<ol>
  <li>
    <p><strong>First-principles</strong> (“What are the primitives?”): Strip away jargon and complexity until you get to the most basic truths. From there, rebuild your understanding. This works because reality is easier to reason about than opinions.</p>
  </li>
  <li>
    <p><strong>Inversion</strong> (“How could this fail?”): Instead of only asking how to succeed, flip the question and look for ways it could go wrong. Anticipating failure is often the fastest way to avoid it.</p>
  </li>
  <li>
    <p><strong>Disconfirming evidence</strong> (“What would prove me wrong?”): Ask what would make your current belief false. This keeps you from cherry-picking facts that only confirm your view.</p>
  </li>
  <li>
    <p><strong>Assumption audit</strong> (“What am I taking for granted?”): List the things you believe are true without checking.
Questioning assumptions often reveals the weakest part of your thinking.</p>
  </li>
  <li>
    <p><strong>Constraint lens</strong> (“What if we had half the time/budget?”): Imagine having fewer resources. Constraints force creativity, sharpen priorities, and surface shortcuts you might have missed.</p>
  </li>
  <li>
    <p><strong>Comparative calibration</strong> (“Compared to what?”): Numbers and claims mean little without context.
Always anchor them to a baseline, a competitor, or a past result.</p>
  </li>
  <li>
    <p><strong>Decomposition</strong> (“Can we split this into 3 parts?”): Break a big problem into smaller, more manageable chunks.
Solving each piece separately is often faster and less overwhelming.</p>
  </li>
  <li>
    <p><strong>Time travel</strong> (pre-mortem/post-mortem): Jump forward in time. In a pre-mortem, imagine the project has failed and ask why. In a post-mortem, imagine it has succeeded and trace back the steps that led there.</p>
  </li>
</ol>

<h1 id="anti-patterns">Anti-Patterns</h1>

<p>Some questions don’t just fail to help but actually backfire.
They actively distort the truth or shut down useful discussion.</p>

<p>Here are four common traps to avoid:</p>

<ol>
  <li>
    <p><strong>Loaded and leading questions:</strong> These questions sneak in assumptions or push the respondent toward a certain answer. For example, “Why is our onboarding so bad?” assumes it <em>is</em> bad. Instead, ask neutrally: “How does our onboarding compare to expectations?”</p>
  </li>
  <li>
    <p><strong>Double-barreled and vague scope:</strong> Two questions in one confuse people and muddy the answer. “How do we improve onboarding and reduce churn?” is really two separate discussions. Similarly, asking with no clear scope, like “What’s the best database?”, leads to endless “it depends.” Split them and define the boundaries.</p>
  </li>
  <li>
    <p><strong>Why-blame vs. how-fix framing:</strong> Asking “Who messed this up?” shifts focus to defending reputations instead of solving problems. “How can we prevent this next time?” keeps the discussion forward-looking and solution-oriented.</p>
  </li>
  <li>
    <p><strong>Scope creep:</strong> Two questions in one confuse people and muddy the answer. “How do we improve onboarding and reduce churn?” is really two separate discussions. Similarly, asking with no clear scope (like “What’s the best database?”) leads to endless “it depends.” Split them and define the boundaries.</p>
  </li>
</ol>

<h1 id="how-to-get-better">How to Get Better</h1>

<aside class="quote">
    <em>“Good questions turn meetings into decisions, incidents into root causes, and hunches into hypotheses.”</em>
</aside>

<p>For me, one of the best ways to learn the art of asking great questions is by listening to the most popular podcasters out there.
Think about it for a second, their entire job revolves around asking the right questions to the right people.</p>

<p>What I usually do is keep track of the best questions they ask (I have a <a href="https://www.notion.com/">Notion</a> template ready for this!).
I note down the exact wording they use, the follow-up questions they make, and even the flow of their thought process when “connecting the dots.”
Over time, this has helped me sharpen my own questioning skills (a lot).</p>

<p>Here are some of my favorite podcasters who have an interview-first and long-content format:</p>

<ol>
  <li><a href="https://www.youtube.com/@TheDiaryofACEO">🎙 The Diary Of A CEO</a></li>
  <li><a href="https://open.spotify.com/show/5qSUyCrk9KR69lEiXbjwXM">🎙 The Tim Ferriss Show</a></li>
  <li><a href="http://www.youtube.com/@lexfridman">🎙 The Lex Fridman Podcast</a></li>
  <li><a href="https://www.youtube.com/@joerogan">🎙 The Joe Rogan Experience</a></li>
</ol>

<h1 id="footnotes">Footnotes</h1>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:3">
      <p>Turns out my childhood instincts were correct: reading <em>was</em> important! <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>We tend to tolerate lower-quality answers from humans than from AI. In many cases, it’s less about the <em>exact</em> answer and more about the mental mechanism you used to get there. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4">
      <p>I read <a href="https://amzn.eu/d/3J43pVi">“Who”</a> by <a href="https://www.linkedin.com/in/drgeoffsmart/">Geoff Smart</a> a few moons ago. It’s based on 1,300 hours of CEO interviews about hiring. NYT bestseller. Great questions and mental models. Recommended! <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:1">
      <p>I know, whether something “makes sense” or not is wildly subjective… but let’s at least agree it should make sense to the interviewee. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>César Soto Valero</name><email>cesarsotovalero@gmail.com</email></author><category term="career" /><summary type="html"><![CDATA[Knowledge is abundant now. The ability to ask insightful questions is becoming increasingly valuable. I believe that mastering the art of questioning can unlock new opportunities and get the most of the current AI landscape. This post dives into techniques, patterns, and anti-patterns I've learned over time from top questioners. It covers real-world examples and best practices targeting both humans and AI alike.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.cesarsotovalero.net/img/posts/2025/2025-08-14/artpiece-triangle-in-metro-station_cover.jpg" /><media:content medium="image" url="https://www.cesarsotovalero.net/img/posts/2025/2025-08-14/artpiece-triangle-in-metro-station_cover.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Consistently Measure Your Consistency to Beat Talent</title><link href="https://www.cesarsotovalero.net/blog/consistently-measure-consistency-to-beat-talent.html" rel="alternate" type="text/html" title="Consistently Measure Your Consistency to Beat Talent" /><published>2025-07-31T00:00:00-07:00</published><updated>2026-04-02T05:57:52-07:00</updated><id>https://www.cesarsotovalero.net/blog/consistently-measure-consistency-to-beat-talent</id><content type="html" xml:base="https://www.cesarsotovalero.net/blog/consistently-measure-consistency-to-beat-talent.html"><![CDATA[<p>I’ve never considered myself particularly talented at anything.
Ever.
Since primary school, there were always other kids performing better than me.
They ran faster, got higher grades, looked better, and so on…</p>

<p>If we had followed the predictions, today I should be a complete social loser,<sup id="fnref:5"><a href="#fn:5" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> and my talented early peers should be doing pretty well.
But a couple of decades later, I’ve noticed that only a few of these kids are actually successful today (according to most common success metrics), and I don’t consider myself a loser, at all.</p>

<p>Why is success non-linear?</p>

<p>I came to the conclusion that I’ve just outworked my <del>now lazy</del> popular early fellows with consistent hard work.
That’s all.
Period.</p>

<aside class="quote">
    <em>“Raw talent is overrated as much as persistence is underrated.”</em>
</aside>

<p>From my experience, I can confidently say that consistency beats talent.
Every, single, time.
No matter how hard it seems, or how good they are.
The question is not “if” you can win but rather “when” you will do it.</p>

<p>The fundamental problem is that not everybody has what it takes to remain consistent over time.
Willpower fades, motivation fluctuates, and discipline alone rarely holds up when things get tough.
And let’s face it, constantly failing at anything really sucks.</p>

<p>So, how exactly can anyone achieve consistency?</p>

<p>I don’t know about you, but for me, it’s pretty straightforward: I consistently measure how consistent I am, and that’s the fuel I need to keep
going.</p>

<p>Let me explain.</p>

<h1 id="consistency-beats-talent">Consistency Beats Talent</h1>

<p>You have probably already heard that consistency is what separates top achievers from the rest of us.
This is actually not that obvious.
Indeed, many years ago, I had serious doubts about this claim.</p>

<p>I heavily overestimated raw talent and luck, while understimating my own capabilities.</p>

<p>Just three quick examples:</p>

<ul>
  <li>I believed it would be impossible to learn English on my own without a proper teacher.</li>
  <li>I thought I wasn’t smart enough to do a PhD because only “brilliant” people with special math thiking could go that path.</li>
  <li>I was convinced I couldn’t get in good physical shape because of my Latin American genes.</li>
</ul>

<p>I was wrong.</p>

<p>These were actually convenient excuses I made up to avoing the hard work.</p>

<p>Over and over, I’ve witnessed that although talent only gives a head start, it is consistency that determines how far you actually go.</p>

<p>Even the most talented guy in the room won’t win if they only show up occasionally.
But those who show up over a long period of time, even with average skills, will eventually surpass the talented but flaky competitors.<sup id="fnref:4"><a href="#fn:4" class="footnote" rel="footnote" role="doc-noteref">2</a></sup></p>

<blockquote>
  <p>“The only thing that I see that is distinctly different about me is I’m not afraid to die on a treadmill. I will not be out-worked, period. You might have more talent than me, you might be smarter than me, you might be sexier than me, you might be all of those things you got it on me in nine categories. But if we get on the treadmill together, there’s two things: You’re getting off first, or I’m going to die. It’s really that simple, right? You’re not going to out-work me. It’s such a simple, basic concept. The guy who is willing to hustle the most is going to be the guy that just gets that loose ball. The majority of people who aren’t getting the places they want or aren’t achieving the things that they want in this business is strictly based on hustle. It’s strictly based on being out-worked; it’s strictly based on missing crucial opportunities. I say all the time if you stay ready, you ain’t gotta get ready.”
― <cite><a href="https://www.goodreads.com/quotes/281801-the-only-thing-that-i-see-that-is-distinctly-different">Will Smith</a></cite></p>
</blockquote>

<p>To achieve anything meaningful in life (whether it’s getting in the top 1% at something, building a career, or improving health) consistency is the real differentiator.</p>

<p>It’s all about refusing to be outworked.
But, like anything truly valuable, becoming consistently consistent requires significant effort and dedication.</p>

<h1 id="consistency-is-hard-to-achieve">Consistency is Hard to Achieve</h1>

<p>If I told you that you’d need to write <a href="./blog/all">69 blog posts over 9 years</a> <em>before</em> getting <a href="./blog/i-am-switching-to-python-and-actually-liking-it">one of them</a> <a href="https://news.ycombinator.com/item?id=44579717">featured at the top of Hacker News</a>, would you still write blog posts with that goal in mind?<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">3</a></sup></p>

<aside class="quote">
    <em>“Most people overestimate what they can do in one year and underestimate what they can do in ten years.”</em>
    <cite><br /> ― <a href="https://www.goodreads.com/quotes/302999-most-people-overestimate-what-they-can-do-in-one-year">Bill Gates</a></cite>
</aside>

<p>If I told you that you’d need to learn research methods, write papers, and get some published <em>before</em> landing a PhD offer <a href="./blog/seven-reasons-to-go-for-a-phd-in-computer-science">that would change your life</a>, would you still try to become a PhD student?</p>

<p>Or what if you have to make 100 YouTube videos until you get your first 1000 subscribers, would you still try to become a successful YouTuber?<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">4</a></sup></p>

<p>That’s a lot of work, and most people wouldn’t even begin.</p>

<p>The fact that we have so many distractions and information flows nowadays makes truly consistent people very rare.
Admittedly, it’s very easy to get distracted by all kinds of shiny opportunities (just ask memecoin traders).
That’s why becoming <a href="https://a.co/d/5F3bVnM">indistractable</a> is such a superpower these days.</p>

<p>Modern society is making us addicted to easy gratification; when we don’t get it immediately, we tend to give up.
Most people will never have the consistency to overcome tedious repetition for that long.<sup id="fnref:3"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">5</a></sup></p>

<p>If you can learn to cultivate consistency in your work, you’ll eventually beat most people.
No matter how much talent, luck, or skill they have, just by being consistent.
But you need to put in the effort.</p>

<p>All the time you’ll spend working hard and failing will make you think you’re a loser.
You’ll feel like a nobody who sucks and isn’t good for anything and should just quit because you’ll never be good enough.
This is how I feel most of the time, btw.
But I’m aware of it.</p>

<p>Consistency is fundamentally a lonely, monotonous pursuit.
Long-term commitment often feels isolating, boring, and repetitive.
Ask any successful entrepreneur, and they’ll confirm they worked quietly, for years, before gaining traction and hit the marks.</p>

<p>So, consistency is hard.</p>

<p>How do I sustain it?</p>

<p>I trick my brain into it.</p>

<h1 id="consistency-needs-to-be-measured">Consistency Needs to Be Measured</h1>

<p>How can you make sure you’re on the rigth track?
You measure it, automatically, with dashboards.</p>

<p>I came up with a system to track how consistent I’m being over time.</p>

<p>What I do:</p>

<ol>
  <li>Set <del>easy</del> realistic goals.</li>
  <li>Track my progress (automatically).</li>
  <li>Reward myself when hitting milestones.</li>
</ol>

<h2 id="setting-realistic-goals">Setting Realistic Goals</h2>

<p>Every year, I set <a href="https://www.linkedin.com/posts/cesarsotovalero_just-before-starting-every-new-year-i-do-activity-7280462841755631617-zaoS">a few goals that I want to achieve</a>.
I try to keep them small (no more than 5), so I can actually achieve them.</p>

<p>Once these goals are set, I lower the barrier to starting by making daily targets incredibly small.
So small, in fact, that saying “no” feels unreasonable.</p>

<p>For example:</p>

<ul>
  <li>Want to read more? Set a goal of just 10 pages daily. By year’s end, you’ve read around 10 books.</li>
  <li>Aiming to write? Just commit to 200 words daily, and you’ll have enough for a full-length book in a year.</li>
</ul>

<p>The key isn’t doing a lot in one sitting, but making some little progress every single day.</p>

<h2 id="tracking-the-progress">Tracking the Progress</h2>

<p>Today, I don’t use a habit tracker, spreadsheet, or calendar (tried in the past but didn’t stick).
Instead, I rely on 1) public exposure of my work, and 2) automation systems that keep track of my progress.
This approach allows me to check the status of my projects in a data-driven manner (i.e., with analytics and data).</p>

<figure class="badge"><a href="https://amzn.eu/d/179RT62"><img src="/img/badges/show-your-work.jpg" style="width:140px;max-width:100%;" alt="badge" /></a></figure>

<p>Nobody wants to look like a fool in public, right?
When creating in public, the mere sense of public exposure provides a sense of accountability.
For me, it also raises the quality bar quite a bit because then I know that <em>other</em> people will see my work.</p>

<p>Keeping track of my progress helps me see how I’m doing at a glance and make adjustments as needed.
Also, looking at how the numbers change over time signals progress, which makes me feel motivated.<sup id="fnref:6"><a href="#fn:6" class="footnote" rel="footnote" role="doc-noteref">6</a></sup>
But I don’t want to get too obsesed and waste time on vanity metrics, this is when automation comes in.</p>

<p>Automating the tracking process is a necessary step fordward.
For example, I’ve <a href="https://github.com/cesarsotovalero/cesarsotovalero.github.io/blob/f5cea42089dad8df514aed0a305dfa98b5dab3f8/.github/workflows/compile-resume-to-pdf.yml">automated</a> the way I build <a href="https://www.cesarsotovalero.net/files/CV/cesar-resume.pdf">my resume</a> using \(\LaTeX\) and GitHub actions.
For that, I <a href="https://github.com/cesarsotovalero/cesarsotovalero.github.io/tree/master/scripts">created Python scripts</a> that scrape my number of subscribers on YouTube, the number of followers on LinkedIn, the number of citations on Google Scholar, etc.
I even keep track of <a href="./races">the timings</a> for the races I run.</p>

<p>This allows me to see my progress over time, and it also gives me a sense of accountability.
At the end of each week or month, I look at my numbers to see if I hit my targets, and where I fell short.</p>

<h2 id="celebrating-achievements">Celebrating Achievements</h2>

<p>Celebrating is important.
Every time I hit a milestone, I reward myself by doing something I enjoy but know I shouldn’t do too often (like spending money in a restaurant or buying a new gadget).</p>

<p>My PhD supervisor was very serious about celebrating successes.
Every time we submitted a new paper, we always celebrated the submission.
Yes, we celebrated not the acceptance, but <em>the submission</em>.
This is because he knew that the process of writing a paper is long and tedious.
He wanted to acknowledge the effort it took us to get to that point.</p>

<p>So, don’t wait for the big wins or the numbers to celebrate.
Celebrate every time a planned task is done.</p>

<figure class="jb_picture">
  



<img width="60%" style="border: 1px solid #808080;" src="/assets/resized/the-cult-of-done-manifesto-640x828.webp" alt="The Cult of Done Manifesto" data-srcset="/assets/resized/the-cult-of-done-manifesto-640x828.webp 640w,/assets/resized/the-cult-of-done-manifesto-768x994.webp 768w,/assets/resized/the-cult-of-done-manifesto-1024x1325.webp 1024w,/assets/resized/the-cult-of-done-manifesto-1366x1768.webp 1366w,/assets/resized/the-cult-of-done-manifesto-1600x2071.webp 1600w,/assets/resized/the-cult-of-done-manifesto-1920x2485.webp 1920w," class="blur-up lazyautosizes lazyload" />
  <figcaption class="stroke">
    &#169; The Cult of Done Manifesto. <a href="https://medium.com/@bre/the-cult-of-done-manifesto-724ca1c2ff13">Source</a>.
  </figcaption>
</figure>

<h1 id="final-thoughts">Final Thoughts</h1>

<aside class="quote">
    <em>“Happiness, for me, is nothing more than consistently achieving my goals.”</em>
</aside>

<p>Consistency is the ultimate differentiator.
But only a few people have it because we all have finite energy, time, and willpower.
If you want to beat talent, luck, or even your own past self, start measuring it.
Put numbers on it.
Because what gets measured gets improved.
Rely on systems, and keep in mind that everything is difficult before it becomes easy.
Reward yourself for the small wins, and don’t wait for the big ones.
Try to surround yourself with consistent people.
And no matter what, just don’t give up.</p>

<h1 id="footnotes">Footnotes</h1>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:5">
      <p>Or something worse, like somebody who spend his nights on the sofa watching <a href="https://en.wikipedia.org/wiki/Love_Is_Blind_(TV_series)">“Love Is Blind”</a> on Netflix. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4">
      <p>The idea that simple repetition will notj bring you very far doesn’t hold true today, AI solves the problem of lacking smart guidance and deliberate learning. Anyone can have the right guidance to learn the right things. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>BTW, I believe that writing is one of the toughest things to do consistently. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:1">
      <p>I’m on my way to becoming a successful YouTuber (i.e., &gt; 100K subs). I still have a long way to go. If you want to follow my journey, you can <a href="https://www.youtube.com/@cesarsotovalero">subscribe to my channel</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3">
      <p>I have a friend who is a professional athlete, and he told me that the hardest part of his job is to stay consistent over time. He has to train every day, even when he doesn’t feel like it, and that takes a lot of mental strength. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6">
      <p>Looking at numbers and see how they grow over time is somehow addictive, just like how video game characters <a href="./blog/building-and-leveling-up-a-computer-scientist-resume">level up over time</a>. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>César Soto Valero</name><email>cesarsotovalero@gmail.com</email></author><category term="career" /><summary type="html"><![CDATA[I've never considered myself particularly talented at anything. Ever. But I have consistently outworked my peers, and I believe that anyone can outperform their toughest competitors by just being consistent. So, consistency beats talent every time. The secret? Measure your consistency, not just your results. In this post, I share why tracking your consistency is the real key to outperforming even the most talented people.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.cesarsotovalero.net/img/posts/2025/2025-07-31/the-iron-child_cover.jpg" /><media:content medium="image" url="https://www.cesarsotovalero.net/img/posts/2025/2025-07-31/the-iron-child_cover.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">I’m Switching to Python and Actually Liking It</title><link href="https://www.cesarsotovalero.net/blog/i-am-switching-to-python-and-actually-liking-it.html" rel="alternate" type="text/html" title="I’m Switching to Python and Actually Liking It" /><published>2025-07-15T00:00:00-07:00</published><updated>2026-04-02T05:57:52-07:00</updated><id>https://www.cesarsotovalero.net/blog/i-am-switching-to-python-and-actually-liking-it</id><content type="html" xml:base="https://www.cesarsotovalero.net/blog/i-am-switching-to-python-and-actually-liking-it.html"><![CDATA[<aside class="youtube">
        <a href="https://www.youtube.com/watch?v=SIVhrtQzknQ"><div class="box">
        <img src="https://i.ytimg.com/vi/SIVhrtQzknQ/mqdefault.jpg" alt="YouTube video #SIVhrtQzknQ" />
        <div class="play">
          <img src="/img/icons/youtube-play20px.svg" alt="Play Video" class="youtube-logo" />
        </div>
        </div></a>
        <div>César Soto Valero-From Ideas to APIs_ Delivering Fast with Modern Python; 9 January 2026.</div></aside>

<p>I started to code more in <a href="https://www.python.org/">Python</a> around 6 months ago.
Why?
Because of AI, obviously.
It’s clear (to me) that big <del>money</del> opportunities are all over AI these days.
And guess what’s the <em>de facto</em> programming language for AI?
Yep, that sneaky one.</p>

<p>I had used Python before, but only for small scripts.
For example, <a href="https://github.com/cesarsotovalero/cesarsotovalero.github.io/blob/1fb2efe0577719a72fdf7d5bdf2a8d4d51ee58c5/scripts/fetch_all_youtube_videos.py">this script</a> scrapes metadata from all videos on <a href="https://www.youtube.com/channel/UCR4rI98w6-MqYoCS6jR9LGg">my YouTube channel</a>.
The metadata is dumped as <a href="https://github.com/cesarsotovalero/cesarsotovalero.github.io/blob/1fb2efe0577719a72fdf7d5bdf2a8d4d51ee58c5/_data/youtube-videos.json">a JSON file</a> that I use to nicely display statistics of the videos <a href="https://www.cesarsotovalero.net/youtube">on this static page</a>.
As you can <a href="https://github.com/cesarsotovalero/cesarsotovalero.github.io/blob/1fb2efe0577719a72fdf7d5bdf2a8d4d51ee58c5/.github/workflows/update-youtube-videos.yml">see here</a>, this little script runs in solo mode every Monday via GitHub Actions.
Doing this kind of thing in Python is just way more convenient than, say, using Batch.
Not only because the syntax is more human-friendly, but also because the Python interpreter is natively integrated in all Unix distros.
Isn’t that cool?</p>

<p>So yeah, Python is powerful, and it couples very well with the now ubiquitous <a href="https://code.visualstudio.com/">VSCode</a> editor.
But I didn’t treat it seriously until recently,<sup id="fnref:3"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> it was just after I wanted to build AI applications (RAG, Agents, GenAI tools, etc.) for the “real world” that I realized that whether you like it or not, Python is the language of choice for those matters.</p>

<p>So I decided to give it a serious try, and to my great surprise, I’ve found that Python, and everything around it, has really improved a lot over the last decades.</p>

<p>Here’re just three examples:</p>

<ol>
  <li>Python has created a very complete ecosystem of libraries and tools for processing and analyzing data.<sup id="fnref:4"><a href="#fn:4" class="footnote" rel="footnote" role="doc-noteref">2</a></sup></li>
  <li>Python has gotten faster with optimized static compilers like <a href="https://cython.org/">Cython</a>.</li>
  <li>Python has done a good job of hiding its legacy ugliness (such as <code class="language-plaintext highlighter-rouge">__init__</code>, <code class="language-plaintext highlighter-rouge">__new__</code>, and similar aberrations), sweetening its syntax to accommodate developers <del>with good taste</del>.</li>
</ol>

<p>Thanks to this and many other things, I’m now feeling a particular joy for the language.</p>

<p>However, during this time, I’ve found that there’s still a big gap between using Python for “production-ready”<sup id="fnref:7"><a href="#fn:7" class="footnote" rel="footnote" role="doc-noteref">3</a></sup> apps vs. the usual Jupyter notebook or script-based workflow.</p>

<p>So in this post, I share the tools, libraries, configs, and other integrations that bring me joy, and that I now use for building my Python applications.</p>

<p>⚠️ This post is highly biased toward the tools I personally use today, and if you think I’m missing some gem, please let me/us know (preferably in the comment section below).</p>

<p><strong>NOTE:</strong> Somehow this article got <a href="https://news.ycombinator.com/item?id=44579717">680+ comments</a> on Hacker News (just another proof that you never really know).</p>

<h1 id="project-structure">Project Structure</h1>

<p>I prefer to use a <a href="https://en.wikipedia.org/wiki/Monorepo">monorepo</a> structure (backend and frontend) for my Python projects.<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">4</a></sup></p>

<p>Why?</p>

<ol>
  <li>Because of my bad memory: I don’t like code parts scattered across multiple repositories (it’s definitely not search-friendly).</li>
  <li>Because having multiple repos is mostly unnecessary: I’m just one guy, and I believe that if a project grows to the point that it needs to be split into multiple repositories, then it’s a sign of over-engineering.</li>
  <li>Because I’m lazy: I like to keep things as simple as possible, compile, test, containerize, and deploy from a single location.<sup id="fnref:5"><a href="#fn:5" class="footnote" rel="footnote" role="doc-noteref">5</a></sup></li>
</ol>

<p>I would like to have a tool that generates the project structure for me, but I haven’t found one that fits me yet.
In the past, I used <a href="https://cookiecutter-data-science.drivendata.org/">CCDS</a>, a project initialization tool mostly for Data Science projects.
It’s very good, but it’s not targeting full-stack developers as its core users.</p>

<p>Here’s the typical structure of a project with a frontend-backend architecture (I’ll go through each subpart later in this post):</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">project/
│
├── .github/ <span class="c"># GitHub Actions workflows for CI/CD pipelines</span>
│ ├── workflows/ <span class="c"># Directory containing YAML files for automated workflows</span>
│ └── dependabot.yml <span class="c"># Configuration for Dependabot to manage dependencies</span>
│
├── .vscode/ <span class="c"># VSCode configuration for the project</span>
│ ├── launch.json <span class="c"># Debugging configurations for VSCode</span>
│ └── settings.json <span class="c"># Project-specific settings for VSCode</span>
│
├── docs/ <span class="c"># Website and docs (a static SPA with MkDocs)</span>
│
├── project-api/ <span class="c"># Backend API for handling business logic and heavy processing</span>
│ ├── data/ <span class="c"># Directory for storing datasets or other static files</span>
│ ├── notebooks/ <span class="c"># Jupyter notebooks for quick (and dirty) experimentation and prototyping</span>
│ ├── tools/ <span class="c"># Utility scripts and tools for development or deployment</span>
│ ├── src/ <span class="c"># Source code for the backend application</span>
│ │ ├── app/ <span class="c"># Main application code</span>
│ │ └── tests/ <span class="c"># Unit tests for the backend</span>
│ │
│ ├── .dockerignore <span class="c"># Specifies files to exclude from Docker builds</span>
│ ├── .python-version <span class="c"># Python version specification for pyenv</span>
│ ├── Dockerfile <span class="c"># Docker configuration for containerizing the backend</span>
│ ├── Makefile <span class="c"># Automation tasks for building, testing, and deploying</span>
│ ├── pyproject.toml <span class="c"># Python project configuration file</span>
│ ├── README.md <span class="c"># Documentation for the backend API</span>
│ └── uv.lock <span class="c"># Lock file for dependencies managed by UV</span>
│
├── project-ui/ <span class="c"># Frontend UI for the project (Next.js, React, etc.)</span>
│
├── .gitignore <span class="c"># Global Git ignore file for the repository</span>
├── .pre-commit-config.yaml <span class="c"># Configuration for pre-commit hooks</span>
├── CONTRIBUTING.md <span class="c"># Guidelines for contributing to the project</span>
├── docker-compose.yml <span class="c"># Docker Compose configuration for multi-container setups</span>
├── LICENSE <span class="c"># License information for the project (I always choose MIT)</span>
├── Makefile <span class="c"># Automation tasks for building, testing, and deploying</span>
└── README.md <span class="c"># Main documentation for the project (main features, installation, and usage)</span></code></pre></figure>

<p>My <code class="language-plaintext highlighter-rouge">project</code> is the root directory and the name of my GitHub repo.
I like short names for projects, ideally less than 10 characters long. No <code class="language-plaintext highlighter-rouge">snake_case</code>; separation with hyphens is OK to me.
Note that the project should be self-contained, meaning it includes documentation, build/deployment infrastructure, and any other necessary files to run it standalone.</p>

<p>It’s important not to do any heavy data processing steps in the <code class="language-plaintext highlighter-rouge">project-ui</code>, as I opted to separate frontend logic from backend responsibilities.
Instead, I choose to make HTTP requests to the <code class="language-plaintext highlighter-rouge">project-api</code> server that contains the Python code.
This way, we keep the browser application light while delegating the heavy lifting and business logic to the server.</p>

<p>There’s an <code class="language-plaintext highlighter-rouge">__init__.py</code> file in <code class="language-plaintext highlighter-rouge">project-api/src/app</code> to indicate that <code class="language-plaintext highlighter-rouge">app</code> is a Python module (it can be imported from other modules).</p>

<h1 id="python-toolbox">Python Toolbox</h1>

<h2 id="uv">uv</h2>

<p>I use <a href="https://github.com/astral-sh/uv">uv</a> as my Python package manager and build tool.
It’s all I need to install and manage dependencies.</p>

<p>Here are the core commands to set it up:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
</pre></td><td class="code"><pre><span class="c"># Install uv globally if not already installed</span>

curl <span class="nt">-sSfL</span> https://astral.sh/install.sh | sh

<span class="c"># Initialize a new project (adds .gitignore, .python-version, pyproject.toml, etc.)</span>

uv init project-api

<span class="c"># Add some dependencies into the project and update pyproject.toml</span>

uv add <span class="nt">--dev</span> pytest ruff pre-commit mkdocs gitleaks fastapi pydantic

<span class="c"># Update the lock file with the latest versions of the dependencies</span>

<span class="c"># (this will also create a .venv in your project directory if it doesn't exist)</span>

uv <span class="nb">sync</span>

<span class="c"># (Opt‑in) explicitly create or recreate a venv in a custom path:</span>

uv venv create .venv

<span class="c"># Activate the .venv yourself</span>

<span class="c"># ──────────────────────────────────────────────</span>

<span class="c"># On macOS/Linux:</span>

<span class="nb">source</span> .venv/bin/activate

<span class="c"># On Windows (PowerShell):</span>

.<span class="se">\.</span>venv<span class="se">\S</span>cripts<span class="se">\A</span>ctivate.ps1

<span class="c"># On Windows (cmd.exe):</span>

.<span class="se">\.</span>venv<span class="se">\S</span>cripts<span class="se">\a</span>ctivate.bat
</pre></td></tr></tbody></table></code></pre></figure>

<p>Note that the most important file for <code class="language-plaintext highlighter-rouge">uv</code> is <code class="language-plaintext highlighter-rouge">pyproject.toml</code>.<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">6</a></sup>
This file <a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/">contains</a> metadata and the list of dependencies required to build and run the project.</p>

<h2 id="ruff">ruff</h2>

<p>I really like <a href="https://github.com/astral-sh/ruff">ruff</a>.
It’s a super-fast Python linter and code formatter, designed to help lazy developers like me keep our codebases clean and maintainable.
Ruff combines <code class="language-plaintext highlighter-rouge">isort</code>, <code class="language-plaintext highlighter-rouge">flake8</code>, <code class="language-plaintext highlighter-rouge">autoflake</code>, and similar tools into a single command-line interface:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="code"><pre><span class="c"># Lint all files in `/path/to/code` (and any subdirectories)</span>

ruff check path/to/code/

<span class="c"># Format all files in `/path/to/code` (and any subdirectories)</span>

ruff format path/to/code/
</pre></td></tr></tbody></table></code></pre></figure>

<p>Ruff supports the <a href="https://pep8.org/">PEP 8</a> style guide out of the box.</p>

<h2 id="ty">ty</h2>

<p><a href="https://github.com/astral-sh/ty">ty</a> is a type checker for Python.
It is a great combo for <a href="https://docs.python.org/3/library/typing">typing</a>, the popular Python module for adding static typing.
I think typing really helps me catch type errors early in the development process. I actually don’t care about having to write more code, in fact, I prefer it if it improves code quality and reduces the likelihood of runtime errors.</p>

<p><strong>NOTE:</strong> At the time of writing, <code class="language-plaintext highlighter-rouge">ty</code> is still in early development by Astral (the same company behind <code class="language-plaintext highlighter-rouge">uv</code> and <code class="language-plaintext highlighter-rouge">ruff</code>), but I’ve been using it and haven’t found any noticeable flaws so far.</p>

<h2 id="pytest">pytest</h2>

<p><a href="https://docs.pytest.org/en/stable/">pytest</a> is the most popular testing library for Python.
Writing simple and scalable test cases with it is just super easy.
It supports fixtures, parameterized tests, and has a rich ecosystem of plugins.
Just create a file named <code class="language-plaintext highlighter-rouge">test_&lt;unit_or_module&gt;.py</code> in <code class="language-plaintext highlighter-rouge">project-api/src/app/tests/</code>, and run:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">uv run pytest</code></pre></figure>

<p>That’s it!</p>

<h2 id="pydantic">Pydantic</h2>

<p><a href="https://pydantic-docs.helpmanual.io/">Pydantic</a> is a data validation and settings management library for Python.
It helps manage all kinds of configuration settings, such as API keys, database connection details, or model parameters (hardcoding these values is a very bad practice, btw).</p>

<p>In particular, <a href="https://docs.pydantic.dev/latest/concepts/pydantic_settings/">Pydantic Settings</a> allows you to define application configurations using Pydantic models.
It can automatically load settings from environment variables or special <code class="language-plaintext highlighter-rouge">.env</code> files, validate their types, and make them easily accessible in your code.</p>

<p>Here’s an illustrative example:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre><span class="kn">from</span> <span class="n">pydantic</span> <span class="kn">import</span> <span class="n">BaseSettings</span>

<span class="k">class</span> <span class="nc">Settings</span><span class="p">(</span><span class="n">BaseSettings</span><span class="p">):</span>
<span class="n">api_key</span><span class="p">:</span> <span class="nb">str</span>
<span class="n">db_url</span><span class="p">:</span> <span class="nb">str</span>

    <span class="k">class</span> <span class="nc">Config</span><span class="p">:</span>
        <span class="n">env_file</span> <span class="o">=</span> <span class="sh">"</span><span class="s">.env</span><span class="sh">"</span>

<span class="n">settings</span> <span class="o">=</span> <span class="nc">Settings</span><span class="p">()</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>Now, when you run this code, Pydantic will automatically load the values of <code class="language-plaintext highlighter-rouge">api_key</code> and <code class="language-plaintext highlighter-rouge">db_url</code> from the <code class="language-plaintext highlighter-rouge">.env</code> file or environment variables.
These values will be accessible and validated according to the types defined in the <code class="language-plaintext highlighter-rouge">Settings</code> model.
Just great!</p>

<h2 id="mkdocs">MkDocs</h2>

<p>I use <a href="https://www.mkdocs.org/">MkDocs</a> for documentation and static generation of the website for the project.<sup id="fnref:6"><a href="#fn:6" class="footnote" rel="footnote" role="doc-noteref">7</a></sup>
I’m not a designer, so I prefer to just copy an aesthetically pleasing design from another similar open-source project and make some simple modifications to the CSS (like changing fonts and colors).</p>

<h2 id="fastapi">FastAPI</h2>

<p>I use <a href="https://fastapi.tiangolo.com/">FastAPI</a> for building APIs.
It has been a game changer for me; it allows for easy creation of RESTful APIs with automatic validation, serialization, and documentation.
FastAPI is built on top of Starlette and Pydantic, which means it provides excellent performance and type safety.
It’s fast, easy to use, and integrates seamlessly with Pydantic for data validation.</p>

<h2 id="dataclasses">Dataclasses</h2>

<p><a href="https://docs.python.org/3/library/dataclasses">Dataclasses</a> is not a library but a Python feature that provides a way to define classes that are primarily used to store data.
They offer a simple syntax for creating classes that automatically generate special methods like <code class="language-plaintext highlighter-rouge">__init__()</code>, <code class="language-plaintext highlighter-rouge">__repr__()</code>, and <code class="language-plaintext highlighter-rouge">__eq__()</code>.</p>

<p>This greatly reduces boilerplate when creating data containers.</p>

<p>Here’s an example:</p>

<figure class="highlight"><pre><code class="language-python" data-lang="python"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre><span class="kn">from</span> <span class="n">dataclasses</span> <span class="kn">import</span> <span class="n">dataclass</span>

<span class="nd">@dataclass</span>
<span class="k">class</span> <span class="nc">Point</span><span class="p">:</span>
<span class="n">x</span><span class="p">:</span> <span class="nb">int</span>
<span class="n">y</span><span class="p">:</span> <span class="nb">int</span>

<span class="n">p</span> <span class="o">=</span> <span class="nc">Point</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="nf">print</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="c1"># Output: Point(x=1, y=2)</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>So goodbye boilerplate and cryptic code!</p>

<h1 id="version-control">Version Control</h1>

<h2 id="github-actions">GitHub Actions</h2>

<p>I’m a fanboy of <a href="https://github.com/features/actions">GitHub Actions</a>, especially for CI across different OSs.
I recommend using it for both API and UI pipelines.</p>

<p>A typical workflow for <code class="language-plaintext highlighter-rouge">project-api</code> looks like this:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="code"><pre><span class="na">name</span><span class="pi">:</span> <span class="s">CI-API</span>

<span class="na">on</span><span class="pi">:</span>
<span class="na">push</span><span class="pi">:</span>
<span class="na">branches</span><span class="pi">:</span> <span class="s">- main</span>
<span class="na">pull_request</span><span class="pi">:</span>
<span class="na">branches</span><span class="pi">:</span> <span class="s">- main</span>

<span class="na">jobs</span><span class="pi">:</span>
<span class="na">build-and-test</span><span class="pi">:</span>
<span class="na">runs-on</span><span class="pi">:</span> <span class="s">ubuntu-latest</span>
<span class="na">steps: - name</span><span class="pi">:</span> <span class="s">Checkout code</span>
<span class="na">uses: actions/checkout@v3 - name</span><span class="pi">:</span> <span class="s">Build Docker image</span>
<span class="na">run: docker build -t project-api:ci ./project-api - name</span><span class="pi">:</span> <span class="s">Run tests</span>
<span class="na">run</span><span class="pi">:</span> <span class="s">docker run --rm project-api:ci pytest</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>Note that this workflow uses Docker to run the tests in an isolated environment.<sup id="fnref:8"><a href="#fn:8" class="footnote" rel="footnote" role="doc-noteref">8</a></sup>
You can change the OS by setting the <code class="language-plaintext highlighter-rouge">runs-on</code> parameter to <code class="language-plaintext highlighter-rouge">windows-latest</code> or <code class="language-plaintext highlighter-rouge">macos-latest</code>.</p>

<h2 id="dependabot">Dependabot</h2>

<p>Handling dependencies is a pain, but <a href="https://dependabot.com/">Dependabot</a> makes it easier.
It automatically checks for outdated dependencies and creates pull requests to update them.</p>

<p>Here’s a sample configuration for Dependabot in the <code class="language-plaintext highlighter-rouge">.github/dependabot.yml</code> file:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre><span class="na">version</span><span class="pi">:</span> <span class="m">2</span>
<span class="na">updates</span><span class="pi">:</span>

<span class="pi">-</span> <span class="na">package-ecosystem</span><span class="pi">:</span> <span class="s2">"</span><span class="s">uv"</span>
  <span class="na">directory</span><span class="pi">:</span> <span class="s2">"</span><span class="s">/"</span>
  <span class="na">schedule</span><span class="pi">:</span>
  <span class="na">interval</span><span class="pi">:</span> <span class="s2">"</span><span class="s">weekly"</span>
  
</pre></td></tr></tbody></table></code></pre></figure>

<h2 id="gitleaks">Gitleaks</h2>

<p>If there’s something that could hurt our reputation, it’s committing sensitive information, like API keys or passwords, directly to a repository.
Fortunately, <a href="https://github.com/gitleaks/gitleaks">Gitleaks</a> helps prevent this from happening.
There’s just no reason not to use it.</p>

<h2 id="pre-commit-hooks">Pre-commit Hooks</h2>

<p>I use <a href="https://pre-commit.com/">pre-commit</a> to run checks and format code before committing.
It helps ensure that the code is always in a good state and follows the project’s coding standards.
For example, I use it to run <a href="https://github.com/astral-sh/ruff-pre-commit">ruff-pre-commit</a> and <code class="language-plaintext highlighter-rouge">gitleaks</code> before committing my code.</p>

<p>Here’s a sample <code class="language-plaintext highlighter-rouge">.pre-commit-config.yaml</code> file that I use:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="code"><pre><span class="na">repos</span><span class="pi">:</span>

<span class="pi">-</span> <span class="na">repo</span><span class="pi">:</span> <span class="s">&lt;https://github.com/astral-sh/ruff-pre-commit&gt;</span>
  <span class="na">rev</span><span class="pi">:</span> <span class="s">v0.12.3</span> <span class="c1"># Ruff version.</span>
  <span class="na">hooks</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">id</span><span class="pi">:</span> <span class="s">ruff-check</span> <span class="c1"># Run the linter.</span>
    <span class="na">args</span><span class="pi">:</span> <span class="pi">[</span> <span class="nv">--fix</span> <span class="pi">]</span>
  <span class="pi">-</span> <span class="na">id</span><span class="pi">:</span> <span class="s">ruff-format</span> <span class="c1"># Run the formatter.</span>
<span class="pi">-</span> <span class="na">repo</span><span class="pi">:</span> <span class="s">&lt;https://github.com/gitleaks/gitleaks&gt;</span>
  <span class="na">rev</span><span class="pi">:</span> <span class="s">v8.27.2</span>
  <span class="na">hooks</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">id</span><span class="pi">:</span> <span class="s">gitleaks</span>
    
</pre></td></tr></tbody></table></code></pre></figure>

<h1 id="infrastructure-management">Infrastructure Management</h1>

<h2 id="make">Make</h2>

<p><a href="https://www.gnu.org/software/make/">Make</a> is a Swiss Army knife, a classic utility for automating tasks.
I use it to create simple shortcuts for common development commands.
Instead of remembering and typing out long CLI incantations to run tests, build Docker images, or start services, I define these tasks in a <code class="language-plaintext highlighter-rouge">Makefile</code>.
Then I just run commands like <code class="language-plaintext highlighter-rouge">make test</code> or <code class="language-plaintext highlighter-rouge">make infrastructure-up</code>.</p>

<p>As you might have noticed, there is a <code class="language-plaintext highlighter-rouge">Makefile</code> in both the <code class="language-plaintext highlighter-rouge">project-api</code> and the global <code class="language-plaintext highlighter-rouge">project</code> directories:</p>

<ol>
  <li><code class="language-plaintext highlighter-rouge">project/project-api/Makefile</code>: For linting, testing, and running the API.</li>
  <li><code class="language-plaintext highlighter-rouge">project/Makefile</code>: For building and running the infrastructure (via <code class="language-plaintext highlighter-rouge">docker-compose</code>).</li>
</ol>

<p>Here’s an extremely simple example of the <code class="language-plaintext highlighter-rouge">project-api</code> Makefile:</p>

<figure class="highlight"><pre><code class="language-plaintext" data-lang="plaintext"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre>DIR := . # project/project-api/Makefile

test:
uv run pytest

format-fix:
uv run ruff format $(DIR)
uv run ruff check --select I --fix

lint-fix:
uv run ruff check --fix
</pre></td></tr></tbody></table></code></pre></figure>

<p>Now, if I want to run the tests, I just run <code class="language-plaintext highlighter-rouge">make test</code>, and it executes <code class="language-plaintext highlighter-rouge">uv run pytest</code> in the current directory.</p>

<p>For the global project, I use the following <code class="language-plaintext highlighter-rouge">Makefile</code>:</p>

<figure class="highlight"><pre><code class="language-plaintext" data-lang="plaintext"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre>infrastructure-build:
docker compose build

infrastructure-up:
docker compose up --build -d

infrastructure-stop:
docker compose stop
</pre></td></tr></tbody></table></code></pre></figure>

<p><code class="language-plaintext highlighter-rouge">make</code> is a powerful tool that can help you automate almost anything in your development workflow.
Although the examples above are very simple, just imagine how you can add more complex tasks as needed.</p>

<h2 id="docker">Docker</h2>

<p><a href="https://www.docker.com/">Docker</a> is a tool that allows you to package your application and its dependencies into a container,including everything needed to run: dependencies, system tools, code, and runtime OS.
When working locally, I use <a href="https://docs.docker.com/compose/">Docker Compose</a> to connect all Docker images into the same network.
Like Docker for dependencies, Docker Compose allows encapsulating the whole application stack and separating it from the rest of your local environment.</p>

<p>To fully grasp this concept, let’s take a look at a simple <code class="language-plaintext highlighter-rouge">docker-compose.yml</code> file:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="code"><pre><span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3.8'</span>
<span class="na">services</span><span class="pi">:</span>
<span class="na">project-api</span><span class="pi">:</span>
<span class="na">build</span><span class="pi">:</span>
<span class="na">context</span><span class="pi">:</span> <span class="s">./project-api</span>
<span class="na">dockerfile</span><span class="pi">:</span> <span class="s">Dockerfile</span>
<span class="na">ports</span><span class="pi">:</span> <span class="s">- "8000:8000"</span>
<span class="na">volumes</span><span class="pi">:</span> <span class="s">- ./project-api:/app</span>
<span class="na">environment</span><span class="pi">:</span> <span class="s">- ENV_VAR=value</span>
<span class="na">networks</span><span class="pi">:</span> <span class="s">- project-network</span>

<span class="na">project-ui</span><span class="pi">:</span>
<span class="na">build</span><span class="pi">:</span>
<span class="na">context</span><span class="pi">:</span> <span class="s">./project-ui</span>
<span class="na">dockerfile</span><span class="pi">:</span> <span class="s">Dockerfile</span>
<span class="na">ports</span><span class="pi">:</span> <span class="s">- "3000:3000"</span>
<span class="na">networks</span><span class="pi">:</span> <span class="s">- project-network</span>

<span class="na">networks</span><span class="pi">:</span>
<span class="na">project-network</span><span class="pi">:</span>
<span class="na">driver</span><span class="pi">:</span> <span class="s">bridge</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>In this file, we define two services: <code class="language-plaintext highlighter-rouge">project-api</code> and <code class="language-plaintext highlighter-rouge">project-ui</code>.
Each service has its own build context (<code class="language-plaintext highlighter-rouge">Dockerfile</code>), ports, volumes, and environment variables.</p>

<p>Here’s a sample <code class="language-plaintext highlighter-rouge">Dockerfile</code> for the <code class="language-plaintext highlighter-rouge">project-api</code> service:</p>

<figure class="highlight"><pre><code class="language-dockerfile" data-lang="dockerfile"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
</pre></td><td class="code"><pre><span class="k">FROM</span><span class="s"> python:3.11-slim</span>

<span class="c"># Install system dependencies</span>

<span class="k">COPY</span><span class="s"> --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/</span>

<span class="k">WORKDIR</span><span class="s"> /app</span>

<span class="k">COPY</span><span class="s"> uv.lock pyproject.toml README.md ./</span>
<span class="k">RUN </span>uv <span class="nb">sync</span> <span class="nt">--frozen</span> <span class="nt">--no-cache</span>

<span class="c"># Bring in the actual application code</span>

<span class="k">COPY</span><span class="s"> src/app app/</span>
<span class="k">COPY</span><span class="s"> tools tools/</span>

<span class="c"># Define a command to run the application</span>

<span class="k">CMD</span><span class="s"> ["/app/.venv/bin/fastapi", "run", "project/infrastructure/api.py", "--port", "8000", "--host", "0.0.0.0"]</span>
</pre></td></tr></tbody></table></code></pre></figure>

<p>As you can see, the Dockerfile starts from a Python base image, installs dependencies, copies the project files, and defines the command to run the FastAPI application.</p>

<p>This way, you can run the entire application stack with a single command:</p>

<figure class="highlight"><pre><code class="language-bash" data-lang="bash">docker compose up <span class="nt">--build</span> <span class="nt">-d</span></code></pre></figure>

<h1 id="footnotes">Footnotes</h1>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:3">
      <p>If you know me, you know I used to be mostly a Java/JavaScript/R kind of guy. <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4">
      <p>For example, today <a href="https://jupyter.org/">Jupyter</a> is bundled by almost every major cloud provider as the de facto tool for interactive data science and scientific computing. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:7">
      <p>“Production-ready,” for me, means I can deploy the app to the cloud as-is, without needing to make a lot of infrastructure changes. <a href="#fnref:7" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:1">
      <p>Don’t get me wrong, I understand there are cases where a multi-repo structure is necessary, like when multiple teams work on different parts of the project or when dependencies needs to be shared across projects. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:5">
      <p>I believe that aviding premature decomposition is a good idea. If a codebase is less than, say, 1/2 million LoC, then setting a network layer (like API calls) over it only would make maintenance a pain for <del>non-Amazon</del> rational developers. <a href="#fnref:5" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>A <code class="language-plaintext highlighter-rouge">pyproject.toml</code> file is similar to <code class="language-plaintext highlighter-rouge">package.json</code> in Node.js or <code class="language-plaintext highlighter-rouge">pom.xml</code> in Java. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:6">
      <p>By the way, I think every single project on GitHub should have its own website (it’s extremely easy via <a href="https://pages.github.com/">GitHub Pages</a>), so no excuses, sorry. <a href="#fnref:6" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:8">
      <p>Using Docker for CI ensures parity with production environments, but it might add some cold-start overhead. You know… compromises, life is full of them. <a href="#fnref:8" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>César Soto Valero</name><email>cesarsotovalero@gmail.com</email></author><category term="tools" /><summary type="html"><![CDATA[I've started writing more Python code lately (because of... AI, you know). In this post, I share the tools, libraries, configs, and other integrations I use for building production-grade Python applications following a frontend-backend architecture.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.cesarsotovalero.net/img/posts/2025/2025-07-15/wall-stones_cover.JPG" /><media:content medium="image" url="https://www.cesarsotovalero.net/img/posts/2025/2025-07-15/wall-stones_cover.JPG" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Evaluation Metrics for Real-Time Financial Fraud Detection ML Models</title><link href="https://www.cesarsotovalero.net/blog/evaluation-metrics-for-real-time-financial-fraud-detection-ml-models.html" rel="alternate" type="text/html" title="Evaluation Metrics for Real-Time Financial Fraud Detection ML Models" /><published>2025-05-08T00:00:00-07:00</published><updated>2026-04-02T05:57:52-07:00</updated><id>https://www.cesarsotovalero.net/blog/evaluation-metrics-for-real-time-financial-fraud-detection-ml-models</id><content type="html" xml:base="https://www.cesarsotovalero.net/blog/evaluation-metrics-for-real-time-financial-fraud-detection-ml-models.html"><![CDATA[<p>After training a machine learning model for <a href="./blog/real-time-financial-fraud-detection">real-time financial fraud detection</a>, the next step is to evaluate its performance.
Fraud detection models face unique challenges during evaluation.
Two examples are (1) class imbalance and (2) the different costs of false positives vs. false negatives.
Class imbalance means that the model doesn’t have enough fraudulent transactions to learn from, as they are rare compared to legitimate ones.
False positives, which flag legitimate transactions as fraudulent, can lead to customer dissatisfaction, while false negatives, where fraudulent transactions go undetected, can result in significant financial losses.
In this post, I cover the most common metrics and considerations for evaluating fraud detection models while keeping these unique challenges in mind.</p>

<h1 id="confusion-matrix">Confusion Matrix</h1>

<p>Most financial fraud detection systems generate a binary output: a prediction indicating whether a transaction (txt) is fraudulent (1) or genuine (0).</p>

<p>By leveraging this universal approach to binary classification, we can establish standard evaluation methodologies to assess the performance of this type of models.</p>

<p>The confusion matrix is a widely used tool for summarizing and visualizing the performance of a classification model in a tabular format.
It provides a clear breakdown of predictions vs. actual outcomes.</p>

<p>In a confusion matrix:</p>

<ul>
  <li>The <em>x</em>-axis represents the ground-truth labels (actual outcomes).</li>
  <li>The <em>y</em>-axis represents the predictions made by the classification model.</li>
</ul>

<p>Both axes are divided into two categories: positive (fraudulent txt) and negative (genuine txt).
The positive class corresponds to the minority class (fraud), while the negative class corresponds to the majority class (genuine).</p>

<p><img src="/img/posts/2025/2025-05-08/confusion-matrix.svg" alt="Confusion Matrix" /></p>

<p>This representation allows us to visualize the model’s performance in terms of true positives, true negatives, false positives, and false negatives.</p>

<ul>
  <li><strong>True Positives (TP):</strong> The number of fraudulent transactions correctly identified as fraud.</li>
  <li><strong>True Negatives (TN):</strong> The number of genuine transactions correctly identified as genuine.</li>
  <li><strong>False Positives (FP):</strong> The number of genuine transactions incorrectly flagged as fraudulent.</li>
  <li><strong>False Negatives (FN):</strong> The number of fraudulent transactions missed by the system.</li>
</ul>

<p>By analyzing these metrics, we gain a comprehensive view of the model’s strengths and weaknesses, enabling informed decisions for further optimization.</p>

<h1 id="precision">Precision</h1>

<p><a href="https://en.wikipedia.org/wiki/Precision_and_recall">Precision</a> is the fraction of transactions that were actually fraud among all transactions the model flagged as fraud (predicted positive).</p>

<p>For example, if a model flagged 100 transactions as fraud, and 80 of those were indeed fraudulent, then:</p>

\[\text{Precision} = \frac{TP}{TP + FP} = \frac{80}{80 + 20} = 0.8\]

<p>High Precision means few false positives, so it is crucial for operational efficiency.</p>

<p>Low Precision means investigators waste time on many false alarms, and customers suffer unnecessary transaction declines.</p>

<p>Many top systems aim for very high Precision (e.g., 0.9+) at low fraud rates, but there’s a trade-off with Recall.
For example, if we lower the threshold to catch more fraud, Precision may drop.
Therefore, Precision is often reported at a certain operating point or as an average if multiple thresholds are considered.</p>

<p>An example interpretation: “Of the transactions our system blocked, 95% were indeed fraudulent”, that’s a Precision of 95%.</p>

<h1 id="recall">Recall</h1>

<p><a href="https://en.wikipedia.org/wiki/Precision_and_recall">Recall</a> is the fraction of actual fraud cases that the model correctly predicts as fraud (true positives) out of all actual fraud cases.</p>

<p>For example, if there were 100 actual fraud cases and the model caught 80 of them, then:</p>

\[\text{Recall} = \frac{TP}{TP + FN} = \frac{80}{80 + 20} = 0.8\]

<p>A Recall of 0.80 means 80% of fraud instances were detected (20% missed).</p>

<p>High Recall means few false negatives, which is critical in fraud detection because missing a fraudulent transaction can lead to financial losses.</p>

<p>Low Recall means many frauds slip through and cause losses.</p>

<p>We can usually increase Recall by lowering the detection threshold at the cost of Precision.
In practice, businesses may set a Recall target like “catch at least 70% of fraud” and then maximize Precision under that constraint.</p>

<h1 id="f1-score">F1-Score</h1>

<p><a href="https://en.wikipedia.org/wiki/F-score">F1-Score</a>, or F1 for short, is the harmonic mean of Precision and Recall.
It gives a single-figure balance of both metrics, which is useful when we want a combined score for model selection or when class distribution is skewed.</p>

<p>For example, if Precision is 0.8 and Recall is 0.6, then:</p>

\[F1 = \frac{2 \times Precision \times Recall}{Precision + Recall} \approx 0.686\]

<p>High F1 means both Precision and Recall are reasonably high.
Low F1 means either Precision or Recall is low, which is undesirable in fraud detection.</p>

<p>Overall, F1 is a good metric to assess a fraud detection model.
It is also a popular metric in Kaggle competitions and papers to compare models, ensuring they are not just optimizing one at the expense of the other.</p>

<h1 id="fpr">FPR</h1>

<p>False Positive Rate (FPR) is is the share of legitimate transactions that get incorrectly flagged as fraud.</p>

<p>For example, if there were 100 legitimate transactions and the model flagged 5 of them as fraud, then:</p>

\[\text{FPR} = \frac{FP}{TN + FP} = \frac{5}{95 + 5} = 0.05\]

<p>FPR is important because it directly impacts customer experience.
High FPR means many legitimate transactions are blocked, leading to customer dissatisfaction and potential loss of business trust.</p>

<p>Sometimes businesses set FPR requirements to control false alarms.
For example: “We can only tolerate reviewing 0.5% of transactions, so FPR must be ≤ 0.005.”</p>

<h1 id="fnr">FNR</h1>

<p>False Negative Rate (FNR) is the share of fraudulent transactions the model misses.</p>

<p>For example, if there were 100 actual fraud cases and the model missed 2 of them, then:</p>

\[\text{FNR} = \frac{FN}{TP + FN} = \frac{2}{98 + 2} = 0.025\]

<p>Some businesses set FNR requirements to control missed fraud.
For example: “We cannot tolerate missing more than 10% of fraud, so FNR ≤ 0.1” which implies Recall ≥ 0.9.</p>

<h1 id="tnr">TNR</h1>

<p>True Negative Rate (TNR) or Specificity measures how well the system correctly identifies legitimate transactions as non-fraud.</p>

<p>For example, if there were 1000 legitimate transactions and the model flagged 50 of them incorrectly as fraud (FP), the calculation would be:</p>

\[\text{TNR} = \frac{TN}{TN + FP} = \frac{950}{950 + 50} = 0.95\]

<p>TNR is often overlooked in fraud detection because it’s essentially the complement of the False Positive Rate (FPR):</p>

\[\text{TNR} = 1 - \text{FPR}\]

<p>TNR is typically very high in fraud detection systems because the number of legitimate transactions (TN) is much larger than the number of frauds or false positives.</p>

<p>Since Precision already focuses on avoiding false positives, and we typically assume we want to approve as many legitimate transactions as possible, TNR doesn’t usually take center stage.</p>

<p>However, in some contexts, like regulatory requirements or customer experience, it’s important to keep FPR below a certain threshold, such as “FPR ≤ 0.1%,” which directly relates to maintaining high TNR.</p>

<h1 id="auc-roc">AUC-ROC</h1>

<p>The Area Under the ROC Curve<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> (AUC-ROC) measures a model’s ability to distinguish fraud from non-fraud across all possible thresholds.
In essence, it plots Recall against FPR.</p>

<p>The AUC is the area under this curve:</p>

<ul>
  <li>AUC = 0.5 means random guessing.</li>
  <li>AUC = 1.0 means perfect discrimination.</li>
</ul>

<p>This area is computed as follows:</p>

\[\text{AUC} = \int_0^1 \text{Recall}(FPR) \, dFPR\]

<p>AUC is threshold-independent: it summarizes performance across all thresholds, and it’s less sensitive to class imbalance than accuracy.</p>

<p>An intuitive interpretation: “If I randomly pick a fraud and a legitimate transaction, AUC is the chance the fraud gets a higher risk score.”</p>

<h1 id="auc-pr">AUC-PR</h1>

<p>The Area Under Precision-Recall Curve (AUC-PR) plots Precision vs. Recall and focuses squarely on the minority class (fraud), so it tells us how well the model catches fraud while keeping false positives low.</p>

<p>In highly imbalanced data such as fraud detection, AUC-PR is more informative than AUC-ROC because it answers how well the model balances Precision and Recall where it matters.</p>

<p>For instance, a model could have AUC-ROC = 0.98 and still have AUC-PR = 0.10, which means that the model detects fraud more often than non-fraud, but when it comes to real-world detection, Precision at high Recall isn’t stellar.</p>

<p>AUC-PR is the go-to metric when fraud cases are rare, and we care about catching as many as possible without overwhelming the system with false alarms.</p>

<h2 id="threshold-for-auc-pr">Threshold for AUC-PR</h2>

<p>Once we have chosen the best model as per AUC-PR, we need to decide a threshold, denoted as \(\tau\), to convert this model’s fraud probability output into a concrete binary decision (fraud or not fraud).</p>

<p>If we look at the Precision-Recall (PR) curve in the figure below, different values of \(\tau\) correspond to different trade-offs between Precision and Recall.</p>

<figure class="jb_picture">
  



<img width="100%" style="border: 1px solid #808080;" src="/assets/resized/AUPRC-640x477.png" alt="Sample Precision-Recall curves for two models A and B." data-srcset="/assets/resized/AUPRC-640x477.png 640w,/assets/resized/AUPRC-768x573.png 768w,/assets/resized/AUPRC-1024x763.png 1024w,/assets/resized/AUPRC-1366x1018.png 1366w," class="blur-up lazyautosizes lazyload" />
  <figcaption class="stroke">
    Sample Precision-Recall curves for two models A and B. Model B is superior to model A as is reflected in the AUC-PR values of the two models. Different points on the PR curve represent different threshold values and different trade-offs between Precision and Recall metrics.
  </figcaption>
</figure>

<p>We ultimately need to choose the right trade-off that suits our use case.
The threshold \(\tau\) determines the decision boundary for classifying transactions as fraudulent or genuine. Mathematically, this can be expressed as:</p>

\[\text{Decision: Fraud if } P(x) &gt; \tau\]

<p>Where:</p>

<ul>
  <li>\(P(x)\) is the predicted probability of fraud for transaction \(x\).</li>
  <li>\(\tau\) is the threshold value.</li>
</ul>

<p>With the above framework in mind, we can decide the threshold value \(\tau\) based on the value of \(k\), where \(k\) represents the minimum Precision we want to maintain.</p>

<p>For example, if we want to maintain a minimum Precision of 90%, then \(k = 90\). Using the Precision-Recall curve, we can derive the threshold value \(\tau\) as well as the equivalent Recall value at 90% Precision.</p>

<p>This strategy allows us to calculate the optimal threshold \(\tau\) while evaluating our trained model on the test set. Once the threshold is determined, it can be used to classify transactions during deployment:</p>

\[\text{Fraud if } P(x) &gt; \tau, \text{ otherwise Genuine.}\]

<p>By adjusting \(\tau\), we can balance Precision and Recall to meet specific business objectives and constraints.</p>

<h1 id="latency">Latency</h1>

<p>Latency is the time it takes for the system to process a transaction and make an inference.
Keeping latency low is crucial for real-time systems.
Fraud models not only need to have good statistical performance but also operate quickly enough to be used in practice.</p>

<p>Latency and complexity matter in payment systems.
In the example below, the <code class="language-plaintext highlighter-rouge">Payment Server</code> dispatches parallel calls to <code class="language-plaintext highlighter-rouge">KYC Service</code>, <code class="language-plaintext highlighter-rouge">Fraud Check</code>, and <code class="language-plaintext highlighter-rouge">Payment Rail</code>, but the transaction can only complete once the slowest of these services responds (the <code class="language-plaintext highlighter-rouge">KYC Service</code> in this example).
Even though <code class="language-plaintext highlighter-rouge">Fraud Check</code> takes just 25 ms, any fluctuation (like a network hiccup or a slow third-party response) can bottleneck the entire flow.
That’s why latency is a system-wide risk amplifier.</p>

<pre><code class="language-mermaid">flowchart LR
  %% define the Payment App node
  PaymentApp[Payment App]

  %% container for backend services
  subgraph container [" "]
    direction LR
    Server[Payment Server]
    KYC[KYC Service]
    Fraud[Fraud Check]
    Rail[Payment Rail]
  end

  %% draw the edges with labels
  PaymentApp --&gt;|50ms| Server
  Server     --&gt;|50ms| KYC
  Server     --&gt;|25ms| Fraud
  Server     --&gt;|25ms| Rail

  %% style all four links purple
  linkStyle 0,1,2,3 stroke:#800080,stroke-width:2px

  %% highlight Fraud Check in red
  style Fraud fill:#ffe6e6,stroke:#ff0000,stroke-width:2px
</code></pre>

<p>Real-time fraud detection latency is commonly measured in two ways:</p>

<ol>
  <li><strong>Online decision latency (ODL):</strong> How long it takes to score a single transaction and respond (which affects user experience and fraud blocking effectiveness).</li>
  <li><strong>Time-to-detection for fraud patterns (TTD):</strong> If an attack starts at a certain time, how long before the system detects and flags it.</li>
</ol>

<p>ODL is usually measured in milliseconds.
For example, a payment system might have an end-to-end latency budget of 200ms for authorization, out of which fraud checks get 20–30ms.
Modern systems often aim for fraud model inference under ~50ms.
In practice, we can look at 99th percentile latency (e.g., 99% of transactions scored in &lt;500ms), to ensure worst-case delays are bounded.</p>

<p>TTD is more about monitoring and measuring the resilience of the system to detect an emerging fraud <em>modus operandi</em>.
For example: “Did we catch the new fraud ring the first day it appeared, or did it go undetected for weeks?”
This is harder to quantify but important in evaluating adaptive systems.</p>

<h1 id="summary">Summary</h1>

<p>In practice, evaluating a fraud detection model involves:</p>

<ol>
  <li>Analyzing the confusion matrix at the operating point.</li>
  <li>Reviewing Recall, F1, and AUC-PR.</li>
  <li>Choosing a threshold that satisfies business constraints (e.g., maximum number of tolerable false positives).</li>
</ol>

<p>But evaluation doesn’t stop at metrics.
Weighting fraud by transaction amount matters: catching a 10,000 USD fraud is more impactful than catching five 1,000 USD cases.
Moreover, metrics on static test sets aren’t enough.
We also need to perform <a href="https://en.wikipedia.org/wiki/Backtesting">backtesting</a> (simulate past performance) and <a href="https://en.wikipedia.org/wiki/Sandbox_(computer_security)">sandbox testing</a> (simulate deployment), and monitor the model in production.</p>

<p>Observe how fraud patterns change: Do attackers evolve? Do false positives creep up?</p>

<p>Or even better: run <a href="https://en.wikipedia.org/wiki/A/B_testing">A/B tests</a>.</p>

<p>Put the new model in production in <a href="https://en.wikipedia.org/wiki/Shadowing_(computing)">shadow mode</a> and compare it to the previous version.</p>

<p>But that’s content for another post.</p>

<h1 id="footnotes">Footnotes</h1>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p><a href="https://en.wikipedia.org/wiki/Receiver_operating_characteristic">ROC Curve</a> stands for “Receiver Operating Characteristic”, a very weird name, indeed. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>César Soto Valero</name><email>cesarsotovalero@gmail.com</email></author><category term="ai" /><summary type="html"><![CDATA[After training a real-time financial fraud detection model, the next step is to evaluate its performance. This post provides an overview of the most common evaluation metrics and considerations for fraud detection models, including confusion matrix, precision, recall, F1-score, AUC-ROC, AUC-PR, and more.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.cesarsotovalero.net/img/posts/2025/2025-05-08/kungstradgarden_cover.jpg" /><media:content medium="image" url="https://www.cesarsotovalero.net/img/posts/2025/2025-05-08/kungstradgarden_cover.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">From Classical ML to DNNs and GNNs for Real-Time Financial Fraud Detection</title><link href="https://www.cesarsotovalero.net/blog/from-classical-ml-to-dnns-and-gnns-for-real-time-financial-fraud-detection.html" rel="alternate" type="text/html" title="From Classical ML to DNNs and GNNs for Real-Time Financial Fraud Detection" /><published>2025-04-03T00:00:00-07:00</published><updated>2026-04-02T05:57:52-07:00</updated><id>https://www.cesarsotovalero.net/blog/from-classical-ml-to-dnns-and-gnns-for-real-time-financial-fraud-detection</id><content type="html" xml:base="https://www.cesarsotovalero.net/blog/from-classical-ml-to-dnns-and-gnns-for-real-time-financial-fraud-detection.html"><![CDATA[<aside class="youtube">
        <a href="https://www.youtube.com/watch?v=nEg8T8doy64"><div class="box">
        <img src="https://i.ytimg.com/vi/nEg8T8doy64/mqdefault.jpg" alt="YouTube video #nEg8T8doy64" />
        <div class="play">
          <img src="/img/icons/youtube-play20px.svg" alt="Play Video" class="youtube-logo" />
        </div>
        </div></a>
        <div>César Soto Valero - Realtime Financial Fraud Detection with Modern Python-PyData Global 2025; 9 January 2026.</div></aside>

<p>Financial fraud is a pervasive problem costing institutions and customers billions annually.<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup>
Most known examples include credit card fraud, fraudulent online payments, and money laundering.
Banks worldwide faced an estimated \(\$442\) billion in fraud-related losses in 2023 alone.
In particular, credit card transactional fraud is projected to reach \(\$43\) billion in annual losses by 2026.
Beyond direct losses, fraud undermines customer trust and damages banks’ reputation.
For example, it leads to false positives where legitimate transactions are wrongly blocked.
Consequently, financial fraud detection systems (a.k.a fraud scoring) must not only catch as much fraud as possible but also minimize false positives.</p>

<p>Fraudsters’ tactics evolve rapidly.
Traditional rule-based systems (or simple statistical methods) have proven inadequate against today’s adaptive fraud models.
On one hand, fraudsters form complex schemes and exploit networks of accounts.
On the other hand, legitimate transaction volumes continue to grow due to the rise of e-commerce and digital payments.</p>

<p>This situation has driven a shift toward Machine Learning (ML) and AI-based approaches that can learn subtle patterns and adapt over time.
Critically, financial fraud detection must happen in real-time (or near-real time) to intervene before fraudsters can complete illicit transactions.
Catching fraud “closer to the time of fraud occurrence is key” so that suspicious transactions can be blocked or flagged immediately.<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">2</a></sup></p>

<p>This article deep dives into the current state-of-the-art of real-time transactional fraud detection, spanning both academic research and current industry practices.</p>

<p>I cover the major model families used today:</p>

<ol>
  <li><strong>Classical ML models:</strong> Logistic regression, decision trees, random forests, and SVMs.</li>
  <li><strong>Deep Learning models:</strong> ANNs, CNNs, RNNs/LSTMs, autoencoders, and GANs.</li>
  <li><strong>Graph-based models:</strong> GNNs and graph algorithms that leverage transaction relationships.</li>
  <li><strong>Transformer-based and foundation models:</strong> Large pre-trained models like Stripe’s payments foundation model.</li>
</ol>

<p>For each category, I discuss representative use cases or studies, highlight strengths and weaknesses, and comment on their suitability for real-time fraud detection.</p>

<h1 id="classical-ml-models">Classical ML Models</h1>

<p>Classical ML algorithms have long been used in fraud detection and remain strong baselines in both research and production systems.
These include linear models like <a href="https://en.wikipedia.org/wiki/Logistic_regression">logistic regression</a>, distance-based classifiers like <a href="https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm">k-Nearest Neighbors</a>, and tree-based models such as <a href="https://en.wikipedia.org/wiki/Random_forest">random forest</a> and <a href="https://en.wikipedia.org/wiki/Gradient_boosting">gradient boosted trees</a> (e.g., <a href="https://xgboost.readthedocs.io/">XGBoost</a>).
These approaches operate on hand-crafted features derived from transaction data (e.g., <code class="language-plaintext highlighter-rouge">transaction_amount</code>, <code class="language-plaintext highlighter-rouge">location</code>, <code class="language-plaintext highlighter-rouge">device_id</code>, <code class="language-plaintext highlighter-rouge">time_of_day</code>, etc.), often requiring substantial feature engineering by domain experts.</p>

<p><strong>Logistic regression</strong> is a foundational model in fraud detection.
Banks and financial institutions have historically relied on it due to its simplicity and interpretability (each coefficient \(w_i\) has a direct and intuitive meaning). A positive coefficient means the feature increases the log-odds of fraud, a negative coefficient means it decreases the risk.</p>

\[P(y = 1 \mid \mathbf{x}) = \frac{1}{1 + e^{-(\mathbf{w}^\top \mathbf{x} + b)}}\]

<ul>
  <li>\(\mathbf{x}\): Feature vector (e.g., transaction amount, time of day, merchant category)</li>
  <li>\(\mathbf{w}\): Coefficients (risk factors)</li>
  <li>\(b\): Bias or intercept</li>
</ul>

<p>Even today, logistic models serve as interpretable baseline detectors and are sometimes combined with a <a href="https://en.wikipedia.org/wiki/Business_rule_management_system">Business Rule Management Systems</a>.
However, linear models struggle to capture complex non-linear patterns in large transaction datasets.</p>

<aside class="quote">
    <em>“XGBoost builds trees sequentially, where each tree learns from the mistakes of the previous ones.”</em>
</aside>

<p><strong>Decision trees</strong> and ensemble forests address this by automatically learning non-linear splits and interactions.
In fact, boosted decision tree ensembles (like XGBoost) became popular in fraud detection competitions and industry solutions due to their high accuracy on tabular data.<sup id="fnref:34"><a href="#fn:34" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>
These models can capture anomalous combinations of features in individual transactions effectively, learning complex, non-linear interactions between features.
For example, <a href="https://github.com/VedangW/ieee-cis-fraud-detection">the winning solutions</a> of the <a href="https://www.kaggle.com/c/ieee-fraud-detection/overview">IEEE-CIS fraud detection Kaggle challenge</a> (2019) heavily used engineered features fed into gradient boosting models, achieving strong performance (AUC ≈ 0.91).</p>

<p><strong>Support Vector Machines</strong> (<a href="https://en.wikipedia.org/wiki/Support_vector_machine">SVMs</a>) have also been explored in academic studies.
However, while they can model non-linear boundaries (with kernels), they tend to be computationally heavy for large datasets and offer no interpretable output.
Therefore, the industry has gravitated more to tree ensembles for complex models.</p>

<h2 id="strengths">Strengths</h2>

<p>Classical ML models are typically fast to train and infer, and many (especially logistic regression and decision trees) are relatively easy to interpret.
For instance, a logistic regression might directly quantify how much a mismatched billing address raises fraud probability, and a decision tree might provide a rule-like structure (e.g., “if IP country ≠ card country and amount &gt; $1000 ⇒ flag fraud”).
More complex models like XGBoost still allow some interpretability through <a href="https://machinelearningmastery.com/feature-importance-and-feature-selection-with-xgboost-in-python/">feature importance scores</a>, <a href="https://medium.com/@lfoster49203/interpretable-machine-learning-models-using-shap-and-lime-for-complex-data-6f65e1224209">CHAP values</a>, or partial dependence plots.</p>

<p>Classical ML models can be deployed in real-time with minimal latency.
A logistic regression is essentially a dot-product of features, and even a large XGBoost ensemble can score a transaction in tens of milliseconds or less on modern hardware.</p>

<p>They also perform well with limited data.
With careful feature engineering, a simple model can already catch a large fraction of fraud patterns.
Consequently, industry adoption is widespread, many banks initially deploy logistic or tree-based models in production, and even today XGBoost is a common choice in fraud ML pipelines.</p>

<h2 id="weaknesses">Weaknesses</h2>

<p>A key limitation of classical ML models is the reliance on manual feature engineering.
In other words, they cannot automatically invent new abstractions beyond the input features given.
These models may miss complex patterns such as sequential spending behavior or R-ring collusion<sup id="fnref:28"><a href="#fn:28" class="footnote" rel="footnote" role="doc-noteref">4</a></sup> between groups of accounts unless analysts explicitly code such features (e.g., number of purchases in the last hour, or count of accounts sharing an email domain).</p>

<p>They may also struggle with high-dimensional data like raw event logs or image data (this is where deep learning excels).
However, this is less an issue for structured transaction records.</p>

<p>Another challenge is class imbalance.
The occurrence of fraud is typically rare (often $ &lt;1\% $ of transactions), which can bias models to predict the majority “non-fraud” class.
Techniques like <a href="https://medium.com/@ravi.abhinav4/improving-class-imbalance-with-class-weights-in-machine-learning-af072fdd4aa4">balanced class weighting</a>, <a href="https://www.kaggle.com/code/residentmario/undersampling-and-oversampling-imbalanced-data">undersampling</a>, or <a href="https://machinelearningmastery.com/smote-oversampling-for-imbalanced-classification/">SMOTE</a> are often needed to train classical models effectively on imbalanced fraud data.<sup id="fnref:16"><a href="#fn:16" class="footnote" rel="footnote" role="doc-noteref">5</a></sup></p>

<p>Finally, while faster than deep neural networks, complex ensembles (hundreds of trees) can become memory-intensive and may require optimization for ultra-low latency at high transaction volumes.</p>

<h2 id="real-time-suitability">Real-Time Suitability</h2>

<p>Classical models are generally well-suited to real-time fraud scoring.
They have low latency inference and modest resource requirements.</p>

<p>For example, a bank’s fraud engine might run a logistic regression and a few decision tree rules in under 10ms per transaction on a CPU.
Even a sophisticated random forest or gradient boosting model can be served via highly optimized C++ libraries or cloud ML endpoints to meet sub-hundred-millisecond SLAs.<sup id="fnref:29"><a href="#fn:29" class="footnote" rel="footnote" role="doc-noteref">6</a></sup></p>

<p>The straightforward nature of these models also simplifies transaction monitoring and model updates.
New data can be used to frequently retrain or update coefficients (even via <a href="https://en.wikipedia.org/wiki/Online_machine_learning">online learning</a> for logistic regression).
The main caution is that if fraud patterns shift significantly (<a href="https://en.wikipedia.org/wiki/Concept_drift">concept drift</a>), purely static classical models will need frequent retraining to keep up.</p>

<p>In practice, many organizations retrain or fine-tune their fraud models on recent data weekly or even daily to adapt to new fraud tactics.
So, while classical models are fast to deploy and iterate on, they do require ongoing maintenance to remain effective.</p>

<h2 id="examples">Examples</h2>

<p>Representative research and use-cases for classical methods include:</p>

<ul>
  <li><strong>Logistic regression and decision trees as baseline models:</strong> Many banks have deployed logistic regression for real-time credit card fraud scoring due to its interpretability.</li>
  <li><strong>Ensemble methods in academic studies:</strong> Research has focused on evaluating logistic vs. decision tree vs. random forest on a credit card dataset (often finding tree ensembles outperform linear models in Recall).<sup id="fnref:17"><a href="#fn:17" class="footnote" rel="footnote" role="doc-noteref">7</a></sup></li>
  <li><strong>Kaggle competitions:</strong> XGBoost was heavily used in the <a href="https://www.kaggle.com/c/ieee-fraud-detection">Kaggle IEEE-CIS 2019 competition</a>, leveraging high accuracy on tabular features.</li>
  <li><strong>Hybrid systems:</strong> Many production systems combine manual business rules for known high-risk patterns with an ML model for subtler patterns, using the rules for immediate high-precision flags and the ML model for broad coverage.</li>
</ul>

<h1 id="deep-learning-models">Deep Learning Models</h1>

<p>In recent years, <a href="https://en.wikipedia.org/wiki/Deep_learning#Deep_neural_networks">Deep Neural Networks</a> (DNNs) have been <a href="https://opencv.org/blog/online-transaction-fraud-detection-using-deep-learning">applied to transaction fraud detection</a> with promising results.
DNNs can automatically learn complex feature representations from raw data, potentially capturing patterns that are hard to manually engineer or find with classical ML models.</p>

<h2 id="deep-learning-architectures">Deep Learning Architectures</h2>

<p>Several deep architectures have been explored for fraud detection.
Below, I summarize the most common types.</p>

<h3 id="feed-forward-neural-networks-anns">Feed-Forward Neural Networks (ANNs)</h3>

<p><a href="https://en.wikipedia.org/wiki/Feedforward_neural_network">ANNs</a> are multi-layer perceptron treating each transaction’s features as input neurons.
These can model non-linear combinations of features beyond what logistic regression can capture.
In practice, simple feed-forward networks have been used as a baseline deep model for fraud (e.g., a 3-layer network on credit card data).
They often perform similarly to tree ensembles if ample data is available but are harder to interpret.
They also don’t inherently handle sequential or time-based information beyond what the input features provide.</p>

<h3 id="convolutional-neural-networks-cnns">Convolutional Neural Networks (CNNs)</h3>

<p><a href="https://en.wikipedia.org/wiki/Convolutional_neural_network">CNNs</a> are most famous for image-related tasks.
However, they have also being been applied to fraud by treating transaction data as temporal or spatial sequences.
For example, a CNN can slide over a sequence of past transactions for a user to detect local patterns or use 1D convolution on time-series of transaction amounts.</p>

<p>CNNs excel at automatic feature extraction of localized patterns.
Some research reformats transaction histories into a 2D “image” (e.g., time vs. feature dimension) so that CNNs can detect anomalous shapes.</p>

<p>CNNs for detecting fraud have seen limited but growing use.
One recent study reported ~99% detection accuracy with a CNN on a credit card dataset.<sup id="fnref:19"><a href="#fn:19" class="footnote" rel="footnote" role="doc-noteref">8</a></sup>
However, such high accuracy is likely due to the highly imbalanced nature of the dataset (using AUC or F1 is more meaningful).</p>

<h3 id="recurrent-neural-networks-rnns">Recurrent Neural Networks (RNNs)</h3>

<p><a href="https://en.wikipedia.org/wiki/Recurrent_neural_network">RNNs</a>, including <a href="https://en.wikipedia.org/wiki/Long_short-term_memory">LSTM</a> and <a href="https://en.wikipedia.org/wiki/Gated_recurrent_unit">GRU</a> networks, are well-suited for sequential transactional data.
They maintain a memory of past events, making them ideal for modeling an account’s behavior over time.</p>

<p>For example, an LSTM can consume a customer’s sequence of transactions (with timestamps) and detect if the latest transaction is anomalous given the recent pattern.
This temporal modeling is very powerful for fraud because many fraud patterns only make sense in context (e.g., a sudden spending spike, or a purchase in a new country right after another far-away purchase).</p>

<p>Research has shown LSTM-based models can effectively distinguish fraudulent vs. legitimate sequences.
In one case, an LSTM achieved significantly higher Recall than static models by catching subtle temporal shifts in user behavior.<sup id="fnref:13"><a href="#fn:13" class="footnote" rel="footnote" role="doc-noteref">9</a></sup>
RNNs do require sequential data, so for one-off transactions without history they are less applicable (unless modeling at the merchant or account aggregate level).</p>

<h3 id="autoencoders">Autoencoders</h3>

<p><a href="https://en.wikipedia.org/wiki/Autoencoder">Autoencoders</a> are unsupervised anomaly detection models that learn to compress and reconstruct data.
When trained on predominantly legitimate transactions, an autoencoder captures the underlying structure of normal behavior (a.k.a. the “normal manifold”).
As a result, it can reconstruct typical transactions with very low error, but struggles with atypical or anomalous ones.
A transaction that doesn’t conform to the learned normal pattern will produce a higher reconstruction error.
By setting a threshold, we can flag transactions with unusually high reconstruction error as potential fraud.</p>

<p>Autoencoders shine in fraud detection, particularly when labeled fraud data is scarce or nonexistent.<sup id="fnref:33"><a href="#fn:33" class="footnote" rel="footnote" role="doc-noteref">10</a></sup>
Their strength lies in identifying transactions that deviate from the learned “normal” without requiring explicit fraud labels during training.
For example, an autoencoder trained on millions of legitimate transactions will likely assign high reconstruction error to fraudulent ones it’s never seen before.
<a href="https://en.wikipedia.org/wiki/Variational_autoencoder">Variational Autoencoder</a>s (VAEs), which introduce probabilistic modeling and latent-space regularization—have also been explored for fraud detection, offering potentially richer representations of normal transaction behavior.<sup id="fnref:21"><a href="#fn:21" class="footnote" rel="footnote" role="doc-noteref">11</a></sup></p>

<h3 id="generative-adversarial-networks-gans">Generative Adversarial Networks (GANs)</h3>

<p><a href="https://en.wikipedia.org/wiki/Generative_adversarial_network">GANs</a> consist of a generator and discriminator.
The generator creates synthetic data, while the discriminator tries to distinguish real from fake data.</p>

<p>There are two main applications of GANs in fraud detection:</p>

<ol>
  <li>
    <p><strong>Generate realistic synthetic fraud examples:</strong> GANs can augment training data to address class imbalance. The generator is trained to produce fake transactions that the discriminator (trained to distinguish real vs. fake) finds plausible. By adding these synthetic frauds to the training set, models (including non-deep models) can learn a broader decision boundary.</p>
  </li>
  <li>
    <p><strong>Serve as anomaly detectors:</strong> The generator tries to model the distribution of legitimate transactions, and the discriminator’s output can highlight outliers.</p>
  </li>
</ol>

<p>Some financial institutions have experimented with GANs.
For example, <a href="https://developer.nvidia.com/blog/detecting-financial-fraud-using-gans-at-swedbank-with-hopsworks-and-gpus/">Swedbank reportedly used GANs</a> to generate additional fraudulent examples for training their models.
However, GAN training can be complex and less common in production.
Still, in research, GAN-based methods have shown improved Recall by expanding the fraud training sample space.<sup id="fnref:22"><a href="#fn:22" class="footnote" rel="footnote" role="doc-noteref">12</a></sup></p>

<h3 id="hybrid-deep-learning-models">Hybrid Deep Learning Models</h3>

<p>There are also custom DNNs architectures combining elements of the above, or combining deep models with classical ones.</p>

<p>For example, a “wide and deep model” might have a linear (wide) component for memorizing known risk patterns and a neural network (deep) component for generalization.
Another example is combining an LSTM for sequence modeling with a feed-forward network for static features (“dual-stream” models).</p>

<p>Ensembles of deep and non-deep models have also been used (e.g., using an autoencoder’s anomaly score as an input feature to a random forest).
Recent research explores stacking deep models with tree models to improve robustness and interpretability.</p>

<h2 id="strengths-1">Strengths</h2>

<p>DNNs biggest advantage is automated feature learning.
These types of models can uncover intricate, non-linear relationships and subtle correlations within massive datasets that older methods miss.
They can digest raw inputs (inc. unstructured data) and find patterns without explicit human-designed features.
For instance, an RNN can learn the notion of “rapid spending spree” or “geographical inconsistency” from raw sequences, which would be hard to capture with handcrafted features.</p>

<p>In fraud detection, large payment companies have millions of transactions which deep models can leverage to potentially exceed the accuracy of simpler models.
DNNs also tend to improve with more data, whereas classical models may saturate in performance.</p>

<p>Another strength is handling complex data types.
For example, if one incorporates additional signals like device fingerprints, text (e.g., product names), or network information, deep networks can combine these modalities more seamlessly (e.g., an embedding layer for device ID, an LSTM for text description, etc.).</p>

<p>In practice, DNNs have shown higher Recall at a given false-positive rate compared to classical models, in several cases.<sup id="fnref:13:1"><a href="#fn:13" class="footnote" rel="footnote" role="doc-noteref">9</a></sup>
They are also adaptive architectures like RNNs or online learning frameworks can update as new data comes in, enabling continuous learning, which is important as fraud scenarios evolve.</p>

<h2 id="weaknesses-1">Weaknesses</h2>

<p>The primary downsides of DNN are complexity and interpretability.</p>

<p>Deep networks are considered “black boxes”, meaning that it’s non-trivial to explain why a certain transaction was flagged as fraudulent.
This is problematic for financial institutions that need to justify decisions to customers or regulators.
Techniques like <a href="https://shap.readthedocs.io/">SHapley Additive exPlanations</a> (SHAP) or <a href="https://github.com/marcotcr/lime">Local Interpretable Model-Agnostic Explanations</a> (LIME) can help interpret feature importance for deep models, <a href="https://www.milliman.com/en/insight/Explainable-AI-in-fraud-detection">but it’s still harder</a> compared to a linear model or decision tree.</p>

<aside class="quote">
    <em>“DNNs can really shine only when there are huge datasets or additional unlabeled data to pre-train on.”</em>
</aside>

<p>Another issue is the data and compute requirement.
Training large DNNs may require GPUs and extensive hyperparameter tuning, which can be overkill for some fraud datasets, especially if data is limited or highly imbalanced.<sup id="fnref:32"><a href="#fn:32" class="footnote" rel="footnote" role="doc-noteref">13</a></sup>
In fact, many academic studies on the popular <a href="https://www.kaggle.com/datasets/mlg-ulb/creditcardfraud">Kaggle credit card dataset</a> (284,807 transactions) found that simpler models can match DNNs performance, likely because the dataset is small and mostly numeric features.</p>

<p>Overfitting is a risk too, fraud datasets are skewed and sometimes composed of static snapshots in time.
A DNN might memorize past fraud patterns that fraudsters no longer use, if not carefully regularized.</p>

<p>Finally, latency can be a concern.
A large CNN or LSTM might take longer to evaluate than a logistic regression.
However, many deep models used for fraud are not excessively large (e.g., an LSTM with a few hundred units), and with optimized inference (batching, quantization, etc.) they can often still meet real-time requirements.
I discuss latency more later, but suffice it to say that deploying deep models at scale might necessitate GPU acceleration or model optimizations in high-throughput environments.</p>

<h2 id="real-time-suitability-1">Real-Time Suitability</h2>

<p>DNNs models can be deployed for real-time fraud scoring, but it requires more care than classical models.
Simpler networks (small MLPs) are no issue in real-time.
However, RNNs or CNNs might introduce slight latency (tens of milliseconds).
Nevertheless, modern inference servers and even FPGAs/TPUs can handle thousands of inferences per second.
For instance, Visa reportedly targets fraud model evaluations in under ~25ms as part of their payment authorization pipeline.
It’s feasible to achieve this with a moderately sized neural network and good infrastructure.</p>

<p>Scaling to high transaction volumes is another aspect.
Deep models may consume more CPU/GPU resources, so a cloud deployment might need to autoscale instances or use GPU inference for peak loads.</p>

<p>A potential strategy for real-time use is a two-stage system: a fast classical model first filters obvious cases (either definitely legitimate or obviously fraudulent), and a slower deep model only analyzes the ambiguous middle chunk of transactions.
This way, the heavy model is used on a fraction of traffic to keep overall throughput high.</p>

<p>Additionally, organizations often maintain a feedback loop.
Flagged predictions are first reviewed by analysts or via outcomes like chargebacks, and then a DNN model is retrained frequently to incorporate the latest data.</p>

<p>Some deep models can be updated via online learning.
For example, an RNN that continuously updates its hidden state or a streaming NN that periodically retrains on a rolling window of data, which helps keep them current with
concept drift.</p>

<h2 id="examples-1">Examples</h2>

<p>Notable examples of deep learning in fraud detection:</p>

<ul>
  <li><strong>Feedforward DNNs:</strong> PayPal in the mid-2010s <a href="https://www.paypal.com/us/brc/article/payment-fraud-detection-machine-learning?utm_source=chatgpt.com">applied neural networks to fraud</a>, fintech companies like Feedzai have further advanced this methodology by combining DNNs with tree-based models.<sup id="fnref:24"><a href="#fn:24" class="footnote" rel="footnote" role="doc-noteref">14</a></sup></li>
  <li><strong>RNNs and LSTMs:</strong> Multiple studies have shown that LSTM networks can detect sequential fraud behavior that static models miss, improving Recall by capturing temporal patterns. Large merchants have employed LSTM-based models to analyze user event streams, enabling the detection of account takeovers and session-based fraud in real-time.</li>
  <li><strong>Autoencoder-based anomaly detection:</strong> Unsupervised autoencoders have been used by banks to flag new types of fraud. For instance, an autoencoder trained on normal mobile transactions flagged anomalies that turned out to be new fraud rings exploiting a loophole (detected via high reconstruction error).</li>
  <li><strong>Hybrid models:</strong> Recent trends include using DNNs to generate features for a gradient boosted tree. One effective approach is to use deep learning models, such as autoencoders or embedding networks, to learn rich feature representations from transaction data. These learned embeddings are then fed into XGBoost, combining the deep models’ ability to capture complex patterns with the interpretability and efficiency of tree-based methods</li>
</ul>

<h1 id="graph-based-models">Graph-Based Models</h1>

<p>Groups of fraudsters might share information (e.g., using the same stolen cards or devices), or a single fraudster might operate many accounts that transact with each other.
A powerful class of methods treats the financial system as a graph, linking entities like users, accounts, devices, IP addresses, merchants, etc.
<a href="https://github.com/safe-graph/graph-fraud-detection-papers">Graph-based fraud detection models</a> aim to exploit these relational structures to detect fraud patterns that single-transaction models might miss.
Classical graph algorithms can then be applied, such as community detection<sup id="fnref:25"><a href="#fn:25" class="footnote" rel="footnote" role="doc-noteref">15</a></sup> and link analysis (e.g., <a href="https://en.wikipedia.org/wiki/PageRank">PageRank</a> on the fraud graph).</p>

<figure class="jb_picture">
  



<img width="100%" style="border: 1px solid #808080;" src="/assets/resized/suspicious-subgraphs-640x378.png" alt="Illustration of entity linkages in transaction fraud" data-srcset="/assets/resized/suspicious-subgraphs-640x378.png 640w,/assets/resized/suspicious-subgraphs-768x454.png 768w," class="blur-up lazyautosizes lazyload" />
  <figcaption class="stroke">
    Illustration of entity linkages in transaction fraud: Shared devices, phone numbers, and locations connect different users. Fraudsters (devil icons) may create many accounts that all link through common data points (phone, IP, geo), forming <b>suspicious</b> subgraphs that graph-based methods can detect.
  </figcaption>
</figure>

<p>For example, in a bipartite graph of credit card transactions, one set of nodes represent cardholders, another set are merchants, and there is an edge connecting a cardholder to a merchant for each transaction.
Fraudulent cards might cluster via merchant edges (e.g., a fraud ring testing many stolen cards at one merchant), or vice versa.<sup id="fnref:35"><a href="#fn:35" class="footnote" rel="footnote" role="doc-noteref">16</a></sup>
Similarly, for online payments we can create nodes for user accounts, email addresses, IP addresses, device IDs, etc., and connect nodes that are observed together in a transaction or account registration.
This yields a rich heterogeneous graph of entities.</p>

<p><a href="https://en.wikipedia.org/wiki/Graph_neural_network">Graph Neural Networks</a> (GNNs) in recent years has led to many applications of this technology in fraud detection.<sup id="fnref:23"><a href="#fn:23" class="footnote" rel="footnote" role="doc-noteref">17</a></sup> <sup id="fnref:30"><a href="#fn:30" class="footnote" rel="footnote" role="doc-noteref">18</a></sup> <sup id="fnref:31"><a href="#fn:31" class="footnote" rel="footnote" role="doc-noteref">19</a></sup>
GNNs are deep learning models designed for graph-structured data.
They propagate information along edges, allowing each node to aggregate features from its neighbors.
In fraud terms, a GNN can learn to identify suspicious nodes (e.g., users or transactions) by looking at their connected partners.
For instance, if a particular device ID node connects to many user accounts that were later flagged as fraud, a GNN can learn to embed that device node as high-risk, which in turn raises the risk of any new account connected to it.</p>

<aside class="quote">
    <em>“Fraud is rarely a problem of isolated events… fraudsters operate within complex networks.”</em>
</aside>

<p>GNNs consider connections between accounts and transactions to reveal patterns of suspicious activity across the network.
By incorporating relational context, GNNs have demonstrated higher fraud detection accuracy and fewer false positives than models that ignore graph structure.
For example, combining GNNs features with an XGBoost classifier led to catching fraud that would otherwise go undetected and reducing false alarms due to the added network context.
A GNN approach might catch a seemingly normal transaction if the card, device, or IP involved has connections to known frauds that a non-graph model wouldn’t see.</p>

<p>Several types of GNNs architectures have been used.
Notably, <a href="https://paperswithcode.com/method/gcn">Graph Convolutional Networks</a> (GCN), <a href="https://neo4j.com/docs/graph-data-science/current/machine-learning/node-embeddings/graph-sage/">GraphSAGE</a>, heterogeneous GNNs for multi-type node graphs, and even <a href="https://paperswithcode.com/method/graph-transformer">Graph Transformers</a>.</p>

<p>A popular benchmark for GNNs is the <a href="https://pytorch-geometric.readthedocs.io/en/latest/generated/torch_geometric.datasets.EllipticBitcoinDataset">Elliptic dataset</a>, a Bitcoin transaction graph where GNNs have been applied to identify illicit transactions by classifying nodes in a large transaction graph.
GNNs have also been applied to credit card networks: e.g., researchers have built graphs linking credit card numbers, merchants, and phone numbers, and used a heterogeneous GNN to detect fraud cases involving synthetic identities and collusive merchants.<sup id="fnref:3"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">20</a></sup></p>

<h2 id="strengths-2">Strengths</h2>

<p>Graph-based methods can detect patterns of collusion and linkage that purely feature-based models miss.
They effectively augment each transaction with context.
Rather than evaluating an event in isolation, the model considers the broader network (device usage graph, money flow graph, etc.).
This is crucial for catching fraud rings.
For example, multiple accounts controlled by one entity or chains of transactions moving funds, which might appear normal individually but are anomalous in aggregate.
GNNs in particular combine the best of both worlds: they leverage graph structure + attribute features together, learning meaningful representations of nodes/edges.<sup id="fnref:3:1"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">20</a></sup>
This is important when fraudsters deliberately make individual transactions look innocuous but cannot hide the relationships (e.g., reusing the same phone or IP address across many accounts).</p>

<p>Another advantage is in reducing false positives by providing context.
For example, a transaction with a new device might normally seem risky, but if that device has a long history with the same user and no links to bad accounts, a graph model can recognize it as low risk, avoiding a false alarm.
Industry reports indicate that adding graph features or GNNs outputs has improved Precision of fraud systems by filtering out cases that looked suspicious in isolation but were safe in context.<sup id="fnref:4"><a href="#fn:4" class="footnote" rel="footnote" role="doc-noteref">21</a></sup></p>

<h2 id="weaknesses-2">Weaknesses</h2>

<aside class="quote">
    <em>“Current GNNs solutions mainly rely on offline batch training and inference, predicting fraudsters in real-time is crucial but challenging.”</em>
</aside>

<p>The biggest challenge is complexity in implementation and deployment.
Building and maintaining the graph data (a.k.a. the “graph pipeline”) is non-trivial.
Transactions arrive in a stream and must update the graph in real-time (e.g., adding new nodes, new edges).
Querying the graph for each new transaction’s neighborhood can be slow if not engineered well.
The inference itself can be heavy.
Running a GNNs means loading a subgraph and doing matrix operations that are costlier than a simple ML model.
Consequently, many current GNNs solutions operate in batch mode (offline).
There are limited reference architectures for real-time GNNs serving, though this is an active development area.</p>

<p>Another issue is scalability.
Graphs of financial transactions or users can be enormous (millions of nodes, tens of millions of edges).
Training a full GNNs on such a graph might not fit in memory or might be extremely slow without sampling techniques.
Some approaches use graph sampling or partitioning to handle this, or only use GNNs to generate features offline.</p>

<p>GNNs can be hard to interpret (even more so than regular deep nets) since the features are aggregate of neighbors.
It can be challenging to explain to an analyst why a certain account was flagged: the reason might be “it’s connected to three other accounts that had chargebacks,” which is somewhat understandable, but the GNN’s learned weights on those connections are not human-interpretable beyond that concept.</p>

<h2 id="real-time-suitability-2">Real-Time Suitability</h2>

<p>Real-time deployment of graph-based models is at the cutting edge.
It is being done in industry but often with approximations.
One pragmatic solution is to use graph analytics to create additional features for a traditional model.
For example, compute features like “number of accounts sharing this card’s IP address that were fraud” or “average fraud score of neighbors” and update these in real-time, then let a gradient boosting model or neural network consume them.
This doesn’t require full GNNs online inference, but captures some graph insights.
However, truly deploying a GNNs in production for each event requires a fast graph database or in-memory graph store.</p>

<p>AWS demonstrated a prototype using Amazon Neptune (graph DB) + DGL (Deep Graph Library) to serve GNNs predictions in real-time by querying a subgraph around the target node for each inference.<sup id="fnref:3:2"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">20</a></sup>
This kind of pipeline can risk score a transaction within seconds, which may be acceptable for certain use cases (e.g., online account opening fraud).
However, for high-frequency card transactions that need sub-second decisions, a full GNNs might still be too slow today unless heavily optimized.</p>

<p>An alternative is what Nvidia suggests: use GNNs offline to produce node embeddings or risk scores, then feed those into a superfast inference system (like an XGBoost model or a rules engine) for the real-time decision.<sup id="fnref:4:1"><a href="#fn:4" class="footnote" rel="footnote" role="doc-noteref">21</a></sup>
This hybrid approach was shown to work at large scale, where GNN-based features improved detection by even a small percent (say 1% AUC gain), which for big banks translates to millions saved.</p>

<p>Lastly, maintaining graph models demands continuous updates as the graph evolves.
This is still manageable, as new data can be incrementally added, but one must watch for concept drift in graph structure.
For example, fraud rings forming new connectivity patterns.</p>

<h2 id="examples-2">Examples</h2>

<p>Representative examples of graph-based fraud detection:</p>

<ul>
  <li><strong>Blockchain networks:</strong> The <a href="https://pytorch-geometric.readthedocs.io/en/latest/generated/torch_geometric.datasets.EllipticBitcoinDataset">Elliptic Bitcoin Dataset</a> is a graph of 203,769 transactions (nodes) with known illicit vs. licit labels. GNNs models on this dataset achieved strong results, showing that analyzing the transaction network is effective for detecting illicit cryptocurrency flows.</li>
  <li><strong>Credit card networks:</strong> Researchers built a graph of credit card transaction and applied a GNNs which outperformed a baseline MLP by leveraging connections (e.g., card linked to a fraudulent merchant gives card a higher fraud probability).</li>
  <li><strong>E-commerce networks:</strong> Companies like Alibaba and PayPal have internal systems modeling user networks. For example, accounts connected via a shared device or IP can indicate <a href="https://en.wikipedia.org/wiki/Sybil_attack">sybil attacks</a> or mule accounts. Graph algorithms identified clusters of accounts that share many attributes (forming fraud communities) which were then taken down as a whole.</li>
  <li><strong>Telecom identity fraud:</strong> Graphs connecting phone numbers, IDs, and addresses have been used to catch identity fraud rings. A famous case is detecting “bust-out fraud” in which a group of credit card accounts all max out and default: the accounts often share phone or address; linking them in a graph helps catch the ring before the bust-out completes.</li>
  <li><strong>Social networks:</strong> In social finance platforms or peer-to-peer payments, graph methods are used to detect money laundering or collusion by analyzing the network of transactions among users (e.g., unusually interconnected payment groups).</li>
</ul>

<p>Overall, graph-based methods, especially GNNs, represent a cutting-edge approach that can significantly enhance fraud detection by considering relational data.
As tooling and infrastructure improve (graph databases, streaming graph processing), I expect to see more real-time GNNs deployments for fraud in the coming years.</p>

<h1 id="transformer-models">Transformer Models</h1>

<h2 id="transformers">Transformers</h2>

<p><a href="https://en.wikipedia.org/wiki/Transformer_(deep_learning_architecture)">Transformers</a> (originally developed for language processing) have revolutionized many domains, and they are now making inroads in fraud detection.
The key innovation of transformers is the <a href="https://www.ibm.com/think/topics/self-attention">self-attention mechanism</a>, which allows modeling long-range dependencies in sequences.
In the context of transaction data, transformers can analyze transaction sequences or sets of features in flexible ways.</p>

<p>Large pre-trained foundation models (akin to GPT or BERT, but for payments) are emerging.
In this case, a model is pre-trained on massive amounts of transaction data to learn general patterns, then fine-tuned for specific fraud tasks.
So that these models can “speak” transactional data.</p>

<blockquote>
  <p>“One of the most notable recent developments comes from Stripe’s <a href="https://www.linkedin.com/posts/gautam-kedia-8a275730_tldr-we-built-a-transformer-based-payments-activity-7325973745292980224-vCPR/">transformer-based payments foundation model.</a> <!-- markdownlint-disable-line MD033 -->
This is a large-scale self-supervised model trained on tens of billions of transactions to create embeddings of each transaction.
The idea is analogous to how LLMs work: to learn a high-dimensional embedding for a transaction that captures its essential characteristics and context.
Transactions with similar patterns end up with similar embeddings, e.g., transactions from the same bank or the same email domain cluster together in embedding space.
These embeddings serve as a universal representation that can be used for various tasks: fraud detection, risk scoring, identifying businesses in trouble, etc.
For the fraud use-case, Stripe reports a dramatic improvement: by feeding sequences of these transaction embeddings into a downstream classifier, they achieved an increase in detection rate for certain fraud attacks from 59% to 97% overnight.
In particular, they targeted “card testing fraud” (i.e., fraudsters testing stolen card credentials with small purchases), something that often hides in high-volume data.
The transformer foundation model was able to spot subtle sequential patterns of card testing that previous feature-engineered models missed, blocking attacks in real-time before they could do damage.”</p>
</blockquote>

<p>Researchers have applied Transformer encoders to tabular data as well.<sup id="fnref:18"><a href="#fn:18" class="footnote" rel="footnote" role="doc-noteref">22</a></sup>
For example, using models like <a href="https://github.com/lucidrains/tab-transformer-pytorch">TabTransformer</a> or integration of transformers with structured data.
They reported improved accuracy over MLPs and even over tree models in some cases.<sup id="fnref:26"><a href="#fn:26" class="footnote" rel="footnote" role="doc-noteref">23</a></sup></p>

<p>The ability of transformers to focus attention on important features or interactions could be beneficial for high-dimensional transaction data.
For example, a transformer might learn to put high attention on the <code class="language-plaintext highlighter-rouge">device_id</code> feature when the <code class="language-plaintext highlighter-rouge">ip_address_country</code> is different from the <code class="language-plaintext highlighter-rouge">billing country</code>, effectively learning a rule-like interaction that would be hard for a linear model.</p>

<p>Transformers can also model cross-item sequences: one can feed a sequence of past transactions as a “sentence” into a transformer, where each transaction is like a token embedding (comprising attributes like amount, merchant category, etc.).
The transformer can then output a representation of the sequence or of the next transaction’s risk.
This is similar to an RNN’s use but with the advantage of attention capturing long-range dependencies (e.g., a pattern that repeats after 20 transactions).
There have been experiments where a transformer outperformed LSTM on fraud sequence classification, due to its parallel processing and ability to consider all transactions’ relations at once.</p>

<p>Another angle is using transformer models for entity resolution and representation in fraud. For instance, a transformer can be trained on the corpus of all descriptions or merchant names that a user has transacted with, thereby learning a “profile” of the user’s spending habits and detecting an out-of-profile transaction (similar to how language models detect an odd word in a sentence).
Additionally, <a href="https://en.wikipedia.org/wiki/BERT_(language_model)">BERT</a>-like models can be used on event logs or customer support chats to detect social engineering fraud attempts, though that’s adjacent to transaction fraud.</p>

<h2 id="foundation-models">Foundation models</h2>

<p><a href="https://en.wikipedia.org/wiki/Foundation_model">Foundation models</a> in fraud detection refer to large models trained on broad data that can then be adapted.
Besides Stripe’s payments’ model, other financial institutions are likely developing similar pre-trained embeddings.
For example, a consortium of banks could train a model on pooled transaction data (in a privacy-preserving way, or via <a href="https://en.wikipedia.org/wiki/Federated_learning">federated learning</a>) to get universal fraud features.</p>

<p>These large models may use transformers or other architectures, but the common theme is self-supervised learning: e.g., predicting a masked field of a transaction (<code class="language-plaintext highlighter-rouge">merchant_category</code>, or <code class="language-plaintext highlighter-rouge">amount</code>) from other fields, or predicting the next transaction given past ones.
Through such tasks, the model gains a deep understanding of normal transactional patterns.
When fine-tuned on a specific fraud dataset, it starts with a rich feature space and thus performs better with less training data than a model from scratch.
This is analogous to how image models pre-trained on <a href="https://www.image-net.org/">ImageNet</a> are fine-tuned for medical images with small datasets.</p>

<h2 id="strengths-3">Strengths</h2>

<p>Transformers and foundation models bring state-of-the-art pattern recognition to fraud.
They particularly shine in capturing complex interactions and sequential/temporal patterns.
The attention mechanism allows the model to focus on the most relevant parts of the input for each decision.
For fraud, this could mean focusing on certain past transactions or specific features that are indicative of risk in context.
This yields high detection performance, especially for “hard fraud” that evades simpler models.</p>

<p>Another strength is multitasking capabilities.
A large foundation model can be trained once and then used for various related tasks such as fraud, credit risk, or marketing predictions simply by fine-tuning or prompting, rather than maintaining separate models for each.
This “one model, many tasks” approach can simplify the system and leverage cross-task learning (e.g., learning what a risky transaction looks like might also help predict chargebacks or customer churn).</p>

<p>Moreover, transformers can handle heterogeneous data relatively easily.
One can concatenate different feature types and the self-attention will figure out which parts to emphasize.
For example, Stripe’s model encodes each transaction as a dense vector capturing numeric fields, categorical fields, etc., all in one embedding.</p>

<p>Finally, foundation models can enable few-shot or zero-shot fraud detection.
Imagine detecting a new fraud pattern that wasn’t in the training data.
A pre-trained model that has generally learned “how transactions usually look” might pick up the anomaly better than a model trained only on past known frauds.</p>

<h2 id="weaknesses-3">Weaknesses</h2>

<p>The obvious downsides are resource intensity and complexity.
Training a transformer on billions of transactions is a monumental effort, requiring distributed training, specialized hardware (TPUs/GPUs), and careful tuning.
This is typically only within reach of large organizations or collaborations.
In production, serving a large transformer in real-time can be challenging due to model size and latency.
Transformers can have millions of parameters, and even if each inference is 50-100ms on a GPU, at very high transaction volumes (thousands per second) this could be costly or slow without scaling out.
Techniques like <a href="https://huggingface.co/docs/optimum/en/concept_guides/quantization">model quantization</a>, <a href="https://www.ibm.com/think/topics/knowledge-distillation">knowledge distillation</a>, or efficient transformer variants (e.g., <a href="https://huggingface.co/papers/2403.20041">Transformer Lite</a>) might be needed.</p>

<p>Another concern is explainability.
Even more so than a standard deep network, a giant foundation model is a black box.
Understanding its decisions requires advanced explainable AI methods, like interpreting attention weights or using SHAP on the embedding features, which is an active research area.
For regulated industries, one might still use a simpler surrogate model to justify decisions externally, while the transformer works under the hood.</p>

<p>Overfitting and concept drift are also concerns.
A foundation model might capture a lot of patterns, including some that are spurious or not causally related to fraud.
If fraudsters adapt, the model might need periodic re-training or fine-tuning with fresh data to unlearn outdated correlations.
For example, the Stripe model is self-supervised (no fraud labels in pre-training) which helps it generalize, but any discriminative fine-tuning on fraud labels will still need updating as fraud evolves.</p>

<h2 id="real-time-suitability-3">Real-Time Suitability</h2>

<p>Surprisingly, with the right engineering, even large transformers can be used in or near real-time.
For example, optimizing the embedding generation via GPU inference or caching mechanisms.
One strategy is to pre-compute embeddings for entities (like a card or user) so that only incremental computation is needed per new transaction.
Another strategy is two-stage scoring: use a smaller model to thin out events, then apply the heavy model to the most suspicious subset.
If real-time means sub-second (say &lt;500ms), a moderately sized transformer model on modern inference servers can fit that window, especially if batch processing a few transactions together to amortize overhead.
Cloud providers also offer accelerated inference endpoints (like AWS Inferentia chips or Azure’s ONNX runtime with GPU) to deploy large models with low latency.</p>

<p>That said, not every company will want to deploy a 100M+ parameter model for each transaction if a simpler model would do.
There is a trade-off between maximum accuracy and infrastructure cost/complexity.
In many cases, a foundation model could be used to periodically score accounts offline (to detect emerging fraud rings) and a simpler online model handle immediate decisions, combining their outputs.</p>

<h2 id="examples-3">Examples</h2>

<p>Use cases and research for transformers in fraud:</p>

<ul>
  <li><strong>Stripe’s Payments Foundation Model:</strong> A transformer-based model trained on billions of transactions, now used to embed transactions and feed into Stripe’s real-time fraud systems. It improved certain fraud detection rates from 59% to 97% and enabled detection of subtle sequential fraud patterns that were previously missed.</li>
  <li><strong>Tabular transformers:</strong> Studies like Chang et al.<sup id="fnref:18:1"><a href="#fn:18" class="footnote" rel="footnote" role="doc-noteref">22</a></sup> applied a transformer to the Kaggle credit card dataset and compared it to SVM, Random Forest, XGBoost, etc. The transformer achieved comparable or superior Precision/Recall, demonstrating that even on tabular data a transformer can learn effectively.</li>
  <li><strong>Sequence anomaly detection:</strong> Some works use transformers to model time series of transactions per account. A transformer may be trained to predict the next transaction features; if the actual next transaction diverges significantly, it could flag an anomaly. This is analogous to language model use (predict next word).</li>
  <li><strong>Cross-entity sequence modeling:</strong> Transformers can also encode sequences of transactions across entities, e.g., tracing a chain of transactions through intermediary accounts (useful in money laundering detection). The recent FraudGT model<sup id="fnref:27"><a href="#fn:27" class="footnote" rel="footnote" role="doc-noteref">24</a></sup> combines ideas of GNNs and transformer to handle transaction graphs with sequential relations.</li>
  <li><strong>Foundation models for documents and text in fraud:</strong> While not the focus here, note that transformers (BERT, GPT) are heavily used to detect fraud in textual data (e.g., scam emails, fraudulent insurance claims text, etc). In a holistic fraud system, a foundation model might take into account not just the structured transaction info but also any unstructured data, like customer input or messages, to make a decision.</li>
</ul>

<p>Transformer-based models and foundation models represent the frontier of fraud detection modeling.
They offer unparalleled modeling capacity and flexibility, at the cost of high complexity.
Early results, especially from industry leaders, indicate they can substantially raise the bar on fraud detection performance when deployed thoughtfully.
As these models become more accessible (with open-source frameworks and possibly smaller specialized versions), more fraud teams will likely adopt them, particularly for large-scale, multi-faceted fraud problems where simpler models hit a ceiling.</p>

<h1 id="appendix">Appendix</h1>

<h2 id="public-datasets">Public Datasets</h2>

<p>Research in fraud detection often relies on a few key <strong>public datasets</strong> to evaluate models, given that real financial data is usually proprietary.</p>

<p>Below I summarize some of the most commonly used datasets, along with their characteristics:</p>

<ul>
  <li>
    <p><a href="https://www.kaggle.com/datasets/mlg-ulb/creditcardfraud">:globe_with_meridians: Credit Card Fraud Detection (Kaggle, 2013)</a>: A classic dataset containing real European credit card transactions over two days. Its key characteristics are its extreme class imbalance (0.172% fraud) and anonymized features (28 PCA components), making it a standard benchmark for testing algorithms on imbalanced data.</p>
  </li>
  <li>
    <p><a href="https://www.kaggle.com/c/ieee-fraud-detection">:globe_with_meridians: IEEE-CIS Fraud Detection (Kaggle, 2019)</a>: A large, rich dataset from an e-commerce provider, released for a Kaggle competition. It features ~300 raw features (device info, card details, etc.), missing values, and a moderate imbalance (3.5% fraud). It is ideal for evaluating complex feature engineering and ensemble models for card-not-present fraud.</p>
  </li>
  <li>
    <p><a href="https://www.kaggle.com/datasets/ealaxi/paysim1">:globe_with_meridians: PaySim (Kaggle, 2016)</a>: A large-scale synthetic dataset that simulates mobile money transactions. It contains over 6 million transactions and is useful for testing model scalability in a controlled environment. Because it is synthetic, models may achieve unrealistically high performance.</p>
  </li>
  <li>
    <p><a href="https://www.kaggle.com/datasets/ellipticco/elliptic-data-set">:globe_with_meridians: Elliptic Bitcoin Dataset (Kaggle, 2019)</a>: A temporal graph of over 200,000 Bitcoin transactions, where nodes are transactions and edges represent fund flows. It is a key benchmark for evaluating graph-based fraud detection methods like GNNs. Only a small fraction of nodes are labeled as illicit, presenting a challenge.</p>
  </li>
</ul>

<p>⚠️ Due to high imbalance, accuracy is not informative (e.g., the credit card dataset has 99.8% non-fraud, so a trivial model gets 99.8% accuracy by predicting all non-fraud!). Hence, papers report metrics like AUC-ROC, Precision/Recall, or F1-score. For instance, on the Kaggle credit card data, an AUC-ROC around 0.95+ is achievable by top models, and PR AUC is much lower (since base fraud rate is 0.172%). In IEEE-CIS data, top models achieved about 0.92–0.94 AUC-ROC in the competition. PaySim being synthetic often yields extremely high AUC (sometimes &gt;0.99 for simple models) since patterns might be easier to learn. When evaluating on these sets, it’s crucial to use proper cross-validation or the given train/test splits to avoid overfitting (particularly an issue with the small Kaggle credit card data).</p>

<p>Overall, these datasets have driven a lot of research.
However, one should be cautious when extrapolating results from them to real-world performance.
Real production data can be more complex (concept drift, additional features, feedback loops).
Nonetheless, the above datasets provide valuable benchmarks to compare algorithms under controlled conditions.</p>

<h1 id="external-resources">External Resources</h1>

<ul>
  <li><a href="https://github.com/safe-graph/graph-fraud-detection-papers/"><i class="fab fa-github"></i></a> <a href="https://github.com/safe-graph/graph-fraud-detection-papers/">Awesome Graph Fraud Detection Papers</a></li>
  <li><a href="https://github.com/safe-graph/DGFraud"><i class="fab fa-github"></i></a> <a href="https://github.com/safe-graph/DGFraud">DGFraud: A Deep Graph-based Toolbox for Fraud Detection</a></li>
  <li><a href="https://github.com/junhongmit/FraudGT"><i class="fab fa-github"></i></a> <a href="https://github.com/junhongmit/FraudGT">FraudGT: A Simple, Effective, and Efficient Graph Transformer for Financial Fraud Detection</a></li>
</ul>

<h1 id="footnotes">Footnotes</h1>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>Oztas, Berkan, et al. “<em><a href="https://www.sciencedirect.com/science/article/pii/S0167739X24002607">Transaction monitoring in anti-money laundering: A qualitative analysis and points of view from industry.</a></em>” Future Generation Computer Systems (2024). <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>G. Praspaliauskas, V. Raman (2023). <em>“<a href="https://aws.amazon.com/blogs/machine-learning/real-time-fraud-detection-using-aws-serverless-and-machine-learning-services/">Real-time fraud detection using AWS serverless and machine learning services</a>.</em> AWS Machine Learning Blog – outlines a serverless architecture using Amazon Kinesis, Lambda, and Amazon Fraud Detector for near-real-time fraud prevention.” <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:34">
      <p>Desai, Ajit, Anneke Kosse, and Jacob Sharples. “<em><a href="https://www.sciencedirect.com/science/article/pii/S2405918825000157">Finding a needle in a haystack: a machine learning framework for anomaly detection in payment systems.</a>.</em>” The Journal of Finance and Data Science 11 (2025): 100163. <a href="#fnref:34" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:28">
      <p>R-ring collusion is a form of coordinated behavior where multiple accounts, potentially belonging to different individuals or groups, engage in fraudulent activities that benefit each other. <a href="#fnref:28" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:16">
      <p>For a Python library dedicated to handling imbalanced datasets and techniques, see <em><a href="https://imbalanced-learn.org/stable/">imbalanced-learn</a></em>, which provides tools for oversampling, undersampling, and more. <a href="#fnref:16" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:29">
      <p>Service Level Agreement (SLA) is a commitment between a service provider and a client that outlines the expected level of service, including performance metrics and response times. <a href="#fnref:29" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:17">
      <p>Afriyie, Jonathan Kwaku, et al. <em>“<a href="https://doi.org/10.1016/j.dajour.2023.100163">A supervised machine learning algorithm for detecting and predicting fraud in credit card transactions.</a></em> Decision Analytics Journal 6 (2023): 100163.” <a href="#fnref:17" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:19">
      <p>Onyeoma, Chidinma Faith, et al. “<em><a href="https://ieeexplore.ieee.org/abstract/document/10838456">Credit Card Fraud Detection Using Deep Neural Network with Shapley Additive Explanations</a>.</em>” 2024 International Conference on Frontiers of Information Technology (FIT). IEEE, 2024. <a href="#fnref:19" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:13">
      <p>Kandi, Kianeh, and Antonio García-Dopico. “<em><a href="https://www.mdpi.com/2504-4990/7/1/20">Enhancing Performance of Credit Card Model by Utilizing LSTM Networks and XGBoost Algorithms.</a></em>” Machine Learning and Knowledge Extraction 7.1 (2025): 20. <a href="#fnref:13" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:13:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a></p>
    </li>
    <li id="fn:33">
      <p>Cherif, Asma, et al. “<em><a href="https://www.sciencedirect.com/science/article/pii/S1319157824000922">Encoder–decoder graph neural network for credit card fraud detection.</a></em>” Journal of King Saud University-Computer and Information Sciences 36.3 (2024). <a href="#fnref:33" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:21">
      <p>Alshameri, Faleh, and Ran Xia. “<em><a href="https://www.sciopen.com/article/10.26599/BDMA.2023.9020035">An Evaluation of Variational Autoencoder in Credit Card Anomaly Detection</a>.</em>” Big Data Mining and Analytics (2024). <a href="#fnref:21" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:22">
      <p>Charitou, Charitos, Artur d’Avila Garcez, and Simo Dragicevic. “<em><a href="https://ieeexplore.ieee.org/document/9206844">Semi-supervised GANs for fraud detection</a>.</em>” 2020 International Joint Conference on Neural Networks (IJCNN). IEEE, 2020. <a href="#fnref:22" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:32">
      <p>Huang, Huajie, et al. “<em><a href="https://www.sciencedirect.com/science/article/abs/pii/S156849462400142X">Imbalanced credit card fraud detection data: A solution based on hybrid neural network and clustering-based undersampling technique.</a></em>” Applied Soft Computing 154 (2024). <a href="#fnref:32" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:24">
      <p>Branco, Bernardo, et al. “<em><a href="https://doi.org/10.1145/3394486.3403361">Interleaved sequence RNNs for fraud detection</a>.</em>” Proceedings of the 26th ACM SIGKDD international conference on knowledge discovery &amp; data mining. 2020. <a href="#fnref:24" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:25">
      <p>Masihullah, Shaik, et al. “<em><a href="https://link.springer.com/chapter/10.1007/978-3-031-14463-9_10">Identifying fraud rings using domain aware weighted community detection</a>.</em>” International Cross-Domain Conference for Machine Learning and Knowledge Extraction. Cham: Springer International Publishing, 2022. <a href="#fnref:25" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:35">
      <p>Boyapati, Mallika, and Ramazan Aygun. “<em><a href="https://www.sciencedirect.com/science/article/abs/pii/S0893608024008554">BalancerGNN: Balancer Graph Neural Networks for imbalanced datasets: A case study on fraud detection.</a>.</em>” Neural Networks 182 (2025): 106926. <a href="#fnref:35" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:23">
      <p>Motie, Soroor, and Bijan Raahemi. “<em><a href="https://doi.org/10.1016/j.eswa.2023.122156">Financial fraud detection using graph neural networks: A systematic review</a>.</em>” Expert Systems with Applications (2024). <a href="#fnref:23" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:30">
      <p>Shih, Yi-Cheng, et al. “<em><a href="https://www.sciencedirect.com/science/article/abs/pii/S0957417424020785">Fund transfer fraud detection: Analyzing irregular transactions and customer relationships with self-attention and graph neural networks.</a></em>” Expert Systems with Applications. 2025. <a href="#fnref:30" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:31">
      <p>Tong, Guoxiang, and Jieyu Shen. “<em><a href="https://www.sciencedirect.com/science/article/abs/pii/S1568494623010025">Financial transaction fraud detector based on imbalance learning and graph neural network.</a></em>” Applied Soft Computing 149 (2023): 110984. <a href="#fnref:31" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3">
      <p>Jian Zhang et al. (2022). <em>“<a href="https://aws.amazon.com/blogs/machine-learning/build-a-gnn-based-real-time-fraud-detection-solution-using-amazon-sagemaker-amazon-neptune-and-the-deep-graph-library/">Build a GNN-based real-time fraud detection solution using Amazon SageMaker, Amazon Neptune, and DGL</a>.</em> AWS ML Blog – explains how graph neural networks can be served in real-time for fraud detection, noting challenges in moving from batch to real-time GNN inference.” <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:3:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a> <a href="#fnref:3:2" class="reversefootnote" role="doc-backlink">&#8617;<sup>3</sup></a></p>
    </li>
    <li id="fn:4">
      <p>Summer Liu et al. (2024). <em>“<a href="https://developer.nvidia.com/blog/supercharging-fraud-detection-in-financial-services-with-graph-neural-networks/">Supercharging Fraud Detection in Financial Services with GNNs</a>.</em> NVIDIA Technical Blog.” <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:4:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a></p>
    </li>
    <li id="fn:18">
      <p>Yu, Chang, et al. “<em><a href="https://arxiv.org/pdf/2406.03733v2">Credit Card Fraud Detection Using Advanced Transformer Model</a>.</em>” 2024 IEEE International Conference on Metaverse Computing, Networking, and Applications (MetaCom). IEEE, 2024. <a href="#fnref:18" class="reversefootnote" role="doc-backlink">&#8617;</a> <a href="#fnref:18:1" class="reversefootnote" role="doc-backlink">&#8617;<sup>2</sup></a></p>
    </li>
    <li id="fn:26">
      <p>Krutikov, Sergei, et al. “<em><a href="https://arxiv.org/html/2405.13692v1">Challenging Gradient Boosted Decision Trees with Tabular Transformers for Fraud Detection at Booking.com</a>.</em>” arXiv preprint arXiv:2405.13692 (2024). <a href="#fnref:26" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:27">
      <p>Lin, Junhong, et al. “<em><a href="https://dl.acm.org/doi/abs/10.1145/3677052.3698648">FraudGT: A Simple, Effective, and Efficient Graph Transformer for Financial Fraud Detection</a>.</em>” Proceedings of the 5th ACM International Conference on AI in Finance. 2024. <a href="#fnref:27" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>César Soto Valero</name><email>cesarsotovalero@gmail.com</email></author><category term="ai" /><summary type="html"><![CDATA[Financial transaction fraud is a pervasive problem costing institutions and customers billions annually. This survey reviews the current state-of-the-art in real-time transaction fraud detection, spanning both academic research and industry adopted solutions.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.cesarsotovalero.net/img/posts/2025/2025-04-03/justitiabrunnen_cover.jpg" /><media:content medium="image" url="https://www.cesarsotovalero.net/img/posts/2025/2025-04-03/justitiabrunnen_cover.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Building GenAI Applications Today</title><link href="https://www.cesarsotovalero.net/blog/building-genai-applications-today.html" rel="alternate" type="text/html" title="Building GenAI Applications Today" /><published>2024-11-17T00:00:00-08:00</published><updated>2026-04-02T05:57:52-07:00</updated><id>https://www.cesarsotovalero.net/blog/building-genai-applications-today</id><content type="html" xml:base="https://www.cesarsotovalero.net/blog/building-genai-applications-today.html"><![CDATA[<figure class="badge"><a href="&lt;https://commons.wikimedia.org/wiki/File:NMA.0039209_Emigration._Svenskar_i_Amerika._Guldvaskare_vid_Black_Foots_River,_Montana.jpg&gt;"><img src="/img/badges/Svenskar_i_Amerika._Guldvaskare_vid_Black_Foots_River,_Montana.jpg" style="width:140px;max-width:100%;" alt="badge" /></a></figure>

<p>The AI fever has been around for a while now (4 years as I can count).
It reminds me of the <a href="https://en.wikipedia.org/wiki/Gold_rush">gold rush</a> and subsequent <a href="https://en.wikipedia.org/wiki/California_Dream">Californian Dream</a> from the 19th century.
The new “happy idea” is that today any single individual could get rich almost instantly by leveraging the novel AI’s-based capabilities the right way.
The potential opportunities to apply Generative AI (GenAI) for profit span almost all areas of development, from pure arts to fundamental science, from medical diagnosis to engineering, and so on.
<a href="https://en.wikipedia.org/wiki/Intelligent_agent">AI agents</a> can now <a href="https://www.sciencedirect.com/science/article/pii/S2352847823001557">generate</a> new scientific hypotheses.
Yet, as history seems to repeat itself, every time OpenAI releases a new model offering more powerful capabilities, many solo entrepreneurs and small startups flounder and fade.
For instance, I’ve attended many hackathons where teams have tried to build AI-powered solutions for problems that could be solved with a simple rule-based system.
There are many many reasons why GenAI projects fail, and less clear reasons (to me) of why some succeed.
So, if you’re a developer or entrepreneur itching to dive into the GenAI space for fun or profit, this post is my step back to reflect on what (I believe) are good fits for this technology and what <em>shouldn’t</em> be built with it.
Let’s dive in!</p>

<figure class="jb_picture">
  



<img width="100%" style="border: 0px solid #808080;" src="/assets/resized/twisted-revolver-640x373.jpg" alt="Time to stop shutting flies with AI powered bullets" data-srcset="/assets/resized/twisted-revolver-640x373.jpg 640w,/assets/resized/twisted-revolver-768x448.jpg 768w,/assets/resized/twisted-revolver-1024x597.jpg 1024w,/assets/resized/twisted-revolver-1366x797.jpg 1366w,/assets/resized/twisted-revolver-1600x933.jpg 1600w," class="blur-up lazyautosizes lazyload" />
  <figcaption class="stroke">
   &#169; Stop shutting flies with AI-powered bullets, sometimes just use flyswatter instead. Picture of a sculpture located in <a href="https://maps.app.goo.gl/9tWekqJTscsLmkgd9">Hötorgshallen 50</a>, in Stockholm city.
  </figcaption>
</figure>

<h1 id="what-seems-to-work">What Seems to Work</h1>

<p>I’m not an entrepreneur myself (yet), but I’m overall an enthusiast of the startup ecosystem. I’ve listened to the <a href="https://www.indiehackers.com/podcasts">Indie Hackers Podcasts</a>, read <a href="https://techcrunch.com/">TechCrunch</a>, and still check cool launches on <a href="https://gumroad.com">Gumroad</a> from time to time. Over the years, I’ve seen many startups succeed and fail along the way for diverse reasons. Some of them failed due to overhyped tech, unrealistic business models, or poor timing (e.g., Pets.com, Theranos, or Rdio).</p>

<p>Obviously, whenever there is a new technology, there is a wave of startups trying to profit from it.
Do you remember the blockchain hype 10 years ago?
I can’t forget about the many crypto millionaires that emerged from it (especially because I wasn’t one of them).</p>

<p>I’m now convinced that GenAI is a powerful tech (much broader than blockchain) that can <a href="./blog/surviving-the-ai-revolution-as-a-software-engineer">transform whole industries</a> and create new new ones.
GenAI will first optimize existing processes and then transform them.
First, everything related to handling paperwork and repetitive tasks is going to be completely automated (and that’s actually good because these boring tasks were not meant for humans in the first place anyway).
Second, GenAI creates new opportunities for everyone, including artists and creators, to express themselves in ways that were not possible before.
Third, GenAI helps us understand the world better by analyzing data in an unprecedented manner, generating new hypotheses, and planning new experiments, thus propelling the scientific discoveries that ultimately transform our lives for the better.</p>

<h2 id="success-factors">Success Factors</h2>

<p>The success of any GenAI project is extremelly hard to predict.
However, the ones that succeed share some common traits that set them apart from the noise.
Below are factors to keep in mind when evaluating whether an AI idea is worth your time and resources.</p>

<p><strong>Clear Value Proposition.</strong>
Before jumping into the development of any AI system, ask yourself: Does this app solve a real-world problem?
And is it doing so in a way that’s better, faster, or cheaper than existing solutions?
The best AI ideas are those that tackle well-defined issues, making people’s lives easier or helping businesses streamline operations.
Without a compelling value proposition, even the most advanced AI will struggle to gain traction.</p>

<p><strong>Scalability.</strong>
While niche markets can be tempting, they are often not the best starting point for an AI system unless you have a very clear, long-term vision.
Aim for ideas that can scale, reaching wider audiences or applying across various industries.
Scalability isn’t just about expanding your user base.
It’s about adapting the technology to different use cases, which will increase the likelihood of long-term success.</p>

<p><strong>Ethical Design.</strong>
Every AI system comes with a responsibility: to design it with ethics in mind. Consider the potential negative uses of your AI. While technologies like deepfake generators show immense creative potential, they also pose significant risks for misuse. It’s essential to build safeguards and establish ethical boundaries to prevent your AI from being used for harmful purposes.</p>

<h2 id="examples">Examples</h2>

<p>Below are three applications of GenAI today that I would like to see more of:</p>

<ol>
  <li>
    <p><strong>Personalized Financial Advisors:</strong> Apps that analyze spending habits, investment opportunities, and financial health using AI are gold. These tools cater to the rising demand for financial literacy and can scale personalized advice to millions (i.e., tangible ROI for users). For example, <a href="https://www.wealthfront.com/">Wealthfront</a>’s AI-driven financial planning features are transforming personal finance management.</p>
  </li>
  <li>
    <p><strong>Content Creation Tools:</strong> GenAI is a boon for creators. Platforms that assist with scriptwriting, graphic design, or even video editing are gaining more and more traction because they save time, amplify human creativity, and have clear market demand. For example, <a href="https://www.adobe.com/products/firefly">Adobe’s Firefly</a> enhances creativity by automating repetitive tasks.</p>
  </li>
  <li>
    <p><strong>AI in Healthcare:</strong> There’s a growing emphasis on preventive health solutions that are scalable and cost-effective. Tools like AI-powered symptom checkers or personalized fitness coaches empower users to manage their health better. For example, <a href="https://www.myfitnesspal.com/">MyFitnessPal</a> leverages AI for smarter diet recommendations.</p>
  </li>
</ol>

<p>While we are still in the early days of GenAI, it seems to me that what works the best is still to focus on solving real world problems in a scalable way.
If you’ve worked on a GenAI product, then you don’t need to be remembered that no matter the tech, is <em>the product</em> what matters the most.</p>

<h1 id="what-to-avoid">What to Avoid</h1>

<p>Most GenAI ideas are not worth pursuing.
Numerous AI-driven apps fail due to over-saturation and lack of resources to compete against the tech giants that already have customer trust and a large user base.
Other fail due to technical glitches, poor product pivots, or simply because they don’t solve a real problem.
There is another set that fail because of premature obsolescense (e.g., a friend built a now dead startup 4 years ago about simplifying email through summarization).
Failure is hard to predict when one truly believes in a proyect.</p>

<h2 id="lessons-from-ai-failures">Lessons from AI Failures</h2>

<p>Building a successful AI system isn’t just about using the best foundational models and feeding them with data.
It’s about learning from the mistakes of others.
A few notable failures provide crucial insights into what can go wrong, and what to avoid in the development process.
Below is a small compilation.</p>

<p><strong>Bias Is The Biggest Enemy.</strong>
One of the most dangerous pitfalls in AI is <a href="https://en.wikipedia.org/wiki/Algorithmic_bias">bias</a>, which can unintentionally emerge through data or algorithmic design.
GenAI systems that perpetuate bias can quickly damage a company’s reputation, especially in sensitive industries.
A classic example of this is <a href="https://en.wikipedia.org/wiki/Tay_(chatbot)">Microsoft’s Tay chatbot</a>, which was launched in 2016 to interact with users on Twitter.
Unfortunately, it was quickly hijacked by biased and offensive content due to insufficient safeguards.
Current AI systems must be designed with bias mitigation in mind, ensuring that they are fair and ethical.</p>

<p><strong>Privacy Is Non-Negotiable.</strong>
When it comes to AI, privacy is not a feature, it’s a fundamental requirement.
Especially in sectors like healthcare and finance, mishandling sensitive user data can lead to significant regulatory penalties and, perhaps worse, the loss of customer trust.
Companies must prioritize data protection, making sure that systems are secure and that users’ privacy is never compromised.
This includes adhering to global privacy standards like <a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation">GDPR</a> or <a href="https://oag.ca.gov/privacy/ccpa/regs">CCPA</a>, and being transparent about how user data is collected and used.</p>

<p><strong>Overpromising.</strong>
While the potential of AI can be captivating, it’s essential to remain grounded and transparent about the technology’s current capabilities.
Many companies have fallen into the trap of hyping up their AI solutions, only to disappoint users with results that fall short of expectations.
For example, <a href="https://www.businessinsider.com/healthcare-startup-forward-shutdown-carepod-adrian-aoun-2024-11">Forward Health</a> promised a futuristic healthcare experience with its CarePods but failed to deliver on its ambitious vision.
To avoid overpromising, companies should set realistic goals, communicate openly with users, and focus on incremental improvements rather than grandiose claims.</p>

<p>In addition to the primary lessons above, there are a few more pitfalls that commonly arise in AI development.
These may not always be immediately obvious but are equally critical in creating a successful AI application.</p>

<p><strong>Use GenAI When You Don’t Need It.</strong>
GenAI is incredibly powerful, but it’s not a one-size-fits-all solution. As the saying goes, “Not everything is a nail.” Sometimes, simpler solutions like linear programming or basic algorithms can solve the problem more efficiently and cost-effectively. For example, optimizing energy consumption with a basic schedule based on electricity prices is far more effective and cheaper than running the same data through a complex language model (as noted by AI expert Chip Huyen). Before opting for GenAI, carefully consider if it’s the best approach.</p>

<p><strong>Confusing Bad Product with Bad AI.</strong>
A common misconception is that poor user experiences or ineffective AI solutions are the result of faulty algorithms. In reality, many issues arise from poor product design or a lack of attention to the user interface (UI).
For example, a chatbot might function perfectly but fail to engage users simply because they don’t know how to interact with it.
A well-designed UX can transform even a mediocre AI into something genuinely useful.
<a href="https://www.intuit.com/intuitassist/">Intuit’s chatbot</a>, for instance, was able to enhance the overall experience through smart design choices, demonstrating that good design can elevate AI performance.</p>

<p><strong>Lack of Model Customization.</strong>
While pre-trained open-source models offer a quick and easy starting point, relying on them without tailoring them to your specific use case is a mistake.
Using these models without fine-tuning them is akin to trying to run a marathon in flip-flops.
It might work to some extent, but you’re not going to achieve optimal results.
Customizing and fine-tuning models allows them to meet the specific needs of your application, making the difference between an average app and one that delivers real value to users.</p>

<h2 id="examples-1">Examples</h2>

<p>Here are four GenAI development directions that I would advise against pursuing:</p>

<ol>
  <li>
    <p><strong>Overhyped General-Purpose Chatbots:</strong> Chatbots are ubiquitous, and most fail to differentiate themselves. Unless your bot is solving a specific problem better than existing solutions, it’s just another chatbot. For example, <a href="https://www.analyticsvidhya.com/blog/2023/01/top-5-failures-of-ai-till-date-reasons-solution/">Microsoft’s Tay chatbot</a> famously spiraled out of control due to poor moderation and biased training data.</p>
  </li>
  <li>
    <p><strong>Poorly Thought-Out Healthcare Applications:</strong> While healthcare is promising, it’s also highly regulated. Products that don’t comply with data protection laws or fail to address ethical concerns will face backlash. For example, <a href="https://www.businessinsider.com/healthcare-startup-forward-shutdown-carepod-adrian-aoun-2024-11">Forward Health’s CarePods</a> failed due to technical glitches and poor location choices.</p>
  </li>
  <li>
    <p><strong>Tools Targeting Hyper-Niche Markets:</strong> Niche markets often lack the scale needed to make an app profitable. AI tools for ultra-specific tasks, like “AI for knitting pattern generation,” may not justify the investment. For example, <a href="https://selldone.com/blog/major-startup-failures-2024-824">Tally’s credit management platform</a> collapsed due to limited scalability and poor product pivots.</p>
  </li>
</ol>

<p>In summary, avoid building AI apps that don’t solve a real problem, are overhyped, or target markets that are too small to scale.</p>

<h1 id="final-thoughts">Final Thoughts</h1>

<p>The AI gold rush is far from over. Many GenAI startups are rushing headfirst into this space, hoping to strike gold, but failing to dig deep enough to find the right nuggets. In the future, the winners will be those who embrace simplicity, scale, and ethics, while staying grounded in real-world needs. To make AI a lasting success, focus on creating products that truly solve problems for the user, rather than just jumping on the bandwagon. Who knows? Maybe your next AI project will end up as the one everyone’s talking about. Or maybe it will be yet another “failed startup” story.</p>

<p>You will never know until you try.</p>

<h1 id="external-resources">External Resources</h1>

<ul>
  <li><a href="https://www.analyticsvidhya.com/blog/2023/01/top-5-failures-of-ai-till-date-reasons-solution/">:globe_with_meridians: Analytics Vidhya: Top 5 AI Failures</a></li>
  <li><a href="https://www.businessinsider.com/healthcare-startup-forward-shutdown-carepod-adrian-aoun-2024-11">:globe_with_meridians: Business Insider: Inside Forward’s Failure</a></li>
  <li><a href="https://selldone.com/blog/major-startup-failures-2024-824">:globe_with_meridians: Selldone: Major Startup Failures 2024</a></li>
  <li><a href="https://huyenchip.com/2025/01/16/ai-engineering-pitfalls">:globe_with_meridians: Chip Huyen: Common pitfalls when building generative AI applications</a></li>
  <li><a href="https://huyenchip.com/llama-police">:globe_with_meridians: Chip Huyen: List of Open Source LLM Tools</a></li>
  <li><a href="https://cloud.google.com/transform/101-real-world-generative-ai-use-cases-from-industry-leaders">:globe_with_meridians: 101 Real-World Generative AI Use Cases from Industry Leaders</a></li>
</ul>]]></content><author><name>César Soto Valero</name><email>cesarsotovalero@gmail.com</email></author><category term="ai" /><summary type="html"><![CDATA[Generative AI has taken the world by storm, offering endless opportunities for innovation. But as with any new technology, there are plenty of pitfalls to avoid. In this post, I dive into the current state of AI startups, shedding light on what works, what doesn't, and why. If you're looking to build something with AI, let’s step back and reflect on where it makes sense to innovate and where it doesn't. From avoiding overhyped general-purpose chatbots to understanding the limitations of AI in niche markets, this post offers practical insights to help you navigate the AI hype more effectively.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.cesarsotovalero.net/img/posts/2024/2024-11-17/twisted-revolver_cover.jpg" /><media:content medium="image" url="https://www.cesarsotovalero.net/img/posts/2024/2024-11-17/twisted-revolver_cover.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Why GenAI Will NOT Replace Software Engineers</title><link href="https://www.cesarsotovalero.net/blog/why-genai-will-not-replace-software-engineers-just-yet.html" rel="alternate" type="text/html" title="Why GenAI Will NOT Replace Software Engineers" /><published>2024-08-19T00:00:00-07:00</published><updated>2026-04-02T05:57:52-07:00</updated><id>https://www.cesarsotovalero.net/blog/why-genai-will-not-replace-software-engineers-just-yet</id><content type="html" xml:base="https://www.cesarsotovalero.net/blog/why-genai-will-not-replace-software-engineers-just-yet.html"><![CDATA[<p>The field of Generative Artificial Intelligence (GenAI) has made incredible strides in recent years, particularly in <a href="https://ml4code.github.io/papers">source code analysis and generation</a>.
The excitement is well justified.
Today’s GenAI systems can perform common software development tasks more efficiently than many human engineers.</p>

<aside class="youtube">
        <a href="https://www.youtube.com/watch?v=kw7fvHf4rDw"><div class="box">
        <img src="https://i.ytimg.com/vi/kw7fvHf4rDw/mqdefault.jpg" alt="YouTube video #kw7fvHf4rDw" />
        <div class="play">
          <img src="/img/icons/youtube-play20px.svg" alt="Play Video" class="youtube-logo" />
        </div>
        </div></a>
        <div>Why AI will NOT replace Software Engineers (for now); 7 December 2024.</div></aside>

<p>I’ve experienced the power of this technology firsthand.
<a href="https://chatgpt.com/?model=gpt-4o">ChatGPT-4o</a> is truly impressive at fixing bugs, refactoring code, and even adding entirely new features to my software projects.
However, the excitement about speeding up software development isn’t universally shared across the industry.
Many developers are concerned that GenAI systems will soon replace them, rendering their skills obsolete.
With its powerful capabilities, GenAI seems poised to disrupt the software development job market, potentially taking over a wide range of engineering roles.</p>

<p>Over the past few months, I’ve attended several academic and <a href="https://www.linkedin.com/posts/cesarsotovalero_ai-sweden-activity-7255582747417571328-oGDN">industrial conferences</a> and read a plethora of <a href="https://ml4code.github.io/papers">research papers on this topic</a>.
Yet, even among world-class experts, there’s no clear consensus on where we’re headed.
Amid the hype, massive AI investments, and pervasive <a href="https://en.wikipedia.org/wiki/Fear_of_missing_out">fear of missing out</a>, confusion and uncertainty about AI’s current and future impact on the software industry abound.</p>

<p>In this blog post, I’ll share my perspective on the capabilities of today’s GenAI systems to see how they measure up to the demands of real-world software development.</p>

<p>Spoiler: I’m on the optimists’ side. So let’s dive in!</p>

<figure class="jb_picture">
  



<img width="100%" style="border: 0px solid #808080;" src="/assets/resized/armillary-sphere-640x373.jpg" alt="Photo of a garden sundial in the form of an armillary sphere in Skansen, Stockholm, Sweden." data-srcset="/assets/resized/armillary-sphere-640x373.jpg 640w,/assets/resized/armillary-sphere-768x448.jpg 768w,/assets/resized/armillary-sphere-1024x597.jpg 1024w,/assets/resized/armillary-sphere-1366x797.jpg 1366w,/assets/resized/armillary-sphere-1600x933.jpg 1600w," class="blur-up lazyautosizes lazyload" />
  <figcaption class="stroke">
    &#169; Since ancient times, we've tried to anticipate the future with certain levels of pessimism, and (ironically) we've been proven wrong most of the time. Photo of a garden sundial in the form of an <a href="https://en.wikipedia.org/wiki/Armillary_sphere">armillary sphere</a> in <a href="https://maps.app.goo.gl/61UCZPas6UNWWWS67">Skansen</a>, Stockholm, Sweden.
  </figcaption>
</figure>

<h1 id="behind-the-sensational-headlines">Behind The Sensational Headlines</h1>

<p>If you follow the latest <a href="https://tldr.tech/">tech newsletters</a> (as I do), you’ll find out that the media seems excited to push out sensational news declaring that GenAI is set to take over a wide range of software engineers jobs.<sup id="fnref:1"><a href="#fn:1" class="footnote" rel="footnote" role="doc-noteref">1</a></sup> For those who make a living <del>pretending to write</del> writing source code, headlines like <em>“<a href="https://brainhub.eu/library/software-developer-age-of-ai">Is There a Future for Software Engineers?</a>”</em> make it easy to feel like the ground is shifting beneath our feet.</p>

<p>The concern is understandable.
Advancements in <a href="https://link.springer.com/article/10.1007/s11042-022-13428-4">natural language processing (NLP)</a> have powered GenAI systems to tackle tasks once deemed uniquely human.
These tasks not only include writing code but also other hardcore software engineering activities, such as gathering requirements, creating documentation, and even identifying user needs.</p>

<p>Consequently, we’re starting to develop a deep fear on the possibility that our professional careers might soon be eclipsed by machines capable of churning out entire software applications faster than we can.<sup id="fnref:4"><a href="#fn:4" class="footnote" rel="footnote" role="doc-noteref">2</a></sup></p>

<p>So, if you work in tech, you’re probably wondering:</p>

<ol>
  <li>Is the career I’ve worked so hard to build at risk?</li>
  <li>Do GenAI tools truly grasp the complexities of a software project?</li>
  <li>Can GenAI tools interpret vague business requirements, understand user needs, and make informed trade-offs like a human engineer?</li>
</ol>

<h1 id="the-catch">The Catch</h1>

<p>The idea of AI kicking out software engineers is not new.
For instance, in 2022 an article in Nature Journal <a href="https://www.nature.com/articles/d41586-022-04383-z">predicted this very phenomenon</a>.
The authors let it clear that <a href="https://openai.com/index/chatgpt/">ChatGPT</a> and <a href="https://alphacode.deepmind.com/">AlphaCode</a> would replace software engineers in the coming years.
But let’s be honest, many companies have tried to automate software development in the past, <a href="https://www.monperrus.net/martin/startups-machine-learning-code">and most of them have failed</a>.<sup id="fnref:3"><a href="#fn:3" class="footnote" rel="footnote" role="doc-noteref">3</a></sup>
So, this kind of prediction is not new.</p>

<blockquote>
  <p>“Most AI startups and companies are primarily focused on creating <em>wrappers</em> around existing AI models without offering real value. I believe most of them will fail in the coming years.”</p>
</blockquote>

<p>Let’s consider the following scenario: a single person without any programming experience today can leverage an AI-powered IDE, like the popular <a href="https://www.cursor.com/">Cursor AI</a>, to develop a full mobile app for personal finance management.
All from scratch!</p>

<p>Impressive, right?</p>

<p>But there is a catch.
The developed app likely doesn’t introduce any groundbreaking innovation because its building blocks (libraries, APIs, and templates) already exist and are probably very well established in the market.
Current GenAI code assistants merely reassemble these components to fit a well-known specific use case.
While undeniably useful, tools like ChatGPT or GitHub Copilot still don’t have the ability to fully understand the context of a software project from the business, technical, and user perspectives.</p>

<p>This distinction is vital: GenAI excels at recombination of existing knowledge, but genuine <a href="https://en.wikipedia.org/wiki/Innovation">innovation</a> (i.e., the ability to transform abstract ideas into novel practical solutions that deliver unique value) requires more than that.<sup id="fnref:2"><a href="#fn:2" class="footnote" rel="footnote" role="doc-noteref">4</a></sup>
It demands a profound understanding of the problem domain, which includes grasping complex trade-offs, navigating edge cases, and adapting to evolving constraints.</p>

<p>Having diverse viewpoints and broad experience in managing a complex set of challenges is something that GenAI systems are still far from achieving.</p>

<h1 id="genai-isnt-replacing-engineers">GenAI Isn’t Replacing Engineers</h1>

<p>GenAI systems operate within well-defined boundaries set by the algorithms powering them and the data they’ve been trained on.
They’re astonishingly effective at recognizing patterns, generating text, and synthesizing existing information in seemingly novel ways.
However, when it comes to the messy, chaotic, and unpredictable world of software development, GenAI falls short in several key areas due to its inherent reasoning limitations.</p>

<figure class="jb_picture">
  



<img width="75%" style="border: 1px solid #808080;" src="/assets/resized/reciting-vs-reasoning-640x619.png" alt="TODO" data-srcset="/assets/resized/reciting-vs-reasoning-640x619.png 640w,/assets/resized/reciting-vs-reasoning-768x742.png 768w,/assets/resized/reciting-vs-reasoning-1024x990.png 1024w,/assets/resized/reciting-vs-reasoning-1366x1320.png 1366w,/assets/resized/reciting-vs-reasoning-1600x1547.png 1600w," class="blur-up lazyautosizes lazyload" />
  <figcaption class="stroke">
    Reciting vs. Reasoning: GenAI can find patterns quickly, but it can't reason like a human. Source: <a href="https://news.mit.edu/2024/reasoning-skills-large-language-models-often-overestimated-0711">MIT News</a>.
  </figcaption>
</figure>

<p>Here are some key limitations of GenAI in the context of software engineering:</p>

<ul>
  <li><strong>No Contextual Understanding:</strong> GenAI lacks the domain expertise and intuition required to assess edge cases or fully grasp the implications of design decisions.</li>
  <li><strong>No True Creativity:</strong> While GenAI can recombine existing elements in unexpected ways, its “creativity” stems from probabilistic reasoning, not from subjective experience, intentionality, or insight.</li>
  <li><strong>No Accountability:</strong> Software projects don’t live in isolation, they exist to solve real-world problems. An engineer must account for business needs, user behavior, and technical feasibility, which requires human judgment and responsibility.</li>
</ul>

<p>Let’s take software architecture as an example.
A GenAI model can suggest patterns or frameworks based on existing designs.
However, defining the architecture for a mission-critical system that has not been done before and for which there is not available training data is a task that requires human extrapolation (e.g., balancing scalability, performance, and security).
This is because <a href="./blog/what-does-it-take-to-become-a-software-architect">architecture</a> is not just about choosing the right tools or patterns, it’s about understanding the problem space, anticipating future needs, and making informed trade-offs decisions.</p>

<p>As I mentioned in a <a href="./blog/ai-doesnt-make-me-the-same-coffee">previous blog post</a>, GenAI powered tools are not a silver bullet.
They are exceptional at <em>augmentation</em> of human capabilities rather than outright replacement.
They can speed up repetitive tasks, such as generation of boilerplate code, fix well-known bugs or code smells, or doing basic refactorings, but lack the capacity for <em>strategic thinking</em> or <em>long-term planning</em>.
Besides, getting code written so easily feels somehow like cheating, which decreases the <em>perceived value</em> of the resulting output.</p>

<h1 id="the-essence-of-software-engineering">The Essence of Software Engineering</h1>

<p>At its core, software engineering is about understanding real-world needs and finding software solutions to them.
It’s about translating chaotic, ambiguous problems into clean, structured systems.
Software engineering is a job of high cognitive complexity because it not only requires technical skills but also a deep understanding of human behavior, business goals, and the broader context in which software operates.</p>

<p>For example, let’s say the goal is to build a mobile application for booking fitness classes.
One solution might be to create a simple interface that allows users to choose a class and reserve a spot.
But is that really enough?
What if users also want to see instructor profiles, class reviews, or even receive reminders based on their schedule?
Should it integrate with their fitness tracker or offer personalized class recommendations?
The possibilities are endless.</p>

<p>As in any other software project, the search space for building a useful app like the one mentioned before is vast.
The key point is that software engineers don’t just dive into coding without fully understanding the deeper needs of the user.
Humans have the ability to grasp context, consider variables like user preferences, time, convenience, and even emotions.
This is something machines can’t do without a clear direction from human insight.</p>

<p>And this is precisely where GenAI falls short.
Sure, GenAI can generate code, but it doesn’t truly <em>understand</em> the problem at hand.
It doesn’t think <em>as a human would do</em>, it lacks specific business context, and that make it fundamentally different.
It cannot sit down with stakeholders and clarify their needs.
It can’t challenge assumptions or deal with conflicting requirements.
All it can do is guess based on patterns it’s been trained on.
And often, that guess is way off the real needs of the users, because those needs are chaotic, and according to my experience, most users don’t really know what they want.</p>

<figure class="jb_picture">
  



<img width="75%" style="border: 1px solid #808080;" src="/assets/resized/jobs-quote-640x301.png" alt="People don't know what they want until you show it to them." data-srcset="/assets/resized/jobs-quote-640x301.png 640w,/assets/resized/jobs-quote-768x361.png 768w," class="blur-up lazyautosizes lazyload" />
  <figcaption class="stroke">
    Steve Jobs understood the importance of human creativity in software development.
  </figcaption>
</figure>

<p>In my opinion, this is where human engineers shine.
We understand nuance.
We can think creatively and adapt to changing environments.
And why does this matter?
Because the world needs critical thinking.
As long as humans remain as complex, messy creatures with changing needs, there will always be a demand for someone who can turn those messy needs into clean software solutions.</p>

<figure class="jb_picture">
  



<img width="50%" style="border: 1px solid #808080;" src="/assets/resized/ai-dumbness-example-640x1131.jpg" alt="Example of chatbot messing out with human context." data-srcset="/assets/resized/ai-dumbness-example-640x1131.jpg 640w,/assets/resized/ai-dumbness-example-768x1357.jpg 768w,/assets/resized/ai-dumbness-example-1024x1809.jpg 1024w," class="blur-up lazyautosizes lazyload" />
  <figcaption class="stroke">
    A dummy example showing the limitations of current GenAI in understanding context.
  </figcaption>
</figure>

<p>Even if we stop writing code ourselves and rely fully on natural language interfaces to generate it, the essence of software engineering will remain the same.
Source code is just the way we found to instruct computer so that they can solve human-centered problems.
Therefore, I believe we should focus on developing our problem-solving skills instead of writing code.
Our ability to define requirements and solve human problems (using AI systems) is going to be much more valuable in the coming years.</p>

<figure class="jb_picture">
  



<img width="100%" style="border: 0px solid #808080;" src="/assets/resized/requirements-engineering-in-the-age-of-ai-640x371.png" alt="TODO" data-srcset="/assets/resized/requirements-engineering-in-the-age-of-ai-640x371.png 640w,/assets/resized/requirements-engineering-in-the-age-of-ai-768x445.png 768w,/assets/resized/requirements-engineering-in-the-age-of-ai-1024x593.png 1024w,/assets/resized/requirements-engineering-in-the-age-of-ai-1366x791.png 1366w," class="blur-up lazyautosizes lazyload" />
  <figcaption class="stroke">
    Example of a loop of requirement engineering in the age of AI. Requirement engineers, stakeholder, engineers and domain experts cooperate with AI agents to define the requirements of a software project.
  </figcaption>
</figure>

<p>Now you might be thinking: What about the software engineering tasks that are more straightforward and annoying for humans? Could GenAI take over there?</p>

<h1 id="engineering-value-is-in-the-details">Engineering Value Is in the Details</h1>

<p>One of the biggest challenges in software development is that it’s error-prone at almost every step.
If you misunderstand a customer need, you end up solving the wrong problem.
Misinterpret a functional requirement, and you will end up designing a system that’s overkill, or worse, one that under-delivers.</p>

<p>For example, imagine you’re tasked with building a real-time messaging system for a customer support team handling thousands of chats per day concurrently.
The natural assumption might be to scale up with advanced <a href="./blog/design-for-microservices">microservices</a> and expensive infrastructure to ensure immediate response times.
But in the prototyping phase, a more practical solution could involve implementing a simple queuing system to handle chat overflow during peak hours, reducing the need for costly infrastructure while keeping the user experience intact.</p>

<figure class="jb_picture">
  



<img width="75%" style="border: 1px solid #808080;" src="/assets/resized/notification-system-architecture-640x475.jpg" alt="Example of a notification system architecture." data-srcset="/assets/resized/notification-system-architecture-640x475.jpg 640w,/assets/resized/notification-system-architecture-768x570.jpg 768w,/assets/resized/notification-system-architecture-1024x761.jpg 1024w," class="blur-up lazyautosizes lazyload" />
  <figcaption class="stroke">
    Example of a notification system architecture using a queuing system to handle peak hours. Source:
    <a href="https://www.cometchat.com/">CometChat</a>.
  </figcaption>
</figure>

<p>A GenAI powered engineer, on the other hand, would probably go with the more “typical” solution, like adding servers and microservices, because that’s how the majority of documentation resources to build these kinds of system explain how it’s done.
GenAI models doesn’t have the creativity to consider alternative options.
They follow the existing data, the information on what is has been trained on.
I foresee this as a potential problem in the future, because it could lead to a homogenization of software solutions, where everyone uses the same tools and the same approaches, and that’s not good for <a href="https://en.wikipedia.org/wiki/Software_diversity">software diversity</a> and innovation.</p>

<p>Personally, I’m seeing too much GenAI generated code in my own work and on GitHub these days.
The difference between a good solution and a great solution often comes down to human ingenuity, finding that less obvious, but more effective approach.
I believe the real magic in engineering happens in those transitions, from needs to requirements, from designs to code.
These steps are where human insight makes all the difference.</p>

<blockquote>
  <p>“My entire career working in AI/ML has shown me that domain knowledge is the highest leverage investment. Most of the tooling for AI/agents/etc. really is still in the <em>technical</em> realm, intended for AI/ML people or maybe engineers. But imagine a world where the domain expert had the agency to iterate and improve an AI, without having to go through an <em>AI engineer intermediary</em>.
That would open the door to create incredibly powerful AI systems quickly. Keep a close eye on the product they are building, because I think it will close this gap.” – <em><a href="https://www.linkedin.com/in/skylar-payne-766a1988/">Skylar Payne</a></em></p>
</blockquote>

<p>Of course, automating the boring parts is a great thing.
That’s what engineers have been doing since the early days, right?
However, today is even more important to look for smarter ways to solve problems, because GenAI might offer a set of solutions, but it’s our job to consider all the possibilities and make a critical evaluation of each of them.</p>

<p>So, what does this mean for the whole software industry, where things are constantly in flux? Can GenAI keep up with those changes?</p>

<h1 id="the-dynamic-nature-of-software-development">The Dynamic Nature of Software Development</h1>

<p>The concept of agile software development is all about flexibility.
This means adapting to change, whether that change comes from shifting market conditions, evolving user needs, or even new regulatory requirements.
It’s a fast-paced, ever-changing process, and that exactly where current GenAI systems based on Large Language Models (LLMs) struggle the most.</p>

<figure class="jb_picture">
  



<img width="75%" style="border: 1px solid #808080;" src="/assets/resized/agile-software-development-640x389.png" alt="TODO" data-srcset="/assets/resized/agile-software-development-640x389.png 640w,/assets/resized/agile-software-development-768x466.png 768w,/assets/resized/agile-software-development-1024x622.png 1024w,/assets/resized/agile-software-development-1366x829.png 1366w," class="blur-up lazyautosizes lazyload" />
  <figcaption class="stroke">
    Agile software development is all about adapting to change.
  </figcaption>
</figure>

<p>For example, imagine you are midway through a project when suddenly, the market shifts. The client wants to pivot.
You need to revise your priorities and adjust the entire architecture of your software. Can a GenAI system do that? Yes, it can!
But what if the conditions are completely new because of an event that has never ever happened before? For example, during a market crash or a natural disaster.
It’s hard for me to imagine a GenAI system that can adapt to those kinds of unpredictable situations.</p>

<p>AI operates based on past data and predefined assumptions.
It’s not going to sit in a meeting with you, hear the client’s concerns, and propose a totally new approach.
But part of our work is finding out what the client really needs, even when they don’t know it themselves!</p>

<p>Now, don’t get me wrong.
Current GenAI can assist in agile development, especially when it comes to routine tasks like generating boilerplate code or testing.
But when it comes to adapting to new, unexpected demands, they show some limitations.</p>

<p>AI will not replace programmers but will fundamentally change the development landscape, making human creativity and problem-solving capabilities more essential than ever.
Adaptability is one of the most valuable skills in software engineering.
As long as the world keeps changing, there will always be a need for people who can pivot quickly and come up with creative solutions.</p>

<p>Now, let’s take a closer look at the situations where GenAI actually excels in software engineering, and how we can leverage that.</p>

<h1 id="where-genai-can-actually-help">Where GenAI Can Actually Help</h1>

<p>Take code reviews, for example.
I think it’s a very tedious but necessary task.
You’ve got to sift through line after line of someone else’s work, looking for bugs, performance issues, or security vulnerabilities.
<a href="https://en.wikipedia.org/wiki/Intelligent_agent">GenAI agents</a>, like those integrated on GitHub or other platforms to review pull-requests, can help automate parts of this process, flagging potential issues before a human ever gets involved.</p>

<p>AI can help with tasks that are repetitive, like generating boilerplate code, adding unit tests, or writing documentation.
It can analyze large datasets and suggest optimizations.
Indeed, many surveys suggest that developers are eager to get away from these tasks to GenAI assistants.</p>

<p>But when it comes to making the final call, the human touch is still essential.</p>

<blockquote>
  <p>“With LLMs, coding won’t be enough to differentiate as an engineer, you’ll need to think about the product, business KPIs, strategy etc.
You need to think about solutions to problems, not software tools.
And PMs are going to be expected to get more technical.
Nothing is stopping them now, LLMs help you code.
They will need to use their product knowledge and combine it with technicals to give engineers more tangible instructions.
Those sound pretty similar, and the result will be less fragmentation and miscommunication between PMs and engineers, something that’s far too common today.
They’ll start speaking more similar languages.”</p>
</blockquote>

<p>In my opinion, the future isn’t about GenAI taking over but about humans learning how to work alongside GenAI to be more efficient and effective. And the engineers who can collaborate with GenAI agents (or whatever we’ll call them) will be the ones who thrive in the future.</p>

<figure class="jb_picture">
  



<img width="100%" style="border: 1px solid #808080;" src="/assets/resized/stackoverflow-dev-survey-2024-ai-developer-tools-ai-complex-social-640x296.png" alt="Stack Overflow Developer Survey 2024 results on the perceived ability of AI tools to handle complex tasks." data-srcset="/assets/resized/stackoverflow-dev-survey-2024-ai-developer-tools-ai-complex-social-640x296.png 640w,/assets/resized/stackoverflow-dev-survey-2024-ai-developer-tools-ai-complex-social-768x355.png 768w,/assets/resized/stackoverflow-dev-survey-2024-ai-developer-tools-ai-complex-social-1024x474.png 1024w,/assets/resized/stackoverflow-dev-survey-2024-ai-developer-tools-ai-complex-social-1366x632.png 1366w,/assets/resized/stackoverflow-dev-survey-2024-ai-developer-tools-ai-complex-social-1600x740.png 1600w,/assets/resized/stackoverflow-dev-survey-2024-ai-developer-tools-ai-complex-social-1920x888.png 1920w," class="blur-up lazyautosizes lazyload" />
  <figcaption class="stroke">
    Stack Overflow Developer Survey 2024 results on the perceived ability of AI tools to handle complex tasks.
  </figcaption>
</figure>

<p>We should integrate GenAI into our workflows where it makes sense, use it to handle the boring stuff, so we can focus on the creative and complex tasks.</p>

<p>And I know what you may be thinking, if GenAI can assist in low level tasks, that means it could eventually replace less experienced engineers. Isn’t it?</p>

<h1 id="can-genai-replace-entry-level-engineers">Can GenAI Replace Entry Level Engineers?</h1>

<p>Well, the answer is… yes/maybe.</p>

<p>But…</p>

<p>Less experienced engineers are not just doing the easy tasks.
They’re learning, growing professionally, and building the experience they need to tackle more complex problems down the road.
GenAI might be able to take over some of the simpler tasks, but it can’t replace the learning process.</p>

<p>For example, if you have been around for a while in this industry, think about how much you learned from your first projects.
It wasn’t just about writing down the code (programmers are not typists).
It was about understanding the problem, communicating with other developers, figuring out how to structure the solution, and dealing with unexpected bugs along the way.</p>

<p>If we remove the possibility of learning that stuff, and put GenAI systems in place, then it’ll be harder for the young people to gain that kind of experience or develop the necessary intuition to solve real-world problems.</p>

<p>What should we do then?
Well… if you’re an entry level engineer, your goal should not be to compete with AI.
It should be to delegate the tasks that GenAI can efficiently handle, and moving on to the bigger, more interesting challenges.</p>

<p>So, keep pushing yourself.
Don’t get too comfortable with the easy tasks and just delegate those to GenAI systems.
Focus on learning <a href="./blog/surviving-the-ai-revolution-as-a-software-engineer">the skills</a> that only humans can master.</p>

<h1 id="final-thoughts">Final Thoughts</h1>

<p>As software <del>developers</del> creators, the key is to think critically about where GenAI adds value and where it falls short.
Instead of viewing GenAI as a <em>competitor</em>, we should treat it as a <em>collaborator</em>.
By leveraging GenAI for grunt work, we can focus on higher-value tasks, such as crafting innovative designs, optimizing user experiences, and solving problems that GenAI simply cannot understand at a conceptual level (i.e., leveraging <em>the human experience</em>).</p>

<figure class="jb_picture">
  



<img width="100%" style="border: 1px solid #808080;" src="/assets/resized/stackoverflow-dev-survey-2024-ai-efficacy-and-ethics-ai-threat-social-640x248.png" alt="Stack Overflow Developer Survey 2024: Are AI tools a threat to software engineers?" data-srcset="/assets/resized/stackoverflow-dev-survey-2024-ai-efficacy-and-ethics-ai-threat-social-640x248.png 640w,/assets/resized/stackoverflow-dev-survey-2024-ai-efficacy-and-ethics-ai-threat-social-768x298.png 768w,/assets/resized/stackoverflow-dev-survey-2024-ai-efficacy-and-ethics-ai-threat-social-1024x397.png 1024w,/assets/resized/stackoverflow-dev-survey-2024-ai-efficacy-and-ethics-ai-threat-social-1366x529.png 1366w,/assets/resized/stackoverflow-dev-survey-2024-ai-efficacy-and-ethics-ai-threat-social-1600x620.png 1600w,/assets/resized/stackoverflow-dev-survey-2024-ai-efficacy-and-ethics-ai-threat-social-1920x744.png 1920w," class="blur-up lazyautosizes lazyload" />
  <figcaption class="stroke">
    Stack Overflow Developer Survey 2024 results about the perceived threat of AI tools for software engineers.
  </figcaption>
</figure>

<p>The truth is, your skills remain highly relevant, so long as you <em>adapt</em> to the new order.
The engineers who thrive in this new landscape will be those who augment their capabilities with GenAI while continuing to bring their unique human creativity, expertise, and empathy to the table.</p>

<p>In conclusion, I don’t think GenAI is going to replace software engineers (at least not anytime soon).
And sure, it can handle repetitive tasks and assist in certain areas, but the real value coming from software development still requires human intuition, creativity, and problem-solving.</p>

<p>Because, let me say it again, software development isn’t just about writing code, it’s about solving problems.
These problems are often really messy, poorly defined, and which solutions are deeply tied to human intuition and creativity.
The main task of engineers is to balance conflicting priorities, adapt to ever-changing environments, and navigate the ambiguous waters between business needs and technical solutions.</p>

<p>If there is one way to keep our pockets safe, I think it is to stay ahead of the curve.</p>

<h1 id="footnotes">Footnotes</h1>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:1">
      <p>This is understandable once we realize that the traditional media is in the business of creating sensational headlines and monetizing our attention rather than providing accurate information. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:4">
      <p>Not to mention the negative impact of AI related layouts on our <del>highly overvalued</del> software engineering pockets. <a href="#fnref:4" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:3">
      <p>I still remember Dreamweaver from <a href="https://en.wikipedia.org/wiki/Macromedia">Macromedia</a> (later purchased by Adobe). It was a no code app that promised to make web development just a matter of throwing components to a canvas. It didn’t work out as expected, and today we are still writing HTML, CSS and JavaScript, ouch! <a href="#fnref:3" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:2">
      <p>Billings, Jay Jay, et al. 2017 “Will humans even write code in 2040 and what would that mean for extreme heterogeneity in computing?”, <a href="https://arxiv.org/pdf/1712.00676">in arXiv</a>. <a href="#fnref:2" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>César Soto Valero</name><email>cesarsotovalero@gmail.com</email></author><category term="ai" /><summary type="html"><![CDATA[GenAI systems are becoming more and more capable of performing complex cognitive tasks that were once thought to be uniquely human. In particular, LLMs are proven to be very good at writing code. With all the buzz around GenAI replacing software engineers, are our jobs really at risk? In this article, I dive deep into AI's current capabilities and how they stack up against the demands of real-world software development. I discuss the actual potential of GenAI to assist in common development activities, and where it still falls short when it comes to creative problem solving and human intuition.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://www.cesarsotovalero.net/img/posts/2024/2024-08-19/armillary-sphere_cover.jpg" /><media:content medium="image" url="https://www.cesarsotovalero.net/img/posts/2024/2024-08-19/armillary-sphere_cover.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>