<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: whaiman</title>
    <description>The latest articles on DEV Community by whaiman (@whaiman).</description>
    <link>https://dev.to/whaiman</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4079356%2Fb499c22f-66fc-4043-8e0f-6cf494961264.jpg</url>
      <title>DEV Community: whaiman</title>
      <link>https://dev.to/whaiman</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/whaiman"/>
    <language>en</language>
    <item>
      <title>Why Next.js Feels Like Flask, Rewritten in TypeScript</title>
      <dc:creator>whaiman</dc:creator>
      <pubDate>Sun, 30 Aug 2026 10:59:04 +0000</pubDate>
      <link>https://dev.to/whaiman/why-nextjs-feels-like-flask-rewritten-in-typescript-1g1b</link>
      <guid>https://dev.to/whaiman/why-nextjs-feels-like-flask-rewritten-in-typescript-1g1b</guid>
      <description>&lt;p&gt;I spent over a year writing backend code in Flask. When I started a new project, I decided to try Next.js. The first thing I noticed was déjà vu. This felt like Flask, rewritten in TypeScript with a newer take on rendering.&lt;/p&gt;

&lt;p&gt;React itself works well for me - a tidy component library, nothing more. Next.js is what I fell for, the App Router most of all. The deeper I dig, the more it looks like a classic server-side framework, with extras Flask would never build on its own.&lt;/p&gt;

&lt;p&gt;My Flask experience isn't from tutorials. I built &lt;a href="https://github.com/whaiman/FileServer" rel="noopener noreferrer"&gt;FileServer&lt;/a&gt; on it (the project is moving to FastAPI now, since the old code stopped scaling). That work is what showed me how much these two tools overlap in concept.&lt;/p&gt;

&lt;h2&gt;
  
  
  Templating and layouts
&lt;/h2&gt;

&lt;p&gt;In Flask with Jinja, you build a &lt;code&gt;base.html&lt;/code&gt; with a &lt;code&gt;{% block content %}&lt;/code&gt; block, and &lt;code&gt;users.html&lt;/code&gt; extends that template. Header, footer, navigation - written once, not copied on every page.&lt;/p&gt;

&lt;p&gt;Next.js does the same job with &lt;code&gt;layout.tsx&lt;/code&gt;. You set it at the folder level, and every page inside wraps in it, no extra code needed. App Router lets you nest layouts at any depth - &lt;code&gt;/admin&lt;/code&gt; gets its own layout on top of the shared one. Flask can do this too, through several levels of &lt;code&gt;{% extends %}&lt;/code&gt;, but by hand it feels less clean.&lt;/p&gt;

&lt;h2&gt;
  
  
  Server rendering by default
&lt;/h2&gt;

&lt;p&gt;My first guess: TSX is Jinja with a different syntax. Close, but not exact.&lt;/p&gt;

&lt;p&gt;A component in Next.js defaults to a &lt;strong&gt;Server Component&lt;/strong&gt;. It renders on the server, and the browser gets finished HTML, the same way it does after Jinja.&lt;/p&gt;

&lt;p&gt;A component turns client-side only when you add &lt;code&gt;'use client'&lt;/code&gt; at the top of the file. That's the real parallel with Flask. Flask never gives you the choice - everything runs on the server. Next.js gives you the choice, and you can mix server markup with interactive pieces in the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data fetching
&lt;/h2&gt;

&lt;p&gt;In Flask, you write the data-fetching logic right in the route, then pass it to the template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/users&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;users&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_all_users&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;users.html&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Next.js, Server Components let you do the same thing inside the component itself, with no extra API endpoint to build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app/users/page.tsx&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UsersPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findMany&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ul&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It still feels like writing backend code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamic routes
&lt;/h2&gt;

&lt;p&gt;Flask: &lt;code&gt;@app.route('/user/&amp;lt;int:user_id&amp;gt;')&lt;/code&gt;, and &lt;code&gt;user_id&lt;/code&gt; arrives in the function as an argument.&lt;/p&gt;

&lt;p&gt;Next.js uses the same idea with square brackets in the folder name: &lt;code&gt;app/user/[id]/page.tsx&lt;/code&gt;, and you pull it out as &lt;code&gt;params.id&lt;/code&gt;. Different syntax, same idea - a piece of the URL becomes a variable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Form handling
&lt;/h2&gt;

&lt;p&gt;In Flask, you build a form, set &lt;code&gt;method="POST"&lt;/code&gt;, and catch the data on the backend through &lt;code&gt;request.form&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next.js added &lt;strong&gt;Server Actions&lt;/strong&gt;. You write an async function on the server and attach it to the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; tag with no extra wiring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createUser&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Create&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's the same old POST request, packaged in newer syntax. You don't need a separate API route for simple mutations.&lt;/p&gt;

&lt;h2&gt;
  
  
  API and HTML in one app
&lt;/h2&gt;

&lt;p&gt;A single Flask app holds &lt;code&gt;/users&lt;/code&gt;, which renders HTML, next to &lt;code&gt;/api/users&lt;/code&gt;, which runs &lt;code&gt;jsonify(...)&lt;/code&gt; and returns plain JSON. One server, two kinds of response.&lt;/p&gt;

&lt;p&gt;Next.js works the same way: &lt;code&gt;app/users/page.tsx&lt;/code&gt; returns a JSX page, and the neighboring &lt;code&gt;app/api/users/route.ts&lt;/code&gt; returns JSON. This is also how I plan to build the FastAPI backend for FileServer - API on one side, frontend on the other, an idea I picked up from the Flask version of the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Request lifecycle: middleware
&lt;/h2&gt;

&lt;p&gt;Who sees a request before it reaches your code? In Flask, &lt;code&gt;@app.before_request&lt;/code&gt; handles that - a function that runs before every route. Useful for checking auth or logging.&lt;/p&gt;

&lt;p&gt;Next.js has &lt;code&gt;middleware.ts&lt;/code&gt; at the project root for the same job. It catches the request before it reaches the page, and that's where you handle redirects or check a cookie. It matches &lt;code&gt;before_request&lt;/code&gt; almost exactly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Grouping routes
&lt;/h2&gt;

&lt;p&gt;Next.js has Route Groups - a folder in parentheses like &lt;code&gt;(auth)&lt;/code&gt; that groups routes logically without showing up in the URL.&lt;/p&gt;

&lt;p&gt;Flask solves the same problem with Blueprints: move the auth routes into one blueprint, the admin routes into another, then register both on the main app. The mechanics differ, but the motivation matches - don't dump every route into one flat pile.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caching and static files
&lt;/h2&gt;

&lt;p&gt;In Flask, to keep from hitting the database on every request, you add a &lt;code&gt;@cache.cached(timeout=50)&lt;/code&gt; decorator.&lt;/p&gt;

&lt;p&gt;Next.js builds this into the core, under the name ISR (Incremental Static Regeneration). You export a config variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;revalidate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Redirects and request context
&lt;/h2&gt;

&lt;p&gt;In Flask, you use &lt;code&gt;redirect(url_for('login'))&lt;/code&gt; to send a user elsewhere. For headers, you import the global &lt;code&gt;request&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;In Next.js server code, you do the same thing through dedicated functions instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;redirect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/navigation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next/headers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;People often file Next.js under "yet another SPA framework." Under the hood, App Router runs a solid server framework that does what Flask does, then lets you drop in client-side JavaScript exactly where you need it. If you know Flask well, Next.js won't take long to pick up. The concepts stay the same. Only the syntax changed.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>flask</category>
      <category>react</category>
    </item>
    <item>
      <title>Why the "AI" Badge Doesn't Matter and How to Restore Trust in Our Code</title>
      <dc:creator>whaiman</dc:creator>
      <pubDate>Sun, 16 Aug 2026 12:01:43 +0000</pubDate>
      <link>https://dev.to/whaiman/why-the-ai-badge-doesnt-matter-and-how-to-restore-trust-in-our-code-16ia</link>
      <guid>https://dev.to/whaiman/why-the-ai-badge-doesnt-matter-and-how-to-restore-trust-in-our-code-16ia</guid>
      <description>&lt;p&gt;Hello, DEV community! 👋&lt;/p&gt;

&lt;p&gt;This is my first post here. I've been reading articles on this platform for a while, but a recent article by &lt;a class="mentioned-user" href="https://dev.to/pascal_cescato_692b7a8a20"&gt;@pascal_cescato_692b7a8a20&lt;/a&gt; - &lt;a href="https://dev.to/pascal_cescato_692b7a8a20/the-ai-badge-doesnt-measure-what-you-think-it-does-3ne9"&gt;"The "AI" Badge Doesn't Measure What You Think It Does"&lt;/a&gt; - finally pushed me to join the conversation.&lt;/p&gt;

&lt;p&gt;It touches on a topic that's been on my mind for a while: the "AI-generated" badge, which is being actively pushed across media platforms, doesn't actually tell us anything useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  Whose thought is it anyway?
&lt;/h2&gt;

&lt;p&gt;As Pascal brilliantly breaks down, we constantly conflate three fundamentally different ways of working:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assisted. When the idea, structure, and artificial decisions belong entirely to the human. A model is used to rephrase, translate, fix regex, or pressure-test arguments.&lt;/li&gt;
&lt;li&gt;Generated. A human gives a prompt, but the model produces the actual thought and text.&lt;/li&gt;
&lt;li&gt;Produced. Automated pipelines generating content at an industrial scale without human oversight.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A binary badge (or statistical watermark) treats all three identically. An engineer who spends hours architecting a system, writing core logic, and using LLM to polish a comment or write boilerplate get shoved into the same bucket as an automated spam farm.&lt;br&gt;
The real line of ownership isn't drawn by how much a model intervened, but by who kept their hand on the decisions. Who designed the solution? Who discarded the bad outputs? Who took responsibility for the final architecture?&lt;/p&gt;




&lt;h2&gt;
  
  
  Proof of work instead of broken labels
&lt;/h2&gt;

&lt;p&gt;Detectors and badges look at the final shape of a file and guess its origin. But as Pascal proved by running his 2021 pre-LLM article through ZeroGPT (getting scores ranging from 97% to 8.6%), detectors merely chase stylistic neutrality, not actual origin.&lt;/p&gt;

&lt;p&gt;We are seeing the exact same flaw happening in programming. Today, there are various code analyzers and enterprise tools designed to detect "AI-generated code". While writing software is fundamentally different from writing prose, the detection methodologies share the exact same underlying bias.&lt;/p&gt;

&lt;p&gt;Just as text detectors flag well-structured, pedagogical paragraphs as "AI," code detectors often flag clean, canonical, and boilerplate code. If an engineer writes a textbook algorithm, strictly follows naming conventions, or implements standard design patterns, they risk being falsely flagged. The detector mistakes professional standardization for machine generation. It judges the final shape without knowing the process.&lt;/p&gt;

&lt;p&gt;This exact problem led me to create &lt;strong&gt;&lt;a href="https://github.com/whaiman/dev-ledger" rel="noopener noreferrer"&gt;dev-ledger&lt;/a&gt;&lt;/strong&gt; - an open-source VS Code extension designed as a conceptual evolution of time-trackers like WakaTime.&lt;/p&gt;

&lt;p&gt;But unlike traditional trackers that send your activity to a third-party server, dev-ledger is 100% local-first. All data collection and behavioral analysis happen directly on your machine - no APIs, no telemetry, no cloud. To guarantee the authenticity of your timeline, it secures your local event log with a cryptographic hash chain. It gives you a mathematically tamper-proof "Proof of Work" that you fully control, without sacrificing your privacy.&lt;/p&gt;

&lt;p&gt;Instead of slapping a meaningless "100% Human" or "AI Generated" badge on a project, this approach shifts the focus back to transparency. By unobtrusively recording keystrokes, active editing time, and structural modifications, it documents the actual process - the iterations, the refactoring, and the human effort behind a codebase. It then converts this data into automated reports and visual SVGs for your README.md, backed by an integrity hash.&lt;/p&gt;

&lt;p&gt;When someone looks at a repository equipped with this kind of timeline, they don't have to blindly trust a flawed detector or guess the code's origin based on its "final shape." They can actually see the chain of decisions and the problem-solving process that went into building it.&lt;br&gt;
We probably can't stop platforms from implementing broken AI detectors or pushing binary badges. But as engineers, we can choose to shift the conversation back to what actually matters: the depth of our thought and the transparency of our work.&lt;/p&gt;

&lt;p&gt;If you share this philosophy and want to secure the integrity of your own coding sessions, you can check out the source code and try the extension here:&lt;br&gt;
👉 &lt;a href="https://github.com/whaiman/dev-ledger" rel="noopener noreferrer"&gt;github.com/whaiman/dev-ledger&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Feedback, GitHub stars, and PRs are deeply appreciated!)&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What Metrics Cannot Fix
&lt;/h2&gt;

&lt;p&gt;Building a cryptographic ledger for development activity solves the technical problem of proving effort, but it doesn't fix the social one.&lt;br&gt;
Having a verified hash chain won't stop a cynical reviewer on Reddit or Hacker News from glancing at a clean, well-structured repository and dismissing it as "LLM slop." Just as in writing, the technical demonstration and the social judgment are two completely different things. Often, the reflexive rejection of code as "AI-generated" has nothing to do with the code itself - it’s just a blunt reaction to the anxiety of our industry's current transition.&lt;/p&gt;

&lt;p&gt;If badges can't measure origin, and enterprise code detectors can't measure thought, then the question in modern software engineering isn't "who typed this line?" but "who holds the steering wheel?"&lt;/p&gt;

&lt;p&gt;Software architecture is fundamentally about making trade-offs, not just generating syntax. An LLM might help write a boilerplate function or figure out a tricky regex, but it doesn't conceptualize a local-first system. It doesn’t weigh the security implications of a cryptographic hash chain against a centralized database. The engineering intent - the real heavy lifting - remains entirely human.&lt;/p&gt;

&lt;p&gt;When we reduce a developer's contribution to a binary label, we erase the unseen hours spent designing data models, debugging AI hallucinations, and rejecting bad abstractions. We erase the actual engineering.&lt;/p&gt;

&lt;p&gt;Code analyzers and enterprise detectors will only ever scan the final repository. They parse the Abstract Syntax Tree (AST) looking for generic patterns, completely blind to the iterative struggle that brought that structure into existence. But true ownership of software isn't found in the finalized syntax. It lives in the problem-solving, the discarded ideas, and the chain of architectural choices that made that code necessary in the first place.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>discuss</category>
      <category>opensource</category>
      <category>writing</category>
    </item>
  </channel>
</rss>
