<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[CodeX - Medium]]></title>
        <description><![CDATA[Everything connected with Tech &amp; Code. Follow to join our 1M+ monthly readers - Medium]]></description>
        <link>https://medium.com/codex?source=rss----29038077e4c6---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>CodeX - Medium</title>
            <link>https://medium.com/codex?source=rss----29038077e4c6---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 11 Oct 2026 09:17:42 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/codex" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Website vs Web Application: What’s the Difference?]]></title>
            <link>https://medium.com/codex/website-vs-web-application-whats-the-difference-2df3545fda3c?source=rss----29038077e4c6---4</link>
            <guid isPermaLink="false">https://medium.com/p/2df3545fda3c</guid>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[website]]></category>
            <category><![CDATA[wordpress]]></category>
            <category><![CDATA[custom-web-development]]></category>
            <category><![CDATA[web-applications]]></category>
            <dc:creator><![CDATA[Wathsala Kg]]></dc:creator>
            <pubDate>Sat, 10 Oct 2026 21:26:01 GMT</pubDate>
            <atom:updated>2026-10-10T21:26:01.518Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="Website vs Web Application" src="https://cdn-images-1.medium.com/max/1000/1*KGau2uoZf8nBKaADB-uSig.jpeg" /><figcaption>Website vs Web Application</figcaption></figure><p>When planning a new digital project, one of the first questions you may have is: <strong>Website vs. Web Application — what’s the difference?</strong></p><p>Although websites and web applications both run through a web browser, they are designed for different purposes. Understanding the difference can help you choose the right solution for your business.</p><h3>What Is a Website?</h3><p>A website is primarily designed to <strong>provide information or content to visitors</strong>.</p><p>Examples include:</p><ul><li>Business websites</li><li>Portfolio websites</li><li>Blogs</li><li>News websites</li><li>Restaurant websites</li><li>Company websites</li><li>Informational websites</li></ul><p>Visitors typically browse pages and consume information rather than performing complex tasks.</p><p>For example, a business website may include:</p><p><strong>Home → About → Services → Portfolio → Blog → Contact</strong></p><p>The main goal is usually to inform visitors, build trust, and generate inquiries or sales.</p><h3>What Is a Web Application?</h3><p>A web application is a website that allows users to <strong>interact with data, tools, or business processes</strong>.</p><p>Instead of simply reading information, users can perform actions within the application.</p><p>Examples include:</p><ul><li>Online banking systems</li><li>Customer portals</li><li>Booking systems</li><li>Project management tools</li><li>Online marketplaces</li><li>Employee dashboards</li><li>CRM systems</li><li>Inventory management systems</li></ul><p>For example, an online booking application may allow a user to:</p><p><strong>Create an account → Select a service → Choose a date → Make a payment → Manage the booking</strong></p><p>This requires considerably more functionality than a standard informational website.</p><h3>Website vs Web Application: Key Differences</h3><p>Website Web Application</p><p>Mainly provides information</p><p>Designed for user interaction</p><p>Usually simpler functionality</p><p>More complex functionality</p><p>Visitors browse content</p><p>Users perform tasks</p><p>Often public-facing</p><p>May require user accounts</p><p>Usually easier to develop</p><p>Requires more development</p><p>Examples: blogs, company sites</p><p>Examples: dashboards, booking systems</p><h3>When Do You Need a Website?</h3><p>A traditional <a href="https://www.wathsalakg.com/website-development/">website</a> is usually the right choice if your primary goal is to:</p><ul><li>Promote your business</li><li>Showcase your services</li><li>Display your portfolio</li><li>Publish articles</li><li>Provide company information</li><li>Generate leads and inquiries</li></ul><p>For many small and medium-sized businesses, a professionally developed website is all they need.</p><h3>When Do You Need a Web Application?</h3><p>A <a href="https://www.wathsalakg.com/custom-web-applications/"><strong>web application</strong></a> may be more appropriate when your business needs users to interact with your system or data.</p><p>You may need a web application if you want to:</p><ul><li>Create customer accounts</li><li>Manage bookings</li><li>Process complex orders</li><li>Build a customer dashboard</li><li>Manage inventory</li><li>Automate business processes</li><li>Connect multiple systems through APIs</li><li>Provide different functionality to different users</li></ul><p>In these situations, simply adding more plugins to a traditional website may not always be the best long-term solution.</p><h3>Can a Website and Web Application Work Together?</h3><p>Yes. Many businesses use both.</p><p>For example:</p><p><strong>Public Website</strong></p><p>Visitors can learn about your company, services, pricing, and contact information.</p><p><strong>Web Application</strong></p><p>Registered customers can log in to manage orders, bookings, invoices, or account information.</p><p>This approach allows a business to have a professional public presence while providing advanced functionality to customers or employees.</p><p><em>Originally published at </em><a href="https://www.wathsalakg.com/website-vs-web-application/"><em>https://www.wathsalakg.com</em></a><em> on September 12, 2026.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=2df3545fda3c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/codex/website-vs-web-application-whats-the-difference-2df3545fda3c">Website vs Web Application: What’s the Difference?</a> was originally published in <a href="https://medium.com/codex">CodeX</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Debug a Linux Server Without Memorizing Every Command]]></title>
            <link>https://medium.com/codex/how-to-debug-a-linux-server-without-memorizing-every-command-3c5cf9cd17c1?source=rss----29038077e4c6---4</link>
            <guid isPermaLink="false">https://medium.com/p/3c5cf9cd17c1</guid>
            <category><![CDATA[linux-server]]></category>
            <category><![CDATA[ssh-client]]></category>
            <category><![CDATA[server-management]]></category>
            <dc:creator><![CDATA[Hiren kalariya]]></dc:creator>
            <pubDate>Sat, 10 Oct 2026 09:21:02 GMT</pubDate>
            <atom:updated>2026-10-10T09:21:01.948Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>TL;DR:</strong> The hard part of managing Linux servers is not Linux. It is remembering everything around Linux. The commands, the flags, the file locations, and which server you are actually connected to. This is what that looked like across five years and more than 50 servers, and what we built to stop it.</p><figure><img alt="How to Debug a Linux Server Without Memorizing Every Command" src="https://cdn-images-1.medium.com/max/1024/1*IT3XhsoF7OEt3KJzXzvkKw.png" /></figure><p>I still remember how simple server management felt when I had only one server.</p><p>I would SSH in, run a few commands, check the logs, fix the problem, and move on.</p><p>Then one server became five.</p><p>Five became ten.</p><p>Eventually, we were managing more than 50 servers.</p><p>That is when I realized something.</p><p><strong>The hard part of managing Linux servers is not Linux itself. It is remembering everything you need to do around Linux.</strong></p><p>You need to remember commands.</p><p>You need to remember flags.</p><p>You need to remember where files are.</p><p>You need to remember which server you are connected to.</p><p>You need to remember how you checked something six months ago.</p><p>And when you do not remember, you search Google, ask AI, open another terminal, or switch to another tool.</p><p>I have spent years doing exactly this.</p><p>That is also why we built CtrlOps.</p><h3>It Starts With a Simple Question: “Why Is My Server Slow?”</h3><p>You get a message:</p><blockquote><em>“The server is slow.”</em></blockquote><p>You open your terminal.</p><p>Now you need to figure out what is happening.</p><p>You might want to check:</p><ul><li>CPU usage</li><li>Memory</li><li>Disk usage</li><li>Running processes</li><li>Services</li><li>Application logs</li><li>Nginx</li><li>PM2</li><li>Database</li></ul><p>The actual problem was one sentence.</p><p>But solving it can mean remembering a lot of commands.</p><p>And that is only <strong>one server</strong>.</p><h3>Managing One Linux Server Already Means Remembering a Lot</h3><p>When you manage a Linux server, you quickly collect a long list of commands.</p><p>You need commands to:</p><ul><li>Check CPU</li><li>Check memory</li><li>Check disk</li><li>Find large files</li><li>Check running processes</li><li>Check services</li><li>Read logs</li><li>Restart services</li><li>Manage users</li><li>Change permissions</li><li>Move files</li><li>Copy files</li><li>Create backups</li><li>Check security</li><li>Monitor the server</li></ul><p>The problem is not that these commands are impossible to learn.</p><p>The problem is that you do not use every command every day.</p><p>So when you need one after three months, you think:</p><blockquote><em>“What was that command again?”</em></blockquote><p>Then Google.</p><p>Then Stack Overflow.</p><p>Then documentation.</p><p>Then maybe AI.</p><p>And finally, you get back to the server.</p><p>That detour costs more than it feels like it does.</p><p>A study of <a href="https://www.microsoft.com/en-us/research/wp-content/uploads/2019/04/devtime-preprint-TSE19.pdf">5,971 professional developers</a> found it takes an average of <strong>8.64 minutes to resume work</strong> after an interruption.</p><p>And <a href="https://chrisparnin.me/pdf/parnin-sqj11.pdf">IDE telemetry from 86 programmers</a> found that <strong>only 10% of interrupted coding sessions resume within a minute</strong>.</p><p>Your brain does not care that you interrupted yourself.</p><h3>Sometimes You Forget the Server Before You Forget the Command</h3><p>This gets worse when you have multiple servers.</p><p>Before running anything, you might need to remember:</p><ul><li>Which server?</li><li>What was the SSH command?</li><li>Which SSH key?</li><li>Is this production or staging?</li><li>How do I check CPU?</li><li>How do I check memory?</li><li>How do I check disk?</li><li>Where are the logs?</li><li>Is a process eating resources?</li><li>Is PM2 running?</li><li>Is Nginx okay?</li><li>Is the disk full?</li></ul><p>Now the problem is not just remembering commands.</p><p>You are remembering <strong>server context</strong>.</p><p>And the more servers you manage, the harder this gets.</p><h3>Then You Ask AI</h3><p>This is probably what many of us do now.</p><p>You tell AI:</p><blockquote><em>“Give me a command to check what is using disk space on Ubuntu.”</em></blockquote><p>You get a command.</p><p>Great.</p><p>But there is still a problem.</p><p>AI does not automatically know your server.</p><p>It does not know:</p><ul><li>What applications you are running</li><li>Which directories are important</li><li>What services are critical</li><li>What data you can safely remove</li><li>What your production setup looks like</li></ul><p>So you copy the command.</p><p>You read it quickly.</p><p>And you run it.</p><p>That can be fine for a simple read-only command.</p><p>But what happens when the command changes something?</p><p>Or deletes something?</p><p>Or restarts something?</p><p>Or modifies a production configuration?</p><p>That is where I do not want to blindly trust an AI-generated command.</p><h3>I Don’t Want AI to Control My Server</h3><p>I want AI to help me.</p><p>There is a difference.</p><p>If I tell AI:</p><blockquote><em>“My disk is almost full. Help me find what is taking the space.”</em></blockquote><p>I want AI to generate a command.</p><p>I want to see the command.</p><p>I want to understand what it does.</p><p>I want to know whether the command is safe.</p><p>And then I want to decide whether to run it.</p><p>That is the idea behind the <a href="https://ctrlops.io/features/ai-terminal">AI Terminal</a> in CtrlOps.</p><figure><img alt="CtrlOps AI Terminal with Approval Gate" src="https://cdn-images-1.medium.com/max/1024/1*LgI9t9T0_UysdUWApadobg.png" /><figcaption>CtrlOps Ai Terminal</figcaption></figure><p>You explain the problem.</p><p>CtrlOps generates the command.</p><p>You can see the command and its explanation before running it.</p><p>Destructive operations are flagged before you decide.</p><p>You can review it.</p><p>You can edit it.</p><p>And <strong>the command does not run until you approve it</strong>.</p><p>The AI is helping you get the command.</p><p>You are still deciding what happens on your server.</p><h3>Sometimes I Know the Command. Sometimes I Don’t.</h3><p>I don’t want to pretend I never use the terminal.</p><p>I use it all the time.</p><p>Sometimes I know exactly what I want to run.</p><p>The problem is the other 20 percent of the time.</p><p>Maybe I remember,pm2but forget the exact command.</p><p>Maybe I know I need to search files, but forget the right flags.</p><p>Maybe I know the service name, but forget the systemctl command.</p><p>In those moments, CtrlOps can help with command suggestions directly in the terminal.</p><p>I can start typing and get suggestions instead of leaving the terminal to search for syntax.</p><figure><img alt="Terminal with commond line" src="https://cdn-images-1.medium.com/max/1024/1*4GBHaPxP6-i9-F5VCaIHrA.png" /></figure><p>Worth knowing: <strong>this part is plain autocomplete. It does not call the AI, and it does not need an API key.</strong></p><p>That sounds small.</p><p>When you do it hundreds of times, it is not.</p><h3>But Server Management Is More Than Commands</h3><p>This is where things get interesting.</p><p>Even if I solve the command problem, I still have a lot of other work.</p><p>I need to manage files.</p><p>I need to check applications.</p><p>I need to read logs.</p><p>I need to monitor resources.</p><p>I need to check security.</p><p>I need backups.</p><p>And I need to do all of this again for every server.</p><p>So we started looking at the bigger problem.</p><h3>“I Just Need to Change One File”</h3><p>Maybe I need to:</p><ul><li>Upload a file</li><li>Download a log</li><li>Edit a configuration</li><li>Rename something</li><li>Move a folder</li><li>Extract an archive</li><li>Find a file</li></ul><p>I can do all of this from the terminal.</p><p>But sometimes I just want to open the server and manage the files.</p><p>That is why CtrlOps has a <a href="https://ctrlops.io/features/file-manager">File Manager</a>.</p><figure><img alt="CtrlOps file manager" src="https://cdn-images-1.medium.com/max/1024/1*6BOfUXj25ULM1tKafK8R6Q.png" /></figure><p>I can browse the server, upload and download files, create folders, edit text files, rename files, and unzip archives without writing a single line of command</p><p>I can opennginx.conf, change the line, and hit Save.</p><p>The download, edit, and re-upload loop stops existing.</p><p>The goal is simple.</p><p><strong>I want to manage the file, not think about the command.</strong></p><h3>“Is My Application Running?”</h3><p>Someone tells me:</p><blockquote><em>“The website is down.”</em></blockquote><p>Now I need to check the application.</p><p>If I use PM2, I might need to check:</p><ul><li>Is the process running?</li><li>Did it restart?</li><li>Is CPU high?</li><li>Is memory high?</li><li>What do the logs say?</li><li>Should I restart it?</li><li>Should I reload it?</li></ul><p>Here is the part that pm2 list will not tell you.</p><p><strong>A process that has restarted 120 times looks exactly like a healthy one.</strong></p><p>With the <a href="https://ctrlops.io/features/pm2-process-manager">PM2 Manager</a>, I can see the processes, CPU, memory, uptime, restarts, and logs in one place.</p><figure><img alt="Dashboard of PM 2 Process manager feature" src="https://cdn-images-1.medium.com/max/1024/1*TQg4ytoCCNbYu0ofhEqOpw.png" /></figure><p>The restart count sits on the row, next to the last exit code.</p><p>I can also restart, reload, start, or stop the process.</p><p>Again, I am not trying to remove PM2.</p><p>I am trying to remove the unnecessary command memorization around PM2.</p><h3>Then There Are Logs</h3><p>Logs are another rabbit hole.</p><p>You know something is wrong.</p><p>Now you need to find the right log.</p><p>Maybe it is Nginx.</p><p>Maybe PM2.</p><p>Maybe the application.</p><p>Maybe a system service.</p><p>Then you need to search through it.</p><p><a href="https://ctrlops.io/features/log-management">Log Management</a> brings the logs into one place so I can search them, follow them live, download them, or clear them.</p><figure><img alt="Log Managment Dashboard" src="https://cdn-images-1.medium.com/max/1024/1*2aRO2EFgE1b641igRSgZnw.png" /></figure><p>Every log is stamped with its size and when it was last written.</p><p>So instead of asking:</p><blockquote><em>“Where is this log?”</em></blockquote><p>I can start with the two files that changed in the last five minutes.</p><p>The one touched three minutes ago is usually where the problem is.</p><h3>“Is the Server Under Pressure?”</h3><p>The website is slow.</p><p>The application looks okay.</p><p>So what is happening?</p><p>I want to check:</p><ul><li>CPU</li><li>Memory</li><li>Disk</li><li>Network</li></ul><p><a href="https://ctrlops.io/features/infra-monitoring">Infrastructure monitoring</a> shows all of it live, refreshing every two seconds over SSH, with nothing installed on the server.</p><figure><img alt="Infrastructure monitoring Dashboard" src="https://cdn-images-1.medium.com/max/1024/1*RUrH2nVMvXvJk1i7Bx59lQ.png" /></figure><p>The top processes are listed and sortable, so I can find what is eating the box without rebuilding a ps aux pipeline from memory.</p><p>Now I can start with information instead of guessing.</p><h3>Then There Is Security</h3><p>Server security is another area where manual work adds up quickly.</p><p>You need to check things like:</p><ul><li>SSH configuration</li><li>User access</li><li>Sudo access</li><li>Firewall</li><li>Open ports</li><li>Running services</li><li>Other security settings</li></ul><p>You can do this manually.</p><p>But how often will you actually remember to do a full audit?</p><p><a href="https://ctrlops.io/features/security-audit">Security Audits</a> run 25 predefined checks over SSH and show findings, warnings, and a hardening score out of 100.</p><figure><img alt="Secure audit feature of CtrlOps" src="https://cdn-images-1.medium.com/max/1024/1*tY6hAchRUbVoeGVkvQcbkA.png" /></figure><p>You also get a PDF you can send to a client.</p><p>Fix commands go through the same approval step as everything else.</p><p>So again:</p><p>Check first. Understand. Then approve the change.</p><h3>And What About Backups?</h3><p>Backups are easy to forget.</p><p>You create a script.</p><p>You schedule it.</p><p>You assume everything is fine.</p><p>Then one day you need the backup.</p><p>And you ask:</p><blockquote><em>“Did the backup actually run?”</em></blockquote><p><a href="https://ctrlops.io/features/backup">Scheduled backups</a> are configured from one form, and every run keeps its log with the file count and the bytes moved.</p><figure><img alt="Scheduled backups dashboard" src="https://cdn-images-1.medium.com/max/1024/1*rgfdcoA0x88xmwebJXtp2Q.png" /></figure><p>So the answer to that question is a screen, not a guess.</p><p>Because having a backup script is one thing.</p><p>Knowing that your backup workflow is actually being managed is another.</p><h3>All of This Is Still One Server</h3><p>This is the part people often underestimate.</p><p>Let’s say you have one server.</p><p>You have:</p><ul><li>Terminal</li><li>File management</li><li>PM2</li><li>Logs</li><li>Monitoring</li><li>Security</li><li>Backups</li></ul><p>You can manage all of that.</p><p>It takes time, but you can do it.</p><p>Now add another server.</p><p>Then another.</p><p>Then another.</p><p>Suddenly you have a completely different problem.</p><h3>What Happens When You Have 10 Servers?</h3><p>Now you need to remember:</p><ul><li>Which server belongs to which project?</li><li>Which one is production?</li><li>Which one is staging?</li><li>Which one runs the database?</li><li>Which SSH key should I use?</li><li>Which terminal is connected to which server?</li><li>Which server had the problem yesterday?</li><li>Where are the logs?</li><li>Which server needs the security audit?</li><li>Which server has the backup issue?</li></ul><p>And now you have terminals everywhere.</p><p>One terminal for this server.</p><p>Another for that server.</p><p>A different window for production.</p><p>Another tool for files.</p><p>Another tool for monitoring.</p><p>Another place for logs.</p><p>Another place for backups.</p><p>This is where server management becomes exhausting.</p><p><strong>You are not just managing servers anymore. You are managing the tools around the servers.</strong></p><p>This is also the gap between a tool that connects you to a server and one that helps you once you are there, which I have <a href="https://medium.com/codetodeploy/5-features-missing-in-termius-that-ctrlops-solves-1eb1e6d9ec8f">written about before</a>.</p><h3>We Know This Pain Because We Lived It</h3><p>We have been running an IT service company for around five years.</p><p>We have managed more than 50 servers.</p><p>So we know this workflow very well.</p><p>We used the terminals.</p><p>We searched for commands.</p><p>We switched between tools.</p><p>We opened multiple SSH sessions.</p><p>We managed files manually.</p><p>We checked logs manually.</p><p>We monitored servers.</p><p>We handled backups.</p><p>We performed security checks.</p><p>And every time we added more servers, the same problems became bigger.</p><p>At some point, we asked:</p><blockquote><em>“Why are we still doing all of this manually?”</em></blockquote><p>That question led us to build <a href="https://ctrlops.io">CtrlOps</a>.</p><h3>So We Built CtrlOps</h3><p>The idea was not to build another terminal.</p><p>The idea was to put the things we repeatedly needed into one place.</p><p>Now I can connect <a href="https://ctrlops.io/features/multi-server-management">multiple Linux servers</a> and manage them from the same application.</p><p>Instead of remembering which terminal belongs to which server, I can select the server I want to work on.</p><p>Each one opens in its own tab with its own connection, so a long build on one never blocks another.</p><p>Then I can use the tool I need.</p><p>Need a terminal? → Open Terminal.</p><p>Don’t know the command? → Ask the AI Terminal.</p><p>Need to manage files? → Open File Manager.</p><p>Need to check Node processes? → Open PM2 Manager.</p><p>Need to investigate an error? → Open Log Management.</p><p>Need to check server resources? → Open Monitoring.</p><p>Need to check security? → Run Security Audit.</p><p>Need to manage backups? → Open Backup Management.</p><p>The goal is not to give you another dashboard.</p><p><strong>The goal is to reduce the number of things you need to remember.</strong></p><h3>What It Does Not Do</h3><p>I would rather say this now than have you find out later.</p><p>It does not run one command across your whole fleet.</p><p>There is no broadcast or fan-out.</p><p>If firing the same command at twenty machines at once is your core need, Ansible is the right tool, and this is not it.</p><p>Backups copy files off the server.</p><p>They do not restore them, and they are not database-aware.</p><p>The security audit is a configuration audit and a hardening record.</p><p>It is not a vulnerability scanner, and it is not attested evidence for SOC 2 or ISO 27001.</p><p>And it does not make you a Linux engineer.</p><p>It removes the recall step, not the thinking.</p><h3>One Server Is One Problem. Multiple Servers Change Everything.</h3><p>When I started managing servers, I thought the hardest part would be learning Linux.</p><p>After managing more than 50 servers, I think differently.</p><p>The hard part is not knowing whether it df exists.</p><p>The hard part is remembering which server, which tool, which command, which file, which log, and which action, when you have many servers to manage.</p><p>That is the pain we experienced.</p><p>That is the reason we built CtrlOps.</p><h3>How Do You Manage Your Servers Today?</h3><p>I would genuinely like to know.</p><p>Do you still manage everything through SSH?</p><p>Do you have multiple terminal windows open all day?</p><p>Do you use different tools for monitoring, files, logs, backups, and security?</p><p>Or have you already found a workflow that makes multiple servers easy to manage?</p><p>Tell me what your setup looks like in the comments.</p><p>I am especially curious about the one command you look up every single time, no matter how many years you have been doing this.</p><p>Mine is still journalctl.</p><p>And if you want to see what we built from these problems, the <a href="https://ctrlops.io/tools">22 browser tools</a> on our site are free with no sign-up, and the desktop app is a one-month trial and then $7 a month.</p><p><em>I am Hiren Kalariya, Co-Founder and CEO of TST Technology. I work on </em><a href="https://ctrlops.io/?utm_source=medium&amp;utm_medium=referral&amp;utm_campaign=debug_linux&amp;ref=medium">CtrlOps</a><em>, a 100% local desktop app for managing multiple Linux servers over SSH. I write about the unglamorous parts of running infrastructure and building a product at the same time.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3c5cf9cd17c1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/codex/how-to-debug-a-linux-server-without-memorizing-every-command-3c5cf9cd17c1">How to Debug a Linux Server Without Memorizing Every Command</a> was originally published in <a href="https://medium.com/codex">CodeX</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Agent Shouldn’t Be Able to Approve Its Own Rules]]></title>
            <link>https://medium.com/codex/the-agent-shouldnt-be-able-to-approve-its-own-rules-a9421ed0a09b?source=rss----29038077e4c6---4</link>
            <guid isPermaLink="false">https://medium.com/p/a9421ed0a09b</guid>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Vlad Zoff]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 21:31:01 GMT</pubDate>
            <atom:updated>2026-10-09T21:31:01.530Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MsCJnAOg-KuDeSqxrNqpuw.png" /></figure><p><em>An agent can change the architecture. That doesn’t mean it should be able to redefine the rules that verify the change.</em></p><p>One of the less obvious problems I’ve run into with coding agents isn’t that they make bad changes.</p><p>It’s that sometimes they make a perfectly reasonable change that invalidates one of the rules I’m using to check the project.</p><p>That’s a different problem.</p><p>Imagine a project has this rule:</p><pre>All payment-provider access must go through PaymentAdapter.</pre><p>An agent is asked to add support for another payment provider.</p><p>It looks at the existing code and decides that the current adapter isn’t quite right. It wants to introduce a new abstraction.</p><p>The resulting change crosses a boundary that the project currently protects.</p><p>The checker reports a violation.</p><p>So what should happen next?</p><p>The obvious automation is:</p><ol><li>agent changes the code;</li><li>check fails;</li><li>agent changes the rule;</li><li>check passes.</li></ol><p>Technically, everything worked.</p><p>But the system has a problem: The thing being checked was allowed to change the conditions of the check.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*w9NdaG9eQiy1iDEMMkpDKA.png" /></figure><p>That made me much more interested in the difference between changing the code and changing the rules around the code.</p><h3>The dangerous loop</h3><p>The simplest version looks like this:</p><pre>agent<br>  ↓<br>change code<br>  ↓<br>verification<br>  ↓<br>failure<br>  ↓<br>agent changes policy<br>  ↓<br>verification<br>  ↓<br>pass</pre><p>There is nothing obviously broken here.</p><p>The agent might even have a good reason for changing the policy.</p><p>The problem is that the verification boundary has disappeared.</p><p>A policy is supposed to tell the system what has to remain true.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*_5IwDVsMhDb6YWG0noldWA.png" /></figure><p>If the same actor that made the change can also redefine what “true” means, a successful verification doesn’t tell you very much.</p><p>It’s just a moving target.</p><p>And this isn’t specific to architecture.</p><p>You could do the same thing with:</p><pre>maximum service size = 300 lines</pre><p>The agent produces a 420-line service.</p><p>The check fails.</p><p>The agent changes the limit to 500.</p><p>The check passes.</p><p>Or:</p><pre>module A cannot import module B</pre><p>The agent needs the dependency.</p><p>It changes the rule.</p><p>The import is now allowed.</p><p>Again, maybe that’s the right architectural decision.</p><p>But those are two separate decisions:</p><ul><li>The implementation changed.</li><li>The policy changed.</li></ul><p>They shouldn’t become one operation just because the same agent proposed both.</p><h3>Not every policy violation is actually a mistake</h3><p>This is where it gets more interesting.</p><p>I don’t want an architecture checker that treats every violation as proof that the agent did something wrong.</p><p>Sometimes the agent really should change the architecture.</p><p>Suppose an application has:</p><pre>Checkout<br>   ↓<br>PaymentAdapter<br>   ↓<br>Stripe</pre><p>And I decide that the product is getting large enough that payment workflows deserve their own domain boundary:</p><pre>Checkout<br>   ↓<br>PaymentService<br>   ↓<br>PaymentAdapter<br>   ↓<br>Stripe</pre><p>The change introduces new files.</p><p>Some imports move.</p><p>Some old boundaries disappear.</p><p>New ones appear.</p><p>A strict checker could report a pile of violations.</p><p>That doesn’t mean the change is bad.</p><p>It means the current policy describes the old architecture.</p><p>This distinction matters.</p><p>A policy isn’t supposed to prevent architecture from ever changing.</p><p>It is supposed to make architecture changes explicit.</p><h3>Proposal and approval are different things</h3><p>This led me to a fairly simple rule: An agent should be able to propose a policy change, but it shouldn’t automatically be able to approve that policy change.</p><p>For example:</p><pre>Current policy:</pre><pre>Payment provider access must go through PaymentAdapter.</pre><p>The agent can say:</p><pre>Proposed change:</pre><pre>Allow PaymentService to depend directly on a new internal<br>PaymentProvider interface.</pre><pre>Reason:<br>The current adapter boundary prevents the new workflow<br>from sharing transaction state correctly.</pre><p>That’s useful.</p><p>The agent has done the hard reasoning.</p><p>It has identified the existing constraint.</p><p>It has explained why the constraint may no longer fit.</p><p>But the proposal should remain a proposal.</p><p>The important part is that the authority approving the policy change is separate from the agent that authored the change.</p><p>Otherwise the system can silently move the goalposts.</p><h3>This is the same reason I don’t want approval in the prompt</h3><p>A prompt can say:</p><pre>Never deploy without approval.</pre><p>That’s useful instruction.</p><p>It’s not much of a control if the same process can modify the configuration that defines what counts as an approved deployment.</p><p>The more autonomous the agent becomes, the more these distinctions move out of the prompt and into the environment around it.</p><p>The agent should be able to reason about the policy.</p><p>It should be able to request a policy change.</p><p>It should be able to explain the change.</p><p>The actual enforcement shouldn’t depend on the agent remembering to follow its own instructions.</p><h3>A policy change should leave a trail</h3><p>Once policy becomes a real project artifact, another problem appears.</p><p>You need to know what the policy was when the original change was checked.</p><p>Otherwise you can end up with a strange situation where today’s successful verification only makes sense because yesterday’s policy was replaced.</p><p>That’s why I like keeping policy changes explicit and versioned.</p><p>Something like:</p><pre>project state<br>    ↓<br>policy revision 17<br>    ↓<br>agent proposes architecture change<br>    ↓<br>verification fails under policy revision 17<br>    ↓<br>policy proposal<br>    ↓<br>approval<br>    ↓<br>policy revision 18<br>    ↓<br>verify change again</pre><p>Now there are two separate facts:</p><ol><li>The change passed under policy revision 18.</li><li>Policy revision 18 was itself approved.</li></ol><p>That’s much more useful than simply seeing a green check.</p><h3>The old policy still matters</h3><p>There’s another subtle point here.</p><p>Suppose an agent changes the rule and then verifies the same diff against the new rule.</p><p>You can no longer tell whether the original change violated the previous policy.</p><p>So I want the system to preserve the distinction between:</p><pre>what the project allowed before the change</pre><p>and:</p><pre>what the project allows after the change</pre><p>This becomes especially useful when investigating a change later.</p><p>You can ask:</p><ul><li>Why did this dependency become allowed?</li><li>Was it always allowed?</li><li>Was there an explicit exception?</li><li>Did a policy revision happen at the same time?</li><li>Who approved it?</li><li>What evidence led to the change?</li></ul><p>Those questions are much harder to answer when policy is just another mutable config file.</p><h3>Temporary exceptions are different again</h3><p>Sometimes the policy is fine.</p><p>The violation is temporary.</p><p>For example:</p><pre>All persistence access must go through Repository.</pre><p>But I’m in the middle of a migration.</p><p>I don’t want to remove the rule.</p><p>I just need one known exception for two weeks.</p><p>That’s not really a policy change.</p><p>It’s a waiver.</p><p>And I think treating it as a different object makes the whole system easier to reason about.</p><p>A useful waiver has at least:</p><pre>owner<br>reason<br>scope<br>expiry</pre><p>So instead of:</p><pre>remove the rule</pre><p>you get:</p><pre>waive this finding until 2026-12-28</pre><pre>owner: platform-team<br>reason: repository migration</pre><p>The rule stays.</p><p>The exception expires.</p><p>That’s a very different thing from changing the architecture policy permanently.</p><h3>Baselines solve a different problem</h3><p>I also don’t want to confuse waivers with baselines.</p><p>A baseline answers:</p><pre>This violation already existed.</pre><p>A waiver answers:</p><pre>This active violation is intentionally allowed for a limited time.</pre><p>And a policy change answers:</p><pre>We changed what the project considers acceptable.</pre><p>Those are three different states.</p><p>That separation might sound overly precise.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*98BjFx4QJo3bd_0u47J_Pw.png" /></figure><p>In practice, it makes the tool much more useful.</p><p>A real codebase can have old architectural debt.</p><p>It can have temporary migration exceptions.</p><p>And it can deliberately evolve its architecture.</p><p>If all three become “ignore this finding”, you lose important information.</p><h3>What the agent should actually do</h3><p>This doesn’t mean agents have to stop making architectural changes.</p><p>Quite the opposite.</p><p>I want them to do more.</p><p>A useful agent flow could look like this:</p><pre>requirement<br>    ↓<br>agent plans change<br>    ↓<br>implementation<br>    ↓<br>deterministic verification<br>    ↓<br>failure<br>    ↓<br>agent explains why<br>    ↓<br>policy proposal / waiver proposal<br>    ↓<br>approval<br>    ↓<br>verification against new state</pre><p>The agent can drive most of that workflow.</p><p>It can inspect the repository.</p><p>It can understand the requirement.</p><p>It can implement the refactor.</p><p>It can identify the policy that stopped the change.</p><p>It can prepare the evidence for a policy proposal.</p><p>It can even tell me that the current architecture appears to be the problem.</p><p>What it shouldn’t get is an invisible path from:</p><pre>my change failed</pre><p>to:</p><pre>therefore my own change is now allowed</pre><h3>This also changes what “autonomous” means</h3><p>I’ve started thinking that autonomy isn’t really one switch.</p><p>An agent can have permission to:</p><ul><li>read the repository;</li><li>modify source files;</li><li>run tests;</li><li>inspect dependencies;</li><li>propose policy changes;</li></ul><p>without having permission to:</p><ul><li>approve those policy changes;</li><li>remove its own enforcement;</li><li>extend a temporary waiver indefinitely;</li><li>change the authority that verifies it.</li></ul><p>That gives you a more useful permission model than simply:</p><pre>autonomous = yes/no</pre><p>Different operations can have different authorities.</p><p>And that matters more once the agent is running for a long time or can delegate work to other agents.</p><h3>The verifier should not care who made the code change</h3><p>There’s another distinction here that I find useful.</p><p>Verification should primarily answer:</p><pre>Does the current change fit the current approved policy?</pre><p>It doesn’t need to decide whether the author was:</p><pre>human<br>Claude<br>Codex<br>Cursor<br>another agent<br>automation</pre><p>That’s a separate concern.</p><p>The verifier checks the state.</p><p>The policy layer defines the constraint.</p><p>The authority layer controls who can change the constraint.</p><p>Keeping those pieces separate makes the system much easier to reason about.</p><h3>This is what I started building into Guard</h3><p>This distinction ended up affecting the design of Codapult Guard quite a bit.</p><p>Guard already had the idea of:</p><pre>facts<br>  ↓<br>policy<br>  ↓<br>verification</pre><p>But that isn’t enough when policy itself can change.</p><p>So I started treating policy changes as first-class operations.</p><p>Guard can discover project facts and prepare proposals.</p><p>A project can explicitly approve them.</p><p>For protected projects, policy approval can require a distinct actor rather than allowing the authoring agent to approve its own proposal.</p><p>The same idea now applies to waivers.</p><p>A waiver is not just “ignore this finding forever.”</p><p>It has an owner, reason and expiry date.</p><p>When the expiry is reached, the finding becomes active again.</p><p>That gives the project three useful things:</p><pre>policy<br>exception<br>history</pre><p>instead of one growing collection of ignored warnings.</p><h3>It also makes agent integration more useful</h3><p>MCP makes this distinction especially important.</p><p>An agent can ask Guard:</p><pre>What policy applies here?</pre><p>or:</p><pre>What did this change affect?</pre><p>or:</p><pre>Why did verification fail?</pre><p>or:</p><pre>What policy change would be needed for this refactor?</pre><p>Those are useful questions.</p><p>But I don’t want the agent to receive:</p><pre>Change policy until verification passes.</pre><p>That isn’t really verification anymore.</p><p>The MCP layer should expose the information and operations the agent needs without quietly giving it the authority to redefine the system around itself.</p><p>That’s a much more interesting role for project-aware tooling than simply adding another set of commands to an agent.</p><h3>The rule can change. That isn’t the problem.</h3><p>I don’t think architectural rules should be permanent.</p><p>Projects change.</p><p>Requirements change.</p><p>Teams change.</p><p>The shape of the system changes.</p><p>A rule that made perfect sense six months ago might become actively harmful.</p><p>The mistake is treating policy change as an implementation detail.</p><p>Changing:</p><pre>src/payments/StripeAdapter.ts</pre><p>and changing:</p><pre>payment-provider-access</pre><p>are fundamentally different operations.</p><p>One changes the system.</p><p>The other changes what the system is allowed to become.</p><p>Once an agent can perform both, they need separate boundaries.</p><h3>I’m less interested in stopping agents than in making their authority explicit</h3><p>The goal isn’t to make agents weaker.</p><p>I actually want agents to make bigger changes.</p><p>But bigger changes require clearer boundaries.</p><p>The more of the implementation I delegate, the more I care about questions like:</p><ul><li>What can the agent change?</li><li>What can it propose?</li><li>What can it approve?</li><li>What evidence does it have to provide?</li><li>What policy was active when the change was checked?</li><li>Was the exception temporary?</li><li>Who approved the exception?</li><li>Can the agent change the thing that verifies it?</li></ul><p>Those questions aren’t really about whether the model is smart enough.</p><p>They’re about the architecture around the model.</p><p>And that’s probably the part that becomes more important as coding agents become capable of doing more work without waiting for a human after every step.</p><p>I don’t want the agent to be afraid of the rules.</p><p>I want it to understand them.</p><p>I want it to be able to challenge them.</p><p>I want it to be able to propose better ones.</p><p>But I don’t want it to be the final authority on whether its own change should become the new rule.</p><p>The code can change.</p><p>The architecture can change.</p><p>Even the policy can change.</p><p>Those changes just shouldn’t all happen under the same authority.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a9421ed0a09b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/codex/the-agent-shouldnt-be-able-to-approve-its-own-rules-a9421ed0a09b">The Agent Shouldn’t Be Able to Approve Its Own Rules</a> was originally published in <a href="https://medium.com/codex">CodeX</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why Dependency Mocking Accuracy Degrades Over Time and What to Do About It]]></title>
            <link>https://medium.com/codex/why-dependency-mocking-accuracy-degrades-over-time-and-what-to-do-about-it-fd4a016c827c?source=rss----29038077e4c6---4</link>
            <guid isPermaLink="false">https://medium.com/p/fd4a016c827c</guid>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[mocking]]></category>
            <category><![CDATA[software-testing]]></category>
            <category><![CDATA[code]]></category>
            <category><![CDATA[dependency-mocking]]></category>
            <dc:creator><![CDATA[Sancharini Panda]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 11:51:01 GMT</pubDate>
            <atom:updated>2026-10-09T11:51:01.481Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="Why Dependency Mocking Accuracy Degrades Over Time and What to Do About It" src="https://cdn-images-1.medium.com/max/1024/1*Luv7Sis-ll4oBrKEtX9L-A.png" /></figure><p>The mock was correct when it was written. Nobody disputes that part. What happens after is the problem. The upstream payment service ships a release. The inventory service updates how it handles out-of-stock responses. The notification service changes its error format for invalid addresses. None of these changes come with an automatic update to the mocks representing those services in the downstream team’s test suite. The mocks keep returning what they were configured to return. The tests keep passing. The divergence between what the mock returns and what the real service currently returns accumulates without any signal.</p><p>Dependency mocking accuracy degradation is not a dramatic failure. It is a slow drift that makes test results progressively less trustworthy without making them visibly wrong.</p><h3>Why Tests Keep Passing While Accuracy Drops</h3><p>The thing that makes mock drift hard to catch is that the tests do not know they are running against stale assumptions. An assertion written against a mock response passes when the mock returns that response correctly. Whether the mock response still reflects current real service behavior is entirely outside what the assertion checks.</p><p>Coverage metrics cannot catch this. A test suite with ninety percent coverage and three-month-old mocks reports the same coverage number as one with current mocks. Pass rates cannot catch it either. The test passed- against its own expectations, which have not changed, even as the real service has.</p><p>The gap surfaces at production. An order fails because the payment service started returning a different charge object structure after its last deployment. The test that covered this interaction passed because it ran against a mock that returned the old structure. No metric flagged the mock as stale. No alert fired when the payment service deployed. The first signal was a failed order.</p><h3>What Creates the Drift</h3><p>Upstream deployments are the event that creates accuracy risk. Every time an upstream service deploys, there is a chance something it returns has changed. Not every deployment introduces a behavioral change that affects downstream mocks, but tracking which ones do requires either reading every changelog carefully or having a mechanism that catches the changes automatically.</p><p>Most teams do neither consistently. The developer who wrote the mock is three projects removed from it. The team consuming the upstream service has no channel through which deployment notifications arrive. Changelogs accumulate unread. The upstream service ships on its own schedule and the mock accumulates distance from it.</p><p>The other factor is how the mock was created. Mocks built from API documentation encode what the documentation said at authoring time. Nobody writes API documentation after the implementation ships. It gets written before, updated inconsistently, and left alone when things change in ways that feel too minor to document. A field that got renamed during a refactor. A format that was deprecated but still comes back on certain request paths. A property that appeared after a schema migration because the database started returning it. None of that shows up in what the mock was built from.</p><h3>What Actually Keeps Mocks Current</h3><p>Waiting for failures is the most common maintenance approach. A production incident reveals the stale mock. Someone updates it. The cycle repeats. What this model misses is everything between the upstream deployment that created the accuracy risk and the downstream failure that eventually surfaced it- days, sometimes weeks, of tests passing against assumptions the real service had already moved past. For drift that does not cause test failures, because the assertions pass against the old behavior even after the real service changed- the reactive approach never triggers at all.</p><p>The alternative trigger is the upstream deployment itself. Not the downstream failure it eventually causes. When the payment service deploys to staging, that event should be what prompts a check of the mocks representing payment service behavior- not the next production incident involving payment processing. This is a different architectural relationship between upstream deployments and downstream test maintenance. CI/CD webhook integrations and shared event subscriptions make it achievable. Most teams have not built it because the trigger for mock maintenance has always been a failure, not a deployment.</p><p>Recording sessions against real staging services change where mock content comes from. Instead of a developer authoring what the upstream service should return, a session against the real service captures what it actually returns for the request patterns the downstream service sends. The mock reflects observed real behavior rather than a developer’s reading of documentation. After the upstream service deploys a new version, a new session against the updated staging service captures current behavior. Comparing the previous session’s output against the new one surfaces what changed- which fields appeared or disappeared, which types shifted -without the developer needing to know in advance that anything changed.</p><p>This approach does not eliminate maintenance. It changes what maintenance requires. Instead of reading changelogs and manually updating mock files, the developer reviews a diff between observed past behavior and observed current behavior. The diff is the input they need to decide whether application code needs updating alongside the mock refresh.</p><h3>The Metric That Would Actually Surface This</h3><p>Mock accuracy does not have a standard metric in most testing frameworks. There is no dashboard showing that a mock was last validated six months ago against a service that has deployed dozens of times since. That information exists- it could be derived from deployment logs and mock metadata, but most teams do not track it.</p><p>The closest proxy is production failures traced to integrations the test suite claimed to cover. Not failures from missing tests. Failures where a test existed, the mock existed, the test passed, and production still broke. Tracking this separately from overall production failure rate reveals the proportion of production failures that came from mock accuracy gaps rather than from code defects or missing coverage.</p><p>Teams that track this number discover it is not small. A meaningful share of their production integration failures were preceded by passing tests against mocks that no longer reflected current <a href="https://keploy.io/blog/community/dependency-mocking-service-virtualization"><strong>dependency mocking</strong></a> reality. That share does not shrink by adding more tests. It shrinks by changing when and how the existing mocks get updated.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fd4a016c827c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/codex/why-dependency-mocking-accuracy-degrades-over-time-and-what-to-do-about-it-fd4a016c827c">Why Dependency Mocking Accuracy Degrades Over Time and What to Do About It</a> was originally published in <a href="https://medium.com/codex">CodeX</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Building File-Based Apps in .NET: A Complete Guide With Multi-File Support]]></title>
            <link>https://medium.com/codex/building-file-based-apps-in-net-a-complete-guide-with-multi-file-support-99afe0e50a13?source=rss----29038077e4c6---4</link>
            <guid isPermaLink="false">https://medium.com/p/99afe0e50a13</guid>
            <category><![CDATA[dotnet]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[csharp]]></category>
            <category><![CDATA[aspnetcore]]></category>
            <dc:creator><![CDATA[Anton Martyniuk]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 09:57:54 GMT</pubDate>
            <atom:updated>2026-10-09T09:57:53.595Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*feXutfnIc2sa9YoUJr-e0Q.png" /></figure><p>For years, even the smallest C# program required a solution file, a project file, and a folder structure.</p><p>While Python and JavaScript developers could create a single file and run it in seconds, .NET developers had to create a solution and add a csproj file just to test an idea.</p><p>That changed with .NET 10, which introduced file-based apps.</p><p>Now you can run a C# file directly with dotnet run.</p><p>And in April 2026 in <strong>.NET 11 Preview 3</strong> we received a support for using multiple files in file-based apps with the #:include directive.</p><p>This finally makes C# a real option for scripts, automation, internal tooling, and quick prototypes — without the overhead of a full project.</p><p>In this post, we will explore:</p><ul><li>What Are File-Based Apps in .NET</li><li>Adding NuGet Packages and SDKs with Directives</li><li>Splitting File-Based Apps Across Multiple Files</li><li>Building an HTTP Health-Check Tool</li><li>Building a Minimal API with EF Core and SQLite</li><li>Converting a File-Based App to a Full Project</li></ul><p>Let’s dive in!</p><p>👉 <strong>Read original article on my newsletter:</strong> <a href="https://antondevtips.com/blog/building-file-based-apps-in-dotnet-with-multi-file-support">https://antondevtips.com/blog/building-file-based-apps-in-dotnet-with-multi-file-support</a></p><h3>What Are File-Based Apps in .NET</h3><p>Traditionally, every C# application required three things:</p><ul><li>Solution file (*.sln)</li><li>Project file (*.csproj)</li><li>Source code (*.cs).</li></ul><p>Even for a 10-line script, you had to create a new solution, add a project with dotnet new console, wait for the scaffolding to finish, and only then write your actual code.</p><p>Starting with .NET 10, you can skip all of that.</p><p>You can create a single .cs file and run it directly:</p><pre>dotnet run main.cs</pre><p>That’s it. No project file. No solution file. Just one file.</p><p>This puts C# on equal footing with Python, JavaScript, Node.js, and other scripting languages.</p><p>For CLI utilities, automation tasks, and one-off tools, this completely changes the workflow.</p><p>Let’s see the simplest possible example. Create a file called hello.cs:</p><pre>Console.WriteLine(&quot;Hello from a file-based app!&quot;);<br>Console.WriteLine($&quot;Today is {DateTime.Now:dddd, MMMM d, yyyy}&quot;);</pre><p>Then run it:</p><pre>dotnet run hello.cs</pre><p>Notice there is no Main method, no class Program, no using statement.</p><p>File-based apps build on top of top-level statements (introduced in C# 9) and implicit usings, so you can write code as if you were in a script.</p><p>Behind the scenes, the .NET CLI compiles your file in a temporary location and runs the resulting binary.</p><p>The first run takes a moment, but subsequent runs are cached and fast.</p><p>I’ve used this many times in the last year for small tasks I would have otherwise written in Python (or created a solution project):</p><ul><li>Quick data transformations on JSON or CSV files</li><li>Calling internal APIs to verify a deployment</li><li>Running test scripts</li><li>Parsing log files to extract patterns</li></ul><h3>Adding NuGet Packages and SDKs with Directives</h3><p>A real script usually needs more than the BCL.</p><p>You probably want JSON parsing, HTTP calls, or database access.</p><p>File-based apps support special # directives at the top of your file to declare dependencies.</p><p><strong>The </strong><strong>#:package Directive</strong></p><p>Use #:package to reference any NuGet package:</p><pre>#:package Newtonsoft.Json@13.0.3<br> <br>using Newtonsoft.Json;<br> <br>var data = new { Name = &quot;Anton&quot;, Year = 2026 };<br>var json = JsonConvert.SerializeObject(data, Formatting.Indented);<br>Console.WriteLine(json);</pre><p>The format is #:package PackageName@Version.</p><p>The .NET CLI restores the package on first run, just like it would for a regular project.</p><p><strong>The </strong><strong>#:sdk Directive</strong></p><p>Use #:sdk when you need a specific SDK, such as the Web SDK for ASP.NET Core:</p><pre>#:sdk Microsoft.NET.Sdk.Web<br> <br>var builder = WebApplication.CreateBuilder();<br>var app = builder.Build();<br> <br>app.MapGet(&quot;/&quot;, () =&gt; &quot;Hello from a single-file API!&quot;);<br> <br>app.Run();</pre><p>By default, file-based apps use Microsoft.NET.Sdk (the standard SDK).</p><p>Switching to Microsoft.NET.Sdk.Web unlocks ASP.NET Core types like WebApplication without any project setup.</p><p><strong>The </strong><strong>#:project Directive</strong></p><p>If you need to call into an existing project (for example, a shared class library), use #:project:</p><pre>#:project ../MyLibrary/MyLibrary.csproj<br> <br>using MyLibrary;<br> <br>var calculator = new Calculator();<br>Console.WriteLine(calculator.Add(2, 3));</pre><p>This is helpful when you have a class library and want to write a quick tool that uses it, without creating yet another console project.</p><p>👉 <strong>Read original article on my newsletter:</strong> <a href="https://antondevtips.com/blog/building-file-based-apps-in-dotnet-with-multi-file-support">https://antondevtips.com/blog/building-file-based-apps-in-dotnet-with-multi-file-support</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=99afe0e50a13" width="1" height="1" alt=""><hr><p><a href="https://medium.com/codex/building-file-based-apps-in-net-a-complete-guide-with-multi-file-support-99afe0e50a13">Building File-Based Apps in .NET: A Complete Guide With Multi-File Support</a> was originally published in <a href="https://medium.com/codex">CodeX</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Understanding Java Exceptions — The Theory Behind Termination, Checked Exceptions, and Cleanup the…]]></title>
            <link>https://medium.com/codex/understanding-java-exceptions-the-theory-behind-termination-checked-exceptions-and-cleanup-the-caf17c87fc2b?source=rss----29038077e4c6---4</link>
            <guid isPermaLink="false">https://medium.com/p/caf17c87fc2b</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[theory]]></category>
            <category><![CDATA[exception]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[day11]]></category>
            <dc:creator><![CDATA[Someone]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 09:57:52 GMT</pubDate>
            <atom:updated>2026-10-09T09:57:51.496Z</atom:updated>
            <content:encoded><![CDATA[<h3>Understanding Java Exceptions — The Theory Behind Termination, Checked Exceptions, and Cleanup the Compiler Writes for You</h3><h4>Arbiter Guide Theory • Day 11 | Why Java’s catch can only terminate, where checked exceptions came from, why finally is copied into every exit, and how try-with-resources becomes nested handlers in the bytecode</h4><figure><img alt="A blue wireframe infographic showing an exception rising through a call stack, two nested resource brackets closing from the inside out, and a bytecode handler table with one highlighted catch-any row." src="https://cdn-images-1.medium.com/max/1024/1*s2ZrECwAIoAgcSRaJ75dKg.png" /><figcaption><em>An exception rises through the call stack, nested resource brackets close from the inside out, and a handler table decides where control lands.</em></figcaption></figure><p><em>An exception rises through the call stack, nested resource brackets close from the inside out, and a handler table decides where control lands.</em></p><p>Before we begin, here’s the source code for today’s lesson:</p><pre>class NegativeAmountException extends Exception {<br>    public NegativeAmountException(String message) {<br>        super(message);<br>    }<br>}<br><br>class DataFormatException extends Exception {<br>    public DataFormatException(String message) {<br>        super(&quot;Data Format Exception: &quot; + message);<br>    }<br>}<br><br>class DataCorruptionException extends RuntimeException {<br>    public DataCorruptionException(String message) {<br>        super(&quot;Data Corruption Exception: &quot; + message);<br>    }<br>}<br><br>class TrackedResource implements AutoCloseable {<br>    private String name;<br>    <br>    public TrackedResource(String name) {<br>        this.name = name;<br>    }<br><br>    @Override<br>    public void close() {<br>        System.out.println(&quot;Closing &quot; + name);<br>    }<br><br>    public void process(int code) throws DataFormatException, DataCorruptionException {<br>        if (code == 1) throw new DataFormatException(&quot;Code = &quot; + code);<br>        else if (code == 2) throw new DataCorruptionException(&quot;Code = &quot; + code);<br>        else System.out.println(&quot;Processed &quot; + code + &quot; in &quot; + name);<br>    }<br>}<br><br>public class java_11Exceptions {<br>    public static void main(String[] args) {<br>        try {<br>            int result = withdraw(100, 40);<br>            System.out.println(&quot;Result: &quot; + result);<br>        } catch (NegativeAmountException e) {<br>            System.out.println(e.getMessage());<br>        } finally {<br>            System.out.println(&quot;Attempt finished.&quot;);<br>        }<br><br>        try {<br>            int result = withdraw(100, -10);<br>            System.out.println(&quot;Result: &quot; + result);<br>        } catch (NegativeAmountException e) {<br>            System.out.println(e.getMessage());<br>        } finally {<br>            System.out.println(&quot;Attempt finished.&quot;);<br>        }<br><br>        System.out.println();<br><br>        try (<br>            TrackedResource tr1 = new TrackedResource(&quot;Tracked Resource 1&quot;);<br>            TrackedResource tr2 = new TrackedResource(&quot;Tracked Resource 2&quot;);<br>        ) {<br>            tr1.process(0);<br>        } catch (DataCorruptionException e) {<br>            System.out.println(e.getMessage());<br>        } catch (DataFormatException e) {<br>            System.out.println(e.getMessage());<br>        }<br><br>        try (<br>            TrackedResource tr1 = new TrackedResource(&quot;Tracked Resource 1&quot;);<br>            TrackedResource tr2 = new TrackedResource(&quot;Tracked Resource 2&quot;);<br>        ) {<br>            tr1.process(1);<br>        } catch (DataCorruptionException e) {<br>            System.out.println(e.getMessage());<br>        } catch (DataFormatException e) {<br>            System.out.println(e.getMessage());<br>        }<br><br>        try (<br>            TrackedResource tr1 = new TrackedResource(&quot;Tracked Resource 1&quot;);<br>            TrackedResource tr2 = new TrackedResource(&quot;Tracked Resource 2&quot;);<br>        ) {<br>            tr1.process(2);<br>        } catch (DataCorruptionException e) {<br>            System.out.println(e.getMessage());<br>        } catch (DataFormatException e) {<br>            System.out.println(e.getMessage());<br>        }<br>    }<br><br>    public static int withdraw(int balance, int amount) throws NegativeAmountException {<br>        if (amount &lt; 0) {<br>            throw new NegativeAmountException(&quot;Amount cannot be negative: &quot; + amount);<br>        }<br>        return balance - amount;<br>    }<br>}</pre><p>The Guide article for this lesson showed <em>what</em> Java does with exceptions: finally always runs, the parent class decides checked vs. unchecked, and try-with-resources closes in reverse order before catch runs. This article asks <em>why those are the rules</em>, and where each one came from.</p><p>This is <strong>Day 11</strong> of my <strong>Arbiter Learning Journey</strong>, where I’m learning Java from the ground up before eventually building <strong>Arbiter</strong>, a complete microservices-based test management platform. The practical version of today’s lesson is in the <a href="https://medium.com/@computer-info-1/learning-java-from-scratch-with-arbiter-exceptions-try-catch-finally-checked-vs-2f66d8ba2bc6">Day 11 Guide</a>.</p><p>In <a href="https://medium.com/codex/understanding-java-regex-the-theory-behind-compiled-patterns-thompsons-automata-and-the-cursor-8f9c8e26dcb4"><strong><em>Day 10 Guide Theory</em></strong></a>, the subject was compiled patterns, Thompson’s automata, and a strict &gt; that ignored ties. This time the machinery is different, but the habit is the same: ask what the language <em>promises</em>, and then check that promise against evidence. Where I could, the evidence below is the compiler&#39;s own output, not my description of it.</p><p><em>(A note on ordering: Day 10 closed by previewing Sets. Today’s lesson file is Exceptions, so Sets get deferred one more slot.)</em></p><h3>Why Failure Needed Its Own Control Flow: Goodenough’s Problem</h3><h4>Before throw existed as a mechanism, failure had to travel inside return values, and the paper that asked for a better notation</h4><p>Look at withdraw:</p><pre>public static int withdraw(int balance, int amount) throws NegativeAmountException {<br>    if (amount &lt; 0) {<br>        throw new NegativeAmountException(&quot;Amount cannot be negative: &quot; + amount);<br>    }<br>    return balance - amount;<br>}</pre><p>It could have been written another way: return -1 for an invalid amount and make every caller check. But -1 is an int, the same type as a legitimate result, so a caller who forgets the check treats the failure as data. The failure disappears into the arithmetic.</p><p>John B. Goodenough took that problem seriously in “Exception Handling: Issues and a Proposed Notation,” published in <em>Communications of the ACM</em> 18(12) in December 1975. The paper doesn’t just propose a keyword. It frames exception handling as a design question with distinct choices, and it uses two terms that still organize the field: <strong>termination</strong> (the operation that raised the exception ends) and <strong>resumption</strong> (control returns to the point of the raise after the handler finishes). It even sorts exceptions by which of those the handler is allowed to do: one class must terminate, one must resume, and one leaves the choice to the handler.</p><p>Everything in today’s lesson sits on that foundation. throw new NegativeAmountException(...) moves the failure out of the data channel and into a separate control channel the compiler and runtime can see.</p><h3>Termination, Not Resumption: What catch Can and Cannot Do</h3><h4>Java sits on one side of Goodenough’s divide, and the same side as CLU</h4><p>Java picked a side. Once an exception is thrown, the code that threw it does not continue. The catch block runs, and control then continues <strong>after the whole </strong><strong>try statement</strong>, never back at the throw point.</p><p>The first output of the lesson shows this. The second withdraw call throws, and the line System.out.println(&quot;Result: &quot; + result); right after it never runs. The output for that attempt contains the exception message and Attempt finished., with no Result: line at all. I checked this in isolation as well: the statement after the throwing call is simply skipped.</p><p>The termination model has a well-documented relative. Barbara Liskov and Alan Snyder’s “Exception Handling in CLU” (<em>IEEE Transactions on Software Engineering</em>, SE-5(6), November 1979) describes a single-level termination model in which the signalling procedure’s activation ceases to exist. I’m not claiming Java copied CLU. I’m pointing out that both landed on the same side of Goodenough’s divide, for a reason you can read straight off the lesson: if the failing code were resumed, withdraw would need to <em>return a value</em> after a rejected amount, and there is no sensible value to return.</p><h3>Checked vs. Unchecked: A Declaration Lineage and an Argument</h3><h4>CLU’s headers, Java’s throws, and the engineer who left checked exceptions out of C#</h4><p>The lesson’s two custom exceptions differ in one word:</p><pre>class NegativeAmountException extends Exception { ... }        // checked<br>class DataCorruptionException extends RuntimeException { ... } // unchecked</pre><p>The Java Language Specification defines the split structurally (§11.1.1): the checked exceptions are Throwable and its subclasses, <em>except</em> RuntimeException, Error, and their subclasses. That&#39;s why there&#39;s no keyword. The class hierarchy <strong>is</strong> the declaration.</p><p>The idea of listing exceptions in a signature has a clear ancestor. In CLU, the Liskov and Snyder paper describes procedure headers that must name the exceptions a procedure may signal, which allows static checking. Java’s throws clause does a similar job. In today&#39;s code, process(...) throws DataFormatException, DataCorruptionException is that header.</p><p>The idea also has a famous critic. In an interview with Bruce Eckel and Bill Venners (held July 30, 2003, published August 18 as “The Trouble with Checked Exceptions”), Anders Hejlsberg, the lead architect of C#, gave two arguments against the feature. The first is <strong>versioning</strong>: adding a new checked exception to a method’s throws clause in a later release breaks every existing caller, much like changing a published interface. The second is <strong>scalability</strong>: in a large system built from many subsystems, declared exceptions pile up, and developers work around them with catch-all declarations or empty catch blocks.</p><p>Both arguments can be read off today’s code. Suppose process later gained a third checked exception. Each of the three try blocks in main would stop compiling until it got a new catch or throws. That is Hejlsberg&#39;s versioning point, in miniature. And the reason DataCorruptionException can be declared or not, as the Guide article showed, is that unchecked exceptions opt out of the whole system. The lesson uses both kinds side by side, which is a fair way to see the trade-off yourself.</p><h3>finally Is Copied, Not Called: What javac Does With Your Always Block</h3><h4>Goodenough split cleanup by path; the compiler splits it back out</h4><p>Goodenough’s paper treats cleanup as a first-class concern. It introduces a CLEANUP exception that runs before an operation is terminated abnormally, and an ENDED exception for normal completion. Two paths, two mechanisms.</p><p>Java’s finally merges them into one block you write once. But the block isn&#39;t a subroutine the runtime calls. I disassembled the lesson with javap -c (JDK 21, so exact offsets may differ on other compilers) and the string Attempt finished. appears <strong>six times</strong> in main&#39;s bytecode, three times per try/finally statement. For the first try:</p><ul><li>offset 20–25: after the try body completes normally</li><li>offset 42–47: after the catch handler completes</li><li>offset 53–59: in a catch-any handler, followed by aload_2 and athrow, which rethrows whatever arrived</li></ul><p>The compiler pasted the finally body onto each exit path. The exception table makes the third path precise:</p><pre>from  to  target  type<br>   0  20      31  Class NegativeAmountException<br>   0  20      53  any<br>  31  42      53  any</pre><p>Read it as: <em>if something is thrown between offset 0 and 20, jump to 31 when it’s a </em><em>NegativeAmountException, otherwise jump to 53.</em> The third row is the surprising one. Offsets 31–42 are the catch body itself, and an exception thrown <em>inside the catch block</em> also routes to 53. So finally runs even when the handler itself fails. The table doesn&#39;t just say finally runs. It says which ranges of code it covers.</p><h3>RAII, Reinvented as a Block: Where Try-with-Resources Comes From</h3><h4>Stroustrup’s destructors, Java 7’s AutoCloseable, and a nesting the compiler builds for you</h4><p>C++ solved cleanup with the object’s lifetime. The technique was developed between 1984 and 1989, mostly by Bjarne Stroustrup and Andrew Koenig, and Stroustrup named it <strong>resource acquisition is initialization (RAII)</strong> in <em>The Design and Evolution of C++</em> (1994). A local object’s destructor runs when its scope ends, and locals are destroyed in the reverse of the order they were constructed.</p><p>Java doesn’t destroy objects at scope end, so it needed a block form instead. Java SE 7 introduced both the try-with-resources statement and the java.lang.AutoCloseable interface. Oracle&#39;s own write-up of the feature names the problem it fixes: with hand-written try/finally, a close() that throws while another exception is already in flight can replace the original, so the root cause is lost. Java 7 added addSuppressed() and getSuppressed() to Throwable so the close failure can be attached instead of hiding the real one.</p><p>Here’s the part I wouldn’t have guessed. <strong>Reverse order isn’t a rule the runtime enforces. It falls out of nesting.</strong> The exception table for the first try-with-resources block:</p><pre>from  to  target  type<br> 154 159     166  Class java/lang/Throwable<br> 167 171     174  Class java/lang/Throwable<br> 144 184     191  Class java/lang/Throwable<br> 192 196     199  Class java/lang/Throwable<br> 134 207     210  Class DataCorruptionException<br> 134 207     224  Class DataFormatException</pre><p>Offsets 154–159 are tr1.process(0), wrapped in a handler that closes tr2. That whole construct sits inside the range 144–184, which is wrapped in a handler that closes tr1. So the compiler turned two flat declarations into two <strong>nested</strong> try blocks, with tr2 innermost. Inner handlers run before outer ones, so tr2 closes first. Stroustrup&#39;s reverse-destruction rule and Java&#39;s reverse-close order are the same idea, reached by different mechanisms.</p><p>The handlers also contain the suppression logic. At offset 174–179 the bytecode calls Throwable.addSuppressed, and the string addSuppressed appears six times in main, two per resource block. That&#39;s the compiler writing the &quot;close failed while handling another failure&quot; code you never have to.</p><p>One more detail from the source: TrackedResource.close() declares no throws clause. That matters, because AutoCloseable.close() itself declares throws Exception. I tested the difference: declaring the resource as plain AutoCloseable r = new T() fails to compile with unreported exception Exception ... from implicit call to close(). The lesson compiles because the variables are typed as TrackedResource, whose close() narrows the signature to throw nothing.</p><h3>Why catch Runs After Close: The Handler Table&#39;s Ordering</h3><h4>The bytecode makes the Guide article’s surprising output a structural fact</h4><p>The Guide article reported the strangest line of the output: both Closing lines print <strong>before</strong> the catch message. In the table above, the last two rows say why. The catch entries cover offsets <strong>134–207</strong>, the whole region, including both resource handlers. The inner Throwable handlers close their resource and then rethrow (athrow at offsets 183 and 206, both inside 134–207). The rethrown exception lands in the outer range, and <em>then</em> the catch handlers at 210 and 224 fire.</p><p>So “resources close before catch” isn’t a special rule. It’s what you get when catch is the outermost handler and each resource handler rethrows into it.</p><p>It also explains why you can’t touch tr1 from the catch block. At the language level the resource variable is scoped to the try body (JLS §14.20.3), and I confirmed that referencing it from catch fails with cannot find symbol. The bytecode agrees: the handler at offset 210 begins with astore_1, storing the exception in local slot 1, the same slot tr1 used inside the block.</p><h3>What the Repeated Lines Prove</h3><h4>A counting argument: six resources opened, six closed, on three different paths</h4><p>The program opens two resources in each of three try blocks, so six TrackedResource objects exist in total. The output contains six Closing lines. And the three blocks take three different paths: a normal exit, a checked exception, an unchecked exception.</p><p>That is the guarantee, stated as a count: every resource that was successfully opened is closed exactly once, on every path. The table shows why it holds. Each resource has a close() call on its normal exit and a handler on its exceptional exit, and the handlers for different resources cover different, nested ranges, so no path escapes without passing through one.</p><p>It’s worth stating what this does <strong>not</strong> prove. It shows the close happens. It doesn’t show the close <em>succeeded</em>, which is exactly the gap the suppression machinery exists to report.</p><h3>The Unchecked Gap: Where the Theory Stops Helping</h3><h4>Everything the compiler can prove is about declared exceptions, and undeclared ones can still pass through</h4><p>The compiler’s help has a boundary. Two rules from exception checking (JLS §11.2.3) show where. Catching a checked exception that the try body can never throw is a compile-time error. I confirmed it:</p><pre>error: exception Checked is never thrown in body of corresponding try statement</pre><p>And a catch for a class already covered by an earlier catch is rejected too:</p><pre>error: exception Sub has already been caught</pre><p>But catching an <strong>unchecked</strong> exception that the body never throws compiles and runs without complaint. I confirmed that as well. The compiler’s analysis simply doesn’t look at RuntimeException subclasses.</p><p>That is the Day 11 version of this series’ recurring “quiet failure.” DataCorruptionException was handled in the lesson only because someone chose to write catch (DataCorruptionException e). Nothing required it. Hejlsberg&#39;s scalability worry is the other side of the same coin: checked exceptions are loud and sometimes too loud, while unchecked ones are silent unless a human adds a handler on purpose.</p><h3>Why These Concepts Matter</h3><p>Exceptions look like one feature, but the lesson combines four independent design decisions, each with a named origin. Taking failure out of the return value is Goodenough’s problem (1975). Terminating instead of resuming is the same choice CLU made (Liskov and Snyder, 1979). Declaring exceptions in signatures is an idea CLU also had, and Hejlsberg’s 2003 critique is the best-known argument against Java’s version of it. And scope-bound cleanup is Stroustrup’s RAII, rebuilt for a garbage-collected language as nested handlers.</p><p>The most useful habit this lesson builds is reading the compiler’s output instead of trusting a mental model. The close order, the copied finally body, and the rethrow into an outer catch are all visible in a few dozen lines of javap output, and that evidence is stronger than a description of how things &quot;should&quot; work.</p><h3>The Arbiter Journey Continues</h3><p>One concept at a time, this series is building toward <strong>Arbiter</strong>, a microservices-based test management platform using Java, Spring Boot, databases, testing frameworks, Docker, and AWS. The theory above will matter there in a concrete way: every database connection, statement, and file handle in a test-run service is an AutoCloseable, so the nested-handler behavior from today is what keeps a failed run from leaking them. And Arbiter&#39;s own domain failures, like an invalid test-case status, will need a deliberate choice between checked and unchecked, with Hejlsberg&#39;s versioning argument as a cost to weigh.</p><h3>Follow along with the complete Arbiter playlist</h3><p><a href="https://youtube.com/playlist?list=PLZG2gr4IjsZM&amp;si=StFLpldNdIOjc3ub">https://youtube.com/playlist?list=PLZG2gr4IjsZM&amp;si=StFLpldNdIOjc3ub</a></p><h3>GitHub Repository</h3><p>You can follow the complete learning journey, source code, explanations, and future lessons here:</p><p>GitHub Repository: <a href="https://github.com/Someone-anon-coder/Arbiter">https://github.com/Someone-anon-coder/Arbiter</a></p><h3>Follow the Complete Arbiter Series</h3><p>Every Guide and Guide Theory article in this series, collected in one place:</p><p>Arbiter Guide: <a href="https://computer-info-1.medium.com/list/arbiter-java-20daa21849c8">https://computer-info-1.medium.com/list/arbiter-java-20daa21849c8</a></p><p>Arbiter Guide Theory: <a href="https://computer-info-1.medium.com/list/arbiter-java-theory-13ecf0e21149">https://computer-info-1.medium.com/list/arbiter-java-theory-13ecf0e21149</a></p><h3>Explore My Other Learning Series</h3><p>If you’re interested in my other programming and technology learning journeys, you may also enjoy:</p><p>Cybersecurity: <a href="https://computer-info-1.medium.com/list/cybersecurity-cba13dd0be16">https://computer-info-1.medium.com/list/cybersecurity-cba13dd0be16</a></p><p>Python Guide: <a href="https://computer-info-1.medium.com/list/python-guide-8e0b3bcab940">https://computer-info-1.medium.com/list/python-guide-8e0b3bcab940</a></p><p>Python Guide Theory: <a href="https://computer-info-1.medium.com/list/python-guide-theory-81528784ebfe">https://computer-info-1.medium.com/list/python-guide-theory-81528784ebfe</a></p><p>GoLang Guide: <a href="https://computer-info-1.medium.com/list/golang-guide-e70e25ca8b42">https://computer-info-1.medium.com/list/golang-guide-e70e25ca8b42</a></p><p>GoLang Guide Theory: <a href="https://computer-info-1.medium.com/list/golang-guide-theoretical-082d8e7624ab">https://computer-info-1.medium.com/list/golang-guide-theoretical-082d8e7624ab</a></p><p>Matplotlib: <a href="https://computer-info-1.medium.com/list/my-progress-matplotlib-669052e8da38">https://computer-info-1.medium.com/list/my-progress-matplotlib-669052e8da38</a></p><h3>What’s Next?</h3><p>In <strong>Day 12</strong>, we pick Sets back up: HashSet and TreeSet, why a Set is really a Map wearing a disguise, and how to choose the right collection for a problem on purpose rather than by habit.</p><p>Happy coding! ☕</p><p><strong>Companion Guide article:</strong> For the practical, code-driven walkthrough of this lesson, see the <a href="https://medium.com/@computer-info-1/learning-java-from-scratch-with-arbiter-exceptions-try-catch-finally-checked-vs-2f66d8ba2bc6">Day 11 Guide</a> piece, published alongside this one. For the previous lesson’s theory, see <a href="https://medium.com/codex/understanding-java-regex-the-theory-behind-compiled-patterns-thompsons-automata-and-the-cursor-8f9c8e26dcb4">Day 10 Guide Theory</a>.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=caf17c87fc2b" width="1" height="1" alt=""><hr><p><a href="https://medium.com/codex/understanding-java-exceptions-the-theory-behind-termination-checked-exceptions-and-cleanup-the-caf17c87fc2b">Understanding Java Exceptions — The Theory Behind Termination, Checked Exceptions, and Cleanup the…</a> was originally published in <a href="https://medium.com/codex">CodeX</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Give Your LangGraph Agents Tools: Build Tool-Using AI Agents with Hugging Face]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/codex/give-your-langgraph-agents-tools-build-tool-using-ai-agents-with-hugging-face-19f251de0f9a?source=rss----29038077e4c6---4"><img src="https://cdn-images-1.medium.com/max/1672/1*5GjSua7k4UvAOUZoR1JmRg.png" width="1672"></a></p><p class="medium-feed-snippet">From chatbots that generate text to AI agents that take useful actions</p><p class="medium-feed-link"><a href="https://medium.com/codex/give-your-langgraph-agents-tools-build-tool-using-ai-agents-with-hugging-face-19f251de0f9a?source=rss----29038077e4c6---4">Continue reading on CodeX »</a></p></div>]]></description>
            <link>https://medium.com/codex/give-your-langgraph-agents-tools-build-tool-using-ai-agents-with-hugging-face-19f251de0f9a?source=rss----29038077e4c6---4</link>
            <guid isPermaLink="false">https://medium.com/p/19f251de0f9a</guid>
            <category><![CDATA[agentic-ai]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[llm]]></category>
            <category><![CDATA[genai]]></category>
            <dc:creator><![CDATA[Zayd Islam]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 09:57:49 GMT</pubDate>
            <atom:updated>2026-10-09T09:57:48.315Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Krux kept an encrypted QR’s own text as the passphrase, because UnicodeDecodeError is a ValueError]]></title>
            <link>https://medium.com/codex/krux-kept-an-encrypted-qrs-own-text-as-the-passphrase-because-unicodedecodeerror-is-a-valueerror-8835df5d0a74?source=rss----29038077e4c6---4</link>
            <guid isPermaLink="false">https://medium.com/p/8835df5d0a74</guid>
            <category><![CDATA[hardware-wallet]]></category>
            <category><![CDATA[software-testing]]></category>
            <category><![CDATA[python]]></category>
            <category><![CDATA[cybersecurity]]></category>
            <category><![CDATA[bitcoin]]></category>
            <dc:creator><![CDATA[Muhammad Azhar]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 09:57:46 GMT</pubDate>
            <atom:updated>2026-10-09T09:57:45.373Z</atom:updated>
            <content:encoded><![CDATA[<h4>I ran the fix’s tests on the code before it. The scanned envelope came back as the passphrase with no error, and opened another wallet</h4><p>Scan a QR holding 2NAOXV31B*LJLQIZLVLYD2$M39DERQ91EFD4/32P9IKP at the passphrase prompt of Krux 25.10.0, running under CPython, and those 44 characters come back as your passphrase. They’re the base43 text of an encrypted QR code, and the right key for it had just been typed in. Krux decrypted it, then threw the result away and kept the ciphertext.</p><p>Krux is open-source firmware that turns cheap K210 boards into Bitcoin signing devices. Since 25.09.0 it can decrypt its own encryption envelope, KEF, in most of the places it takes data in, including the menu item that scans a BIP-39 passphrase from a QR. The 25.10.1 changelog has the bug in one line: “Krux encrypted mnemonic as a passphrase is invalid, but no error was raised.” It says the base43 envelope was shown and used as the passphrase, deriving the wrong wallet.</p><p>Yesterday I wrote about <a href="https://medium.com/@azhar.great/krux-shows-the-same-warning-for-a-non-ascii-passphrase-whether-it-opens-the-right-wallet-or-not-0f4292f2b704">what Krux does with a non-ASCII passphrase from a QR</a>. This is the bug that put an ASCII check on that path in the first place.</p><h3>One except clause, two meanings</h3><p>The scan handler runs decrypt_kef(…).decode() inside a try with two except branches. KeyError means the key was wrong, and it shows Failed to decrypt. ValueError carries the comment “not KEF or declined to decrypt” and does nothing, so the scanned text falls through and becomes the passphrase. For a QR that holds a plain passphrase, that’s exactly right.</p><p>The trouble is the .decode(). A Krux encrypted mnemonic holds the entropy as raw bytes rather than the words. The PR that fixed it says what the user actually wanted was the mnemonic as a lowercase, space-separated string. Raw entropy almost never decodes as UTF-8. I tried a million random 16-byte values, the size behind 12 words, and 90 decoded. For 32 bytes, 24 words, none did. And in CPython the error that raises, UnicodeDecodeError, is a subclass of ValueError. So the decode failure landed in the not-KEF branch.</p><figure><img alt="Flow diagram. A QR holding a KEF envelope is scanned at the passphrase prompt and decrypt_kef returns the plaintext, raw entropy bytes. Calling decode on them fails. On CPython the error is UnicodeDecodeError, a subclass of ValueError, so the not KEF branch runs and the 44 character scanned text becomes the passphrase, opening wallet 974aa24d. On MaixPy, per PR 756, a TypeError escapes to the menu, which shows Error: and the exception." src="https://cdn-images-1.medium.com/max/1024/1*j5Gv84TSUN3lCcyqIgsyzQ.png" /><figcaption>One scan, two runtimes. The except branch meant for a plain QR also caught the decode failure on CPython.</figcaption></figure><p>The board didn’t behave the same way. According to the PR, MaixPy raised a TypeError there instead, which neither branch catches, with the message Can’t convert ’int’ object to str implicitely. Krux’s menu catches anything that escapes an item and prints Error: followed by the exception, so on a device the scan should have ended on an error screen. The changelog’s wording, no error and the wrong wallet, matches the simulator.</p><h3>What I ran</h3><p>I exported the 25.10.1 tree, which carries the tests that came with the fix, and ran tests/pages/test_wallet_settings.py: 17 passed. Then I put back the 25.10.0 version of wallet_settings.py and ran it again: 3 failed, 14 passed. One of the three is the case itself, an envelope from the test file that wraps a single 0x8f byte under the key “a”. The fixed code returns to the menu with Failed to load. The old code returned the envelope.</p><p>The test feeds the envelope in as bytes, so I fed the same envelope in as base43 text, the way a QR carries it, and took it on to the fingerprint. The old code handed back the 44-character string with nothing flashed on screen. On the test suite’s 12-word mnemonic that passphrase opens wallet 974aa24d. The same words with no passphrase open 55f8fc5d. Both are valid wallets.</p><figure><img alt="Table with two columns, Krux 25.10.0 code and Krux 25.10.1 code. Test file test_wallet_settings.py: 3 failed and 14 passed, against 17 passed. Scanning the base43 envelope: the 44 character text is returned as the passphrase with nothing flashed, against Failed to load. Fingerprint on the test mnemonic: 974aa24d, against none. The same mnemonic with no passphrase opens 55f8fc5d." src="https://cdn-images-1.medium.com/max/1024/1*b_667SiEvpxrFhVp2R_qQg.png" /><figcaption>The fix’s own tests and my probe, run against the code before and after it.</figcaption></figure><p>The confirmation screen wasn’t hiding anything, to be fair to it. It showed the fingerprint, Passphrase (44): and the string itself above Proceed?. Someone expecting twelve lowercase words would see base43 and stop, unless they’d long since stopped reading that screen.</p><h3>Forty-five days, and a fix that was later softened</h3><p>KEF decryption reached the passphrase scanner in commit ccb65290 on 23 May 2025 and first shipped in 25.09.0 on 15 September. tadeubas found the bug, jdlcdl’s PR #756 was opened and merged on 29 October, and 25.10.1 went out on 30 October. That’s 45 days in a release.</p><p>The fix decodes the decrypted bytes in a try of their own and fails with Failed to load. The wallet descriptor and address loaders had the identical line and got the same change. It also added a check that refused any passphrase with a byte over 126. In 26.03.0, five months later, that refusal became a warning you can click through, which is where yesterday’s piece picks it up.</p><figure><img alt="Timeline. 23 May 2025, commit ccb65290 adds KEF decryption to the passphrase scanner. 15 September 2025, Krux 25.09.0 ships it. 29 October, PR 756 opened and merged. 30 October, 25.10.1 ships the fix and refuses any passphrase byte over 126. 25 March 2026, 26.03.0 turns that refusal into a warning. The bug was in a release for 45 days." src="https://cdn-images-1.medium.com/max/1024/1*4SXWtAyh-7o386iMMVa6aA.png" /><figcaption>From the commit that added KEF to the scanner to the release that softened the fix.</figcaption></figure><p>If you write Python that also has to run on MicroPython, this is the pattern to grep for: an except ValueError meant for one failure, around a call that can also raise UnicodeDecodeError. On CPython it swallows the decode error. On this board it raised something else entirely, and the loud failure was the one on the device.</p><h3>What I didn’t check</h3><p>I didn’t run any of this on a K210. The device behaviour is the PR author’s account, who says it was built and tested on the simulator and an Amigo, read against Krux’s menu code. I don’t know how many people scanned an encrypted mnemonic at that prompt in those 45 days, and nothing public says.</p><p>Krux source read at tags v25.09.0, v25.10.0, v25.10.1 and v26.03.0, release dates from the GitHub API. Tests and the decode count run 8 October 2026 on CPython 3.13.6 with pytest 9.1.1.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8835df5d0a74" width="1" height="1" alt=""><hr><p><a href="https://medium.com/codex/krux-kept-an-encrypted-qrs-own-text-as-the-passphrase-because-unicodedecodeerror-is-a-valueerror-8835df5d0a74">Krux kept an encrypted QR’s own text as the passphrase, because UnicodeDecodeError is a ValueError</a> was originally published in <a href="https://medium.com/codex">CodeX</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Gantt Charts as Code: Mermaid, Make and the Fortnightly Progress Report]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/codex/gantt-charts-as-code-mermaid-make-and-the-fortnightly-progress-report-7cf6066d7e35?source=rss----29038077e4c6---4"><img src="https://cdn-images-1.medium.com/max/1344/0*SaBGtS9Safo8eZsR.png" width="1344"></a></p><p class="medium-feed-snippet">Every other Friday morning for about two years I updated the same Gantt chart.</p><p class="medium-feed-link"><a href="https://medium.com/codex/gantt-charts-as-code-mermaid-make-and-the-fortnightly-progress-report-7cf6066d7e35?source=rss----29038077e4c6---4">Continue reading on CodeX »</a></p></div>]]></description>
            <link>https://medium.com/codex/gantt-charts-as-code-mermaid-make-and-the-fortnightly-progress-report-7cf6066d7e35?source=rss----29038077e4c6---4</link>
            <guid isPermaLink="false">https://medium.com/p/7cf6066d7e35</guid>
            <dc:creator><![CDATA[Brian Jones]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 09:57:27 GMT</pubDate>
            <atom:updated>2026-10-09T09:57:26.282Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[How to Use ellmer with LM Studio for AI-Assisted Exploratory Data Analysis in R]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/codex/how-to-use-ellmer-with-lm-studio-for-ai-assisted-exploratory-data-analysis-in-r-3ccc805215b5?source=rss----29038077e4c6---4"><img src="https://cdn-images-1.medium.com/max/2600/1*aOIfphbYTsAxTGvDL4p2oA.jpeg" width="2752"></a></p><p class="medium-feed-snippet">Run AI-Assisted Data Summaries Locally with ellmer, LM Studio, and Phi-4</p><p class="medium-feed-link"><a href="https://medium.com/codex/how-to-use-ellmer-with-lm-studio-for-ai-assisted-exploratory-data-analysis-in-r-3ccc805215b5?source=rss----29038077e4c6---4">Continue reading on CodeX »</a></p></div>]]></description>
            <link>https://medium.com/codex/how-to-use-ellmer-with-lm-studio-for-ai-assisted-exploratory-data-analysis-in-r-3ccc805215b5?source=rss----29038077e4c6---4</link>
            <guid isPermaLink="false">https://medium.com/p/3ccc805215b5</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[data-analysis]]></category>
            <category><![CDATA[business-intelligence]]></category>
            <dc:creator><![CDATA[Pierre DeBois]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 09:57:22 GMT</pubDate>
            <atom:updated>2026-10-09T09:57:21.577Z</atom:updated>
        </item>
    </channel>
</rss>