<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Walter Hrad</title>
    <description>The latest articles on DEV Community by Walter Hrad (@blakcodes).</description>
    <link>https://dev.to/blakcodes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3842736%2F943c54b1-f289-441f-b6aa-b1d1feb9f3f8.jpg</url>
      <title>DEV Community: Walter Hrad</title>
      <link>https://dev.to/blakcodes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/blakcodes"/>
    <language>en</language>
    <item>
      <title>Every Time You Run npm install, You Are Trusting Hundreds of Strangers</title>
      <dc:creator>Walter Hrad</dc:creator>
      <pubDate>Mon, 06 Jul 2026 07:44:43 +0000</pubDate>
      <link>https://dev.to/blakcodes/every-time-you-run-npm-install-you-are-trusting-hundreds-of-strangers-1bg6</link>
      <guid>https://dev.to/blakcodes/every-time-you-run-npm-install-you-are-trusting-hundreds-of-strangers-1bg6</guid>
      <description>&lt;p&gt;Most developers have a ritual with new projects. You clone the repo, change into the directory, type npm install, and watch the terminal fill up with download progress. It is automatic. Comfortable. So routine that you stop thinking about it entirely.&lt;/p&gt;

&lt;p&gt;That comfort is exactly what attackers have been counting on.&lt;/p&gt;

&lt;p&gt;This year has been brutal for the JavaScript ecosystem in ways that have not gotten enough attention outside of security circles. The Axios library, which sits in roughly 174,000 downstream packages and pulls in somewhere around 100 million weekly downloads, was compromised in March 2026 through stolen maintainer credentials. A malicious dependency called plain-crypto-js was injected into two versions, and it downloaded multi-stage payloads including a remote access trojan onto machines belonging to developers who had no idea anything had changed. CISA issued an advisory. Most people just ran npm update and moved on.&lt;/p&gt;

&lt;p&gt;That was not an isolated incident. It was part of a pattern that has been accelerating for over a year.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Shai-Hulud Problem
&lt;/h2&gt;

&lt;p&gt;In September 2025, a campaign compromised 18 widely used packages including debug and chalk. If you have ever worked on a Node.js project you have used those packages. They are not optional utilities most teams choose to include. They are woven into the dependency trees of virtually everything. Combined weekly downloads at the time exceeded 2.6 billion. The attackers did not need to trick anyone into installing something new. They just had to poison something developers were already trusting implicitly.&lt;/p&gt;

&lt;p&gt;The campaign that became known as Shai-Hulud took things further. What made it especially dangerous was its self-propagation mechanism. Once a developer installed an infected package, the malicious preinstall script would harvest that developer's npm tokens and GitHub credentials, then use those credentials to publish poisoned versions of every package that developer maintained. The worm spread not through a traditional exploit chain but through the trust relationships baked into the npm ecosystem itself. By the time multiple waves of the campaign had played out, it had touched 796 packages with a combined 132 million monthly downloads.&lt;/p&gt;

&lt;p&gt;The June 2026 Miasma attack introduced a refinement called the Phantom Gyp technique. An attacker places a 157-byte binding.gyp file inside a compromised package. That file triggers npm's implicit node-gyp rebuild call, an automatic C/C++ compilation step that npm runs for any package containing that file. The critical detail is that this happens with no lifecycle script required and with no protection from --ignore-scripts, which was the flag developers had been told would protect them. The Miasma worm used this technique to compromise 57 packages in a second wave after the initial infection, all within a two-hour window, targeting Red Hat's official @redhat-cloud-services namespace.&lt;/p&gt;

&lt;p&gt;These were not obscure packages from unknown maintainers. They were packages from organisations developers had every reason to trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Architecture Makes This So Difficult
&lt;/h2&gt;

&lt;p&gt;The underlying problem is structural. When you run npm install, the package manager hands execution rights to every package in your dependency tree. Not just the packages you chose to install directly. Every transitive dependency. Packages you have never read, maintained by people you have never heard of, several levels deep in a tree you probably have not audited. The average npm project pulls in 79 transitive dependencies.&lt;/p&gt;

&lt;p&gt;Each of those packages can define preinstall, install, and postinstall lifecycle scripts. Those scripts run automatically and silently with the full permissions of the user running the install. On a developer workstation that might mean access to AWS credentials sitting in environment variables, SSH keys, GitHub tokens stored by the git credential manager, CI/CD secrets, and cloud provider configurations. On a build server the situation is often worse, because build servers tend to have elevated access to production infrastructure by design.&lt;/p&gt;

&lt;p&gt;GitHub themselves described this as the single largest code-execution surface in the npm ecosystem. That framing is worth sitting with. Not the largest vulnerability. The largest code-execution surface. Meaning the attack does not require finding a bug. It requires getting code into a package, which is significantly easier.&lt;/p&gt;

&lt;p&gt;Attackers have three main ways to do that. First is account compromise, where a maintainer's credentials get stolen and used to publish a malicious version of a legitimate package. Second is dependency confusion, where an attacker publishes a public package with the same name as an internal private package, exploiting the way some package managers resolve scoped dependencies. Third is typosquatting, where a malicious package is published under a name close enough to a popular one that a developer with a typo in their install command will pull it in instead.&lt;/p&gt;

&lt;p&gt;In late May 2026, Microsoft Threat Intelligence documented a dependency confusion campaign where a threat actor published malicious packages under organisational scopes that mirrored real internal corporate namespaces. The packages spoofed internal enterprise infrastructure URLs in their package.json to appear legitimate. One maintainer account involved had been sitting dormant since April 2024, when it was registered under bug bounty research branding. Two years later the same account was pushing obfuscated credential-stealing payloads. The patience involved is not reassuring.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Happens When You Get Hit
&lt;/h2&gt;

&lt;p&gt;The Shai-Hulud payload execution chain is worth understanding in detail because it illustrates how far beyond simple credential theft these attacks have evolved.&lt;/p&gt;

&lt;p&gt;The infection starts during npm install, where the malicious preinstall hook executes a loader script without any user interaction. The payload unpacks through multiple decoding layers, using ROT-based obfuscation variants followed by AES-128-GCM decryption. It then downloads the Bun JavaScript runtime and detonates the final payload. Before continuing, the malware validates the execution environment and can restrict itself to CI/CD environments only, which means some developer machines may never see any obvious signs of infection while build pipelines are being quietly drained.&lt;/p&gt;

&lt;p&gt;The self-propagation step is where it turns into a worm. The malware republishes packages owned by the compromised maintainer using forged provenance metadata, which means it spreads wearing the identity of a trusted publisher. In one campaign there was also a destructive tripwire: if the malware detected interaction with a planted decoy token, it triggered a command that wiped the victim's home directory.&lt;/p&gt;

&lt;p&gt;The stolen credentials cascade in a specific way. Stolen npm tokens enable further package poisoning. Stolen GitHub tokens enable repository manipulation. Stolen AWS credentials enable cloud access. Each layer of compromise opens the next one, and by the time a security team detects unusual activity in one place, multiple systems are often already affected.&lt;/p&gt;

&lt;p&gt;In 2025 alone, attackers published nearly 455,000 malicious npm packages. Over 99 percent of all open source malware now targets npm according to Sonatype's 2026 report. This is not a niche concern for enterprise security teams. It is the environment every JavaScript developer is working in every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The npm v12 Response
&lt;/h2&gt;

&lt;p&gt;GitHub announced earlier this month that npm v12, shipping in July 2026, will flip three defaults that have been permissive since npm was created.&lt;/p&gt;

&lt;p&gt;The first and most significant change is that install scripts will be blocked by default. Running npm install will no longer automatically execute preinstall, install, or postinstall scripts from dependencies unless they are explicitly allowed in your project. This includes native node-gyp builds triggered by binding.gyp files, which closes the Phantom Gyp vector directly. The second change blocks Git dependencies by default. The third blocks remote URL dependencies like HTTPS tarballs.&lt;/p&gt;

&lt;p&gt;The mechanism for managing this is a new command called npm approve-scripts. You run it, it shows you which packages in your dependency tree have scripts that are not yet covered by your policy, and you approve the ones you have actually reviewed. Those approvals get committed to your package.json and checked into source control, creating a documented record of which packages your team has explicitly trusted to execute code at install time.&lt;/p&gt;

&lt;p&gt;The security argument for this is clear. Only about 2 percent of npm packages actually need install scripts to function. The vast majority use them as a convenience shortcut for build steps that could be performed explicitly. For those 2 percent, adding them to an explicit allowlist is a one-time cost. For the other 98 percent, blocking execution by default removes the attack surface entirely.&lt;/p&gt;

&lt;p&gt;The friction argument against it is also real. Paul McCarty, a vulnerability researcher who has been tracking npm supply chain attacks closely, put it plainly: when the choice is between a build that works and a build that is less prone to malware, the former will always win. Developers under deadline pressure who encounter blocked scripts are likely to approve everything with a blanket flag just to make the warnings go away, which defeats the purpose entirely. The tools exist to do this correctly, but tooling availability and tooling adoption are different things.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Should Actually Do Before July
&lt;/h2&gt;

&lt;p&gt;If you are running the npm CLI today, you are relying entirely on publisher-side defences and assuming nothing slips through the registry. That assumption has been wrong repeatedly this year.&lt;/p&gt;

&lt;p&gt;The most immediate thing you can do is upgrade to npm 11.16.0, which enables advisory mode for the v12 changes. In advisory mode, warnings surface but nothing breaks yet. That gives you visibility into which packages in your dependency tree have scripts running during install, without forcing you to fix everything before you understand the scope of the problem.&lt;/p&gt;

&lt;p&gt;Run npm approve-scripts --allow-scripts-pending in read-only mode first. It lists every package whose scripts are not yet covered by your policy without changing anything. Go through that list and think carefully about which packages actually need to execute code at install time. Most of them do not. Approve the ones that do, pinned to the specific version you have reviewed, and commit the result.&lt;/p&gt;

&lt;p&gt;Switch --ignore-scripts on for your CI pipelines now rather than waiting for v12. Anything that breaks when you do that is a package that was silently running code in your build environment, and you should know which packages those are.&lt;/p&gt;

&lt;p&gt;Consider moving to pnpm or Yarn Berry if you have not already. Both have been shipping consumer-side security defaults for some time. pnpm v11, released in April 2026, unified its security configuration into a cleaner API and blocks lifecycle scripts by default unless you explicitly allowlist them. The npm CLI is the only major package manager that had not shipped these consumer-side protections until the v12 announcement. The alternatives were ahead of this problem.&lt;/p&gt;

&lt;p&gt;For packages your team publishes, set up trusted publishing if you have not already. Trusted publishing ties a package release to a verified CI/CD workflow rather than to credentials that can be stolen from a developer's machine. It does not prevent every attack vector but it raises the bar significantly for account compromise scenarios.&lt;/p&gt;

&lt;p&gt;Pin your dependency versions in CI. A package that was clean when you last audited it is not guaranteed to be clean after an update. Automated dependency updates through tools like Dependabot are useful for staying current with security patches, but they should go through a review step rather than merging automatically. The Axios attack left detectable signals in the registry metadata before anyone ran npm install, including a missing trusted publisher block and a missing gitHead field. Tools like Socket and StepSecurity surface these signals automatically. Without those tools, checking a package's metadata manually takes about thirty seconds per package and would have caught that attack before it ran.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Deeper Shift
&lt;/h2&gt;

&lt;p&gt;What is happening in the npm ecosystem right now is a forced reckoning with a trust model that was designed for a different era. When npm was built, the open source ecosystem operated on a foundation of good faith. Packages were published by developers who wanted to share useful tools. The idea that someone would weaponise that distribution mechanism at industrial scale was not a primary design consideration.&lt;/p&gt;

&lt;p&gt;That era is over. The attacks in 2025 and 2026 are industrialised, coordinated, and in some cases state-linked. The Shai-Hulud campaigns showed self-replicating worm behaviour. The dependency confusion attacks showed patience and careful reconnaissance. The Bitwarden impersonation package that was part of one Shai-Hulud wave shows attackers studying which tools developers trust specifically to impersonate them.&lt;/p&gt;

&lt;p&gt;npm v12 is a structural response to a structural problem. Blocking install scripts by default, requiring explicit allowlists, and closing the Phantom Gyp loophole are not complete solutions but they remove the path of least resistance that attackers have been using repeatedly. The era of implicit trust in package installation is ending whether developers are ready for it or not.&lt;/p&gt;

&lt;p&gt;July is not far away. The question is whether you find out what is running in your dependency tree on your own terms, or whether npm v12 breaks your CI pipeline on the first day it ships and you figure it out under pressure.&lt;/p&gt;

&lt;p&gt;The inventory is not that hard. The install is the thing you have been doing automatically for years without thinking about it. It is worth thinking about it now.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>opensource</category>
      <category>security</category>
    </item>
    <item>
      <title>How Git Actually Works Under the Hood</title>
      <dc:creator>Walter Hrad</dc:creator>
      <pubDate>Sun, 05 Jul 2026 12:26:54 +0000</pubDate>
      <link>https://dev.to/blakcodes/-how-git-actually-works-under-the-hood-o8k</link>
      <guid>https://dev.to/blakcodes/-how-git-actually-works-under-the-hood-o8k</guid>
      <description>&lt;p&gt;Most developers use Git every day and understand almost none of it. That's not an insult, it's just the reality of how most people learn tools. You pick up the commands that get you through the day, you memorize the ones that fix the situations you keep breaking, and you build a working mental model that is almost entirely wrong at the mechanical level.&lt;/p&gt;

&lt;p&gt;The mental model most people carry looks something like this: Git tracks changes to files. When you commit, it saves a snapshot of what changed. Branches are pointers to different lines of work. That's roughly correct at a surface level, but it skips over the actual machinery in a way that leaves you confused every time something unexpected happens. Why does rebasing rewrite history? Why are commits immutable? Why does detached HEAD state exist? Why can you lose work in ways that feel impossible if Git is just tracking changes?&lt;/p&gt;

&lt;p&gt;The answers are all in the object model, and the object model is surprisingly simple once you sit with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Git is a content-addressable filesystem
&lt;/h2&gt;

&lt;p&gt;Before any of the version control concepts, Git is a key-value store. You put content in, you get a hash back. You use that hash later to retrieve the content. That's the entire foundation, and everything else is built on top of it.&lt;/p&gt;

&lt;p&gt;The hash Git uses is SHA-1, producing a 40-character hexadecimal string. When you run &lt;code&gt;git hash-object&lt;/code&gt; on a file, Git takes the content, prepends a small header describing the object type and size, and runs SHA-1 over the whole thing. The resulting hash is both the key and the identity of that content. Two files with identical content will always produce the same hash. A file whose content changes even slightly will produce a completely different hash.&lt;/p&gt;

&lt;p&gt;This is the first thing that breaks people's mental models. In most storage systems, identity is location: a file is "that file" because it lives at that path. In Git's object store, identity is content. The path a file lives at is separate metadata, not the file's identity.&lt;/p&gt;

&lt;p&gt;All of Git's objects live in &lt;code&gt;.git/objects&lt;/code&gt;. Go look at it sometime on a real repository. You'll find subdirectories named with two-character hex prefixes, and inside each one, files named with the remaining 38 characters of various hashes. Each of those files is a compressed Git object. The entire history of your project, every version of every file that ever existed, every commit, every tree, is sitting right there in that directory as a pile of content-addressed blobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The four object types
&lt;/h2&gt;

&lt;p&gt;Git has exactly four types of objects: blobs, trees, commits, and tags. That's it. The entire version control system is built from those four things.&lt;/p&gt;

&lt;h3&gt;
  
  
  Blobs
&lt;/h3&gt;

&lt;p&gt;A blob is file content, nothing else. Not a filename, not a path, not permissions. Just the raw bytes of a file at a particular moment in time.&lt;/p&gt;

&lt;p&gt;If you have two files in your repository with the same content, they share a single blob. Git doesn't store duplicates. The filename and location of those files are stored elsewhere. The blob itself is just content.&lt;/p&gt;

&lt;p&gt;This is why Git is efficient in ways that surprise people. If you have a thousand files and you change one of them, Git only needs to store one new blob. The other 999 files haven't changed, so their blobs already exist in the object store, and the new tree structure just references them by hash. Nothing is copied.&lt;/p&gt;

&lt;p&gt;You can look at a blob directly using &lt;code&gt;git cat-file -p &amp;lt;hash&amp;gt;&lt;/code&gt;. Run &lt;code&gt;git ls-files --stage&lt;/code&gt; in any repository and you'll see the blob hashes for every file in your current index alongside their filenames and modes. Pick any hash from that list and cat-file it, and you'll see the raw file content.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trees
&lt;/h3&gt;

&lt;p&gt;A tree is Git's representation of a directory. It contains a list of entries, where each entry is a mode, a type, a hash, and a name. The type is either blob or tree, because directories can contain files and other directories.&lt;/p&gt;

&lt;p&gt;A tree for a simple directory with two files and one subdirectory might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;100644 blob a8c6a8d9...    README.md
100644 blob 3f1b2c4e...    main.go
040000 tree 9d2e1f7a...    internal
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tree has a hash. The subdirectory &lt;code&gt;internal&lt;/code&gt; is itself another tree object with its own hash. That tree has a hash. Every file in it is a blob with a hash. The whole directory structure of your project at any given moment is represented as a tree of hashes pointing to other hashes.&lt;/p&gt;

&lt;p&gt;This structure is a Merkle tree, the same data structure that shows up in Bitcoin and a lot of other systems where you need to verify large amounts of data efficiently. If any blob anywhere in your directory tree changes, its hash changes, which changes the hash of the tree containing it, which changes the hash of any parent tree, which changes the hash of the root tree. The root tree hash is a fingerprint of your entire directory structure at that moment. If two root tree hashes are equal, every file in every directory is byte-for-byte identical.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commits
&lt;/h3&gt;

&lt;p&gt;A commit object contains four things: a pointer to a root tree, zero or more pointers to parent commits, author and committer metadata, and a commit message.&lt;/p&gt;

&lt;p&gt;The pointer to the root tree is what gives a commit its snapshot of the entire project. When you check out a commit, Git reads that commit's tree hash, then recursively resolves all the trees and blobs in it, and reconstructs your working directory from those objects. There's no concept of a "diff" in the object store. Every commit has a full snapshot, but because blobs and trees are deduplicated by content, the actual storage cost of a new commit is only the objects that didn't exist before.&lt;/p&gt;

&lt;p&gt;The pointer to parent commits is what makes the history graph. A regular commit has one parent, the previous commit. A merge commit has two or more parents, one for each branch that was merged. The first commit in a repository has no parent.&lt;/p&gt;

&lt;p&gt;This is the second thing that breaks mental models. The history graph is not a sequence of diffs. It is a directed acyclic graph of snapshots, where each node contains a full picture of the entire project, and edges point backwards in time to parent commits. When you ask Git to show you what changed between two commits, it reconstructs both snapshots from their respective trees and computes the diff on the fly. The diff is not stored anywhere. It's derived.&lt;/p&gt;

&lt;p&gt;Commits are also immutable. Once a commit object exists with a given hash, it cannot be changed, because any change to its content would change its hash, making it a different object. When you amend a commit, you're not modifying the existing commit. You're creating a new commit object and moving the branch pointer to it. The old commit still exists in the object store until it gets garbage collected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tags
&lt;/h3&gt;

&lt;p&gt;Annotated tags are the fourth object type. A tag object contains a pointer to another object (usually a commit), along with a tagger name, date, and message. Like commits, annotated tags are signed references with metadata attached.&lt;/p&gt;

&lt;p&gt;Lightweight tags, the ones you create with &lt;code&gt;git tag &amp;lt;name&amp;gt;&lt;/code&gt; without the &lt;code&gt;-a&lt;/code&gt; flag, are not tag objects at all. They're just references, which we'll get to in a moment.&lt;/p&gt;

&lt;h2&gt;
  
  
  References: the human layer on top of hashes
&lt;/h2&gt;

&lt;p&gt;Hashes are how Git thinks about objects internally. Humans are bad at hashes. References are the layer that makes Git usable for humans.&lt;/p&gt;

&lt;p&gt;A reference is a file containing a hash. That's all it is. The file &lt;code&gt;.git/refs/heads/main&lt;/code&gt; contains the SHA-1 hash of the commit that the main branch currently points to. The file &lt;code&gt;.git/refs/tags/v1.0.0&lt;/code&gt; contains a hash. The file &lt;code&gt;.git/HEAD&lt;/code&gt; contains either a hash (when you're in detached HEAD state) or a symbolic reference to another ref file (when you're on a branch, it contains something like &lt;code&gt;ref: refs/heads/main&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;When you create a new commit on the main branch, Git creates the commit object, writes its hash into &lt;code&gt;.git/refs/heads/main&lt;/code&gt;, and that's the entire operation of "advancing the branch." The branch didn't grow. The branch pointer moved.&lt;/p&gt;

&lt;p&gt;Branches in Git are not containers. They're not timelines. They're not parallel universes of code. They're a single file containing a single hash. When people say a branch is "just a pointer," they're being completely literal. Go look at &lt;code&gt;.git/refs/heads&lt;/code&gt; in any repository. Every branch is a file. Open any of those files. It contains exactly one hash.&lt;/p&gt;

&lt;p&gt;This is why creating and deleting branches in Git is so cheap compared to other version control systems. There's no data to copy, no history to replicate. Creating a branch is creating a file. Deleting a branch is deleting a file. The commits the branch pointed to are unaffected, they still exist in the object store.&lt;/p&gt;

&lt;h2&gt;
  
  
  HEAD and what detached HEAD actually means
&lt;/h2&gt;

&lt;p&gt;HEAD is a special reference that tells Git where you currently are. In the normal case, HEAD contains a symbolic reference to a branch, something like &lt;code&gt;ref: refs/heads/main&lt;/code&gt;. Git calls this being "attached" to a branch. When you make a commit, Git creates the commit object, updates the branch ref to point to it, and HEAD follows because HEAD points to the branch, not directly to a commit.&lt;/p&gt;

&lt;p&gt;Detached HEAD happens when HEAD contains a commit hash directly instead of a branch reference. This happens when you check out a specific commit, a tag, or a remote tracking branch. Git tells you about it because it has consequences: if you make commits in this state, those commits don't belong to any branch. They exist in the object store, and HEAD advances as you commit, but no named reference tracks them. When you switch back to a branch, HEAD moves to that branch, and your detached commits are now orphaned. They'll get garbage collected eventually unless you create a branch pointing to them first.&lt;/p&gt;

&lt;p&gt;Once you understand what HEAD actually is, detached HEAD goes from a scary warning to a completely sensible description of what's happening. You're not attached to anything. You're floating at a specific commit with no branch to record where you go from here.&lt;/p&gt;

&lt;h2&gt;
  
  
  How staging actually works
&lt;/h2&gt;

&lt;p&gt;The staging area, also called the index, is one of the more misunderstood parts of Git. Most people think of it as a temporary holding area for changes on the way to a commit. That's not wrong, but it undersells what it actually is.&lt;/p&gt;

&lt;p&gt;The index is a binary file at &lt;code&gt;.git/index&lt;/code&gt; that represents a complete snapshot of your project. It contains an entry for every tracked file: the file's path, its mode, its blob hash, and some stat information from the filesystem that Git uses to detect changes without hashing every file on every status check.&lt;/p&gt;

&lt;p&gt;When you run &lt;code&gt;git add&lt;/code&gt;, Git takes the current content of that file, creates a blob object for it in the object store, and updates the index entry for that file to point to the new blob hash. Nothing else happens. No commit is created. Just a blob object and an updated index entry.&lt;/p&gt;

&lt;p&gt;When you run &lt;code&gt;git commit&lt;/code&gt;, Git takes the current state of the index, constructs a tree object hierarchy from it, creates a commit object pointing to the root tree and the current HEAD commit as parent, writes the new commit hash to the current branch ref, and updates HEAD.&lt;/p&gt;

&lt;p&gt;The index is essentially a proposed tree. It's the snapshot you're about to commit. That's why partial staging makes sense mechanically: you're selectively updating the index to contain some changes but not others, constructing the exact snapshot you want the commit to represent.&lt;/p&gt;

&lt;h2&gt;
  
  
  How merging works
&lt;/h2&gt;

&lt;p&gt;A merge takes two commits and produces a new commit with both of them as parents. But to know what to put in that new commit's tree, Git has to figure out what changed in each branch relative to their common ancestor.&lt;/p&gt;

&lt;p&gt;Finding that common ancestor is the job of the merge base algorithm. Git walks the commit graph backwards from both commits simultaneously, looking for the first commit that appears in both paths. Once it has the merge base, it diffs each branch tip against the merge base to find what changed in each, then applies both sets of changes to produce the merged result.&lt;/p&gt;

&lt;p&gt;When the two branches changed different parts of different files, the merge is automatic. When they changed overlapping parts of the same file, you get a conflict, because Git can't automatically decide whose change should win.&lt;/p&gt;

&lt;p&gt;A fast-forward merge is a special case where one commit is a direct ancestor of the other. If you're on main and you merge a feature branch, and main hasn't moved since you branched off, there are no divergent changes to combine. Git can just move the main pointer forward to the feature branch's tip. No merge commit is created because no actual merging needed to happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  How rebasing works and why it rewrites history
&lt;/h2&gt;

&lt;p&gt;Rebasing is where a lot of people get confused, and the confusion usually comes from thinking about branches as containers rather than pointers.&lt;/p&gt;

&lt;p&gt;When you rebase a branch onto another, Git takes the commits in your branch that aren't in the target, and replays them one by one on top of the target. For each commit, it computes the diff between that commit and its parent, then applies that diff on top of the current tip of the target, creating a new commit object.&lt;/p&gt;

&lt;p&gt;The key word is "new." The replayed commits are new objects. They have different parent hashes (because their parents changed), which means they have different hashes themselves. The original commits still exist in the object store, they're just no longer reachable from any branch. You haven't moved your commits. You've created copies of them in a new position and moved your branch pointer to the last copy.&lt;/p&gt;

&lt;p&gt;This is what "rewriting history" means. The commits that exist after a rebase are different objects than the commits that existed before, even if their content is identical. If someone else had pulled your branch before you rebased, they have references to the old commits. After your rebase, those old commits no longer appear in your branch history. Their local branch and your remote branch have diverged. This is why rebasing shared branches causes problems: you've replaced the objects other people are pointing at with new objects, and their local Git has no way of knowing the new ones are meant to replace the old ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  How git gc and the reflog save you
&lt;/h2&gt;

&lt;p&gt;Git's object store is append-only during normal operation. Every blob, tree, commit, and tag you ever create lives in &lt;code&gt;.git/objects&lt;/code&gt; until Git decides to clean up. Objects that aren't reachable from any reference are called loose objects or dangling objects, and they accumulate over time from operations like amending commits, rebasing, and resetting branches.&lt;/p&gt;

&lt;p&gt;The reflog is what gives you a window to recover from those operations before cleanup happens. Every time HEAD or a branch ref moves, Git appends an entry to the reflog recording where the ref was before and where it moved to. The reflog for HEAD lives at &lt;code&gt;.git/logs/HEAD&lt;/code&gt;. Run &lt;code&gt;git reflog&lt;/code&gt; and you'll see a timestamped history of every position HEAD has ever been at in that repository.&lt;/p&gt;

&lt;p&gt;When you do a hard reset and realize you needed those commits, or you rebase and want to get back to the pre-rebase state, the reflog is how you find the hash of the commit you want to return to. That commit still exists in the object store. You just need the hash to reach it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;git gc&lt;/code&gt; is the garbage collector. It finds objects with no references pointing to them (directly or through the reflog, which has its own expiry) and deletes them. By default, objects that are more than 30 days old and unreachable from any ref or reflog entry are eligible for collection. This is the window you have to recover from mistakes. It's also why truly losing work in Git is harder than it feels in the moment: Git is quite conservative about actually deleting anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Packfiles and how Git stores history efficiently
&lt;/h2&gt;

&lt;p&gt;If every object is stored as a separate compressed file, large repositories with long histories would be enormous. Git handles this with packfiles.&lt;/p&gt;

&lt;p&gt;A packfile is a single file that contains many objects stored together with delta compression. Instead of storing each version of a file as a complete compressed blob, Git can store one version in full and then store other versions as deltas relative to it. For files that change incrementally over time, this is dramatically more space efficient than storing each version separately.&lt;/p&gt;

&lt;p&gt;Packfiles get created by &lt;code&gt;git gc&lt;/code&gt;, by &lt;code&gt;git repack&lt;/code&gt;, and automatically by Git when the number of loose objects crosses a threshold. When Git reads from a packfile, it reconstructs the requested object on the fly from whatever deltas are needed. From your perspective as a user, this is invisible. You use the same commands, request objects by the same hashes, and Git handles the physical storage transparently.&lt;/p&gt;

&lt;p&gt;The index file that accompanies each packfile allows Git to binary search for any object hash in the pack without reading the entire file. This is how Git can efficiently access any object in a repository with millions of commits and hundreds of thousands of files.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this changes about how you use Git
&lt;/h2&gt;

&lt;p&gt;Understanding the object model doesn't make you memorize fewer commands. It does make the commands make sense in a way that changes how you work.&lt;/p&gt;

&lt;p&gt;You stop being afraid of rebasing once you understand it's just replaying commits in a new position, not shuffling some fragile linear history. You stop being afraid of resetting once you know the reflog has your back and objects don't disappear immediately. You stop being confused by detached HEAD because you know exactly what HEAD is and what "detached" describes. You understand why &lt;code&gt;git push --force&lt;/code&gt; is dangerous on shared branches, because you understand that it replaces remote refs with hashes pointing to new objects, orphaning whatever the old hashes pointed to.&lt;/p&gt;

&lt;p&gt;You also start reading error messages differently. When Git says "your branch and origin/main have diverged," you know that means the commit graphs have branched: the local ref and the remote ref point to different commits that don't have a simple ancestor relationship. When Git says a ref is not valid, you can go look in &lt;code&gt;.git/refs&lt;/code&gt; and see for yourself what's there and what isn't. When you need to find a lost commit, you know to check the reflog rather than feeling like something is gone forever.&lt;/p&gt;

&lt;p&gt;Git's design is not arbitrary. The object model, the content addressing, the immutability of objects, the lightweight references, the reflog, the packfiles: all of it fits together into a system that is remarkably consistent once you have the right mental model. The learning curve exists because most people learn Git from the outside, picking up commands without ever looking at what the commands are actually doing to the files in &lt;code&gt;.git&lt;/code&gt;. Going the other direction, starting with the objects and working outward to the commands, is a slower way to start but a much more durable way to understand.&lt;/p&gt;

&lt;p&gt;The next time something goes wrong in Git and your instinct is to blow away the repository and clone fresh, stop and think about what objects exist and what references point to what. The answer to what happened and how to fix it is almost always there, in plain text, sitting in &lt;code&gt;.git&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>git</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Nobody Wants Your 70B Parameter Model Anymore</title>
      <dc:creator>Walter Hrad</dc:creator>
      <pubDate>Sat, 20 Jun 2026 20:18:16 +0000</pubDate>
      <link>https://dev.to/blakcodes/nobody-wants-your-70b-parameter-model-anymore-56jo</link>
      <guid>https://dev.to/blakcodes/nobody-wants-your-70b-parameter-model-anymore-56jo</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnjslfm36ecrbfk5wpwkt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnjslfm36ecrbfk5wpwkt.jpg" alt=" " width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For a while the entire AI conversation was about scale. Bigger model, bigger context window, bigger benchmark score, bigger headline. If you weren't training something with a parameter count that needed scientific notation, you weren't really in the game.&lt;/p&gt;

&lt;p&gt;That story is quietly falling apart, and I don't think enough people are talking about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing nobody admits about huge models
&lt;/h2&gt;

&lt;p&gt;Massive general purpose models are genuinely impressive, but most of what they're impressive at, you don't actually need. If you're building a voice assistant for a car dashboard, you don't need a model that can write sonnets about quantum mechanics. You need something that reliably understands "turn the AC down" and runs without melting the car's battery.&lt;/p&gt;

&lt;p&gt;That mismatch between what big models offer and what most real products need has been sitting there for years, mostly ignored because everyone was chasing the same leaderboard. What's changed recently is that the tooling finally caught up to the obvious idea: train something small, train it well, and point it at one job.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality of data beats size of model
&lt;/h2&gt;

&lt;p&gt;The thing that broke this open wasn't a clever new architecture, it was a boring realization. A handful of labs proved that a model trained on a few billion carefully chosen, high quality tokens can go toe to toe with models many times its size trained on whatever could be scraped off the internet.&lt;/p&gt;

&lt;p&gt;That's a strange thing to sit with if you came up believing more data and more parameters was the whole game. It turns out a smaller model trained like a sharp student studying good textbooks beats a bigger model trained like someone speed reading the entire internet once.&lt;/p&gt;

&lt;p&gt;This is the same lesson every engineer eventually learns the hard way. Throwing more resources at a problem is rarely as effective as understanding the problem better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this actually matters
&lt;/h2&gt;

&lt;p&gt;A few places where small, specialized models are quietly taking over instead of staying a research curiosity:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;On-device assistants.&lt;/strong&gt; Phones now ship with dedicated AI hardware that can run a few billion parameter model directly, no network call required. That means your voice assistant keeps working in a tunnel, on a flight, or somewhere with terrible signal, which honestly describes a lot of where I live and work. There's something almost funny about it too. For years the assumption was that intelligence lived in the cloud and your phone was just a window into it. Now the phone itself is quietly doing real reasoning, locally, while you're not even thinking about it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anything regulated.&lt;/strong&gt; Hospitals, law firms, anyone handling sensitive data has a real problem sending that data to a third party API. Running a smaller model locally, on hardware you control, sidesteps that entire conversation. No data leaves the building, no compliance headache. A clinic running a specialized model on its own server doesn't need to explain to a regulator where patient data went, because it never went anywhere. That single fact unlocks entire industries that were effectively locked out of the AI conversation until recently, not because the technology wasn't good enough, but because the deployment model was wrong for how those industries are required to operate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Latency sensitive systems.&lt;/strong&gt; A self driving car cannot afford a network round trip to decide whether the shape ahead is a pedestrian. Object detection models running locally on quantized weights are the only version of this that makes sense. The same logic shows up in smaller, less dramatic ways too. Industrial sensors deciding in real time whether a machine is about to fail. Cameras on a factory line flagging defects before the part moves to the next station. None of these can wait two hundred milliseconds for a server somewhere to respond. The model has to live where the decision happens.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost at scale.&lt;/strong&gt; If you're running millions of inference calls a day, the difference between a frontier model and a well tuned small model is the difference between a sustainable business and one that burns cash on every request. This is the one that doesn't get talked about enough outside finance meetings, but it's probably the most decisive factor for most companies. A frontier model might be ten times more capable on a benchmark, but if it costs forty times more per call and your use case only needed a fraction of that capability anyway, you were never going to win by using it. Margins don't care how impressive your model is on a leaderboard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Offline and low connectivity environments.&lt;/strong&gt; This one is personal for me, working out of Kisumu. A lot of AI products are quietly designed with the assumption that internet access is fast, cheap, and constant. That assumption doesn't hold everywhere, and it especially doesn't hold in a lot of the world outside a handful of countries. A small model that runs entirely on-device doesn't care if the connection drops. It doesn't care if data is expensive that month. For products meant to work in places with patchy infrastructure, this isn't a nice to have, it's the only way the product functions at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  How you actually shrink a model without ruining it
&lt;/h2&gt;

&lt;p&gt;It's worth pulling back the curtain a bit on how this is done, because "just make it smaller" undersells the engineering involved.&lt;/p&gt;

&lt;p&gt;Quantization is the most common technique, and the idea is simple even if the implementation isn't. A model's weights are normally stored as high precision numbers. Quantization reduces that precision, packing the same information into fewer bits. A model that took 400 megabytes can shrink to 100 megabytes this way, and in a lot of cases the accuracy loss is barely noticeable for the task at hand. Some of the more aggressive recent approaches go even further, using ternary weights that only take the values negative one, zero, or positive one, which sounds almost too simple to work, but it does, within the right constraints.&lt;/p&gt;

&lt;p&gt;Pruning is the other major lever. Neural networks tend to be wildly overparameterized, meaning a lot of the connections inside them contribute very little to the final output. Pruning identifies and removes those low impact connections, shrinking the model without meaningfully touching its behavior. It's the machine learning equivalent of realizing half your codebase is dead code nobody ever calls.&lt;/p&gt;

&lt;p&gt;Then there's knowledge distillation, which I find the most conceptually interesting of the three. You take a large, capable model and use it as a teacher. The small model is trained not just on raw data, but on the larger model's outputs and internal behavior, essentially learning to mimic the bigger model's reasoning on the specific tasks that matter. It's apprenticeship, formalized into a training process. The student doesn't need to know everything the teacher knows, it just needs to get good at the narrow thing it was hired to do.&lt;/p&gt;

&lt;p&gt;Stack these techniques together and you get something that sounds almost contradictory on paper: a model a fraction of the size, running a fraction of the cost, that still holds its own against something many times larger, as long as you stay within the lane it was built for.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that's relevant to actual developers
&lt;/h2&gt;

&lt;p&gt;The interesting bit for people building things, rather than people writing think pieces about AI, is that this shifts the skill that matters. Prompting a giant general model well is one skill. Picking, fine tuning, and deploying a small specialized model for your exact use case is a different one entirely, and it's closer to traditional engineering than people expect.&lt;/p&gt;

&lt;p&gt;It involves real tradeoffs you have to reason about. How much accuracy are you willing to give up for speed. Can you get away with quantizing down to a smaller footprint without your outputs degrading in ways your users will notice. Do you need one model doing everything, or are you better off chaining a few small specialized ones together, each handling a narrow piece of the task.&lt;/p&gt;

&lt;p&gt;That last pattern is becoming more common than I expected. Instead of one model trying to reason, call tools, and generate a final response, you split it. One small model handles reasoning, another handles tool calls, another handles the final response generation. Each one is small enough to run cheaply and fast enough to feel instant, and together they cover ground that used to require a single enormous model.&lt;/p&gt;

&lt;p&gt;It's a similar idea to microservices, if you squint. Stop building one giant thing that does everything adequately, and instead build several small things that each do one thing well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this matters beyond hype cycles
&lt;/h2&gt;

&lt;p&gt;I think the honest reason this shift is sticking, rather than being another trend that fades, is that it lines up with constraints that don't go away. Power costs money. Bandwidth isn't guaranteed everywhere. Sensitive data shouldn't leave the building it was created in. None of those problems get solved by a bigger model. If anything, they get worse.&lt;/p&gt;

&lt;p&gt;A model that runs locally, fast, cheap, and reliably on one specific task isn't a downgrade from a giant general purpose model. It's a different tool built for a different job, the way a screwdriver isn't a worse hammer.&lt;/p&gt;

&lt;p&gt;The giant frontier models aren't going anywhere, and they're still pushing the frontier of what's possible, which is exactly what they should be doing. But the actual day to day work of building things that run reliably, cheaply, and close to where people are, that's increasingly happening on models small enough that a few years ago nobody would have bothered writing a paper about them.&lt;/p&gt;

&lt;p&gt;That's the part I find genuinely interesting. Not that small models exist, but that betting on them is now the obviously correct engineering decision in a lot more situations than people expected.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>What I Learned About the Lightning Network in My First Week of Bootcamp</title>
      <dc:creator>Walter Hrad</dc:creator>
      <pubDate>Mon, 15 Jun 2026 08:29:13 +0000</pubDate>
      <link>https://dev.to/blakcodes/what-i-learned-about-the-lightning-network-in-my-first-week-of-bootcamp-12hj</link>
      <guid>https://dev.to/blakcodes/what-i-learned-about-the-lightning-network-in-my-first-week-of-bootcamp-12hj</guid>
      <description>&lt;p&gt;A few weeks ago I sat down for the Bitcoin Lightning Network bootcamp at Zone01 Kisumu with a vague idea that Lightning was "the fast version of Bitcoin." That's about as accurate as saying a smartphone is "the fast version of a brick." It's not wrong, but it skips over the part that actually matters: how it works underneath.&lt;/p&gt;

&lt;p&gt;This article is a walkthrough of what clicked for me during the first stretch of the bootcamp, mostly around channels and payments. I'm writing it the way I wish someone had explained it to me on day one, with the confusion left in rather than smoothed over.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem Lightning is solving
&lt;/h2&gt;

&lt;p&gt;Before touching any Lightning concepts, the bootcamp made us sit with the actual problem. Every transaction on the Bitcoin blockchain has to be broadcast, verified by miners, and included in a block. That's the whole point of Bitcoin's security model, but it means transactions are slow and, depending on network congestion, expensive. Paying for a coffee with an on-chain transaction is technically possible but practically absurd.&lt;/p&gt;

&lt;p&gt;Lightning's pitch is: what if two people who transact with each other often didn't have to touch the blockchain every single time? What if they could settle up on-chain only occasionally, while doing the actual back-and-forth payments off-chain, instantly, for nearly nothing?&lt;/p&gt;

&lt;p&gt;That's the entire idea. Everything else is just the engineering required to make that idea trustworthy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Channels: the unit everything is built on
&lt;/h2&gt;

&lt;p&gt;The first real concept is the payment channel. Two parties open a channel by committing funds into a special on-chain transaction, a 2-of-2 multisig output. Both parties have to sign off on anything that happens with that money from this point forward.&lt;/p&gt;

&lt;p&gt;Once the channel is open, the two parties can send funds back and forth between each other by exchanging signed updates that represent the current balance split. None of these updates touch the blockchain. They're just signed messages held by both parties, each one superseding the last.&lt;/p&gt;

&lt;p&gt;When either party wants to stop, they broadcast the latest balance state to the chain, and the channel closes with funds distributed according to that final state.&lt;/p&gt;

&lt;p&gt;What took me a while to get is that the channel doesn't track "transactions" the way I think of them. It tracks a running balance. If I open a channel with 100,000 sats and send my counterparty 5,000 sats, the new state isn't "a transfer happened," it's "the balance is now 95,000 / 5,000." Every payment is really just both parties agreeing to a new split and signing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens if someone cheats
&lt;/h2&gt;

&lt;p&gt;This is where the bootcamp slowed down and made sure we actually understood it, because it's the part that makes Lightning trustworthy without a central authority.&lt;/p&gt;

&lt;p&gt;Each balance update invalidates the previous one. But what stops someone from broadcasting an old state where they had more funds, the one before they sent money out?&lt;/p&gt;

&lt;p&gt;The answer is a punishment mechanism. Every time the channel state updates, both parties generate a new commitment transaction, and as part of agreeing to the new state, each party gives the other a revocation secret for the old state. If someone tries to broadcast an outdated commitment transaction, the other party can use that revocation secret to claim the entire channel balance as a penalty.&lt;/p&gt;

&lt;p&gt;So broadcasting an old, more favorable state isn't a sneaky shortcut. It's a way to lose everything. The incentive structure does the enforcement, not a referee.&lt;/p&gt;

&lt;p&gt;I found this genuinely elegant once it landed. The system doesn't prevent cheating by making it impossible. It prevents cheating by making it economically suicidal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routing payments through people you've never met
&lt;/h2&gt;

&lt;p&gt;Channels alone would only let you pay people you've directly opened a channel with, which isn't useful at scale. The next piece is routing payments across a network of channels you're not directly part of.&lt;/p&gt;

&lt;p&gt;Say I have a channel with Alvin, and Alvin has a channel with Ronnie. I can pay Ronnie without ever opening a channel with him directly, by routing the payment through Alvin.&lt;/p&gt;

&lt;p&gt;The mechanism that makes this safe is Hashed Timelock Contracts, or HTLCs. Ronnie generates a secret, hashes it, and gives the hash to me (via an invoice). I forward a conditional payment to Alvin: "you get this amount if you produce the preimage of this hash, and you have until this block height to do it." Alvin forwards the same conditional payment to Ronnie. Ronnie, who knows the secret, reveals it to Alvin to claim the payment. Alvin now has the secret and reveals it to me to claim his payment.&lt;/p&gt;

&lt;p&gt;The result: the payment moves across the route atomically. Either the whole chain of HTLCs resolves and everyone gets paid, or it times out and everyone reverts. Nobody in the middle can walk away with funds without forwarding them, because the only way to claim the incoming HTLC is to produce a preimage they can only get by paying out the outgoing HTLC.&lt;/p&gt;

&lt;p&gt;The timelocks decrease as you go further from the payer, which I initially found unintuitive until I realized why: it gives nodes earlier in the route enough time to claim or fail their HTLC after observing what happens downstream.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up nodes and actually sending a payment
&lt;/h2&gt;

&lt;p&gt;The bootcamp had us spin up LND nodes connected to a Bitcoin Core regtest network, open channels between our nodes, and send payments across them. Doing this hands-on is where a lot of the theory actually solidified.&lt;/p&gt;

&lt;p&gt;A few things stood out in practice:&lt;/p&gt;

&lt;p&gt;Opening a channel is itself an on-chain transaction, so it needs to be confirmed before the channel is usable. On regtest this is trivial since you can mine blocks on demand, but it's a good reminder that Lightning doesn't eliminate on-chain activity, it just minimizes it.&lt;/p&gt;

&lt;p&gt;Creating an invoice on the receiving node generates that hash I mentioned earlier, encoded along with the amount and other metadata into a string you'd recognize as a Lightning invoice (the ones starting with &lt;code&gt;lnbc&lt;/code&gt;). Paying that invoice from another node is what kicks off the HTLC chain.&lt;/p&gt;

&lt;p&gt;Watching &lt;code&gt;lncli&lt;/code&gt; output during a multi-hop payment, and seeing the HTLCs appear and resolve across the route in real time, made the routing concept click in a way that diagrams hadn't. There's something about watching the actual state changes happen on nodes you set up yourself that diagrams can't replicate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm still working through
&lt;/h2&gt;

&lt;p&gt;Channel balance and liquidity management is the next thing on my list. A channel can only route payments in a direction it has capacity for, which means the network's ability to route payments depends heavily on how liquidity is distributed across channels. Rebalancing, fee policies for routing nodes, and how nodes decide on routes (especially with private channels and onion routing for privacy) are all things I've only scratched the surface of.&lt;/p&gt;

&lt;p&gt;I'm also curious about how this connects to the broader infrastructure work I've been doing, particularly around building tools that interact with these nodes programmatically rather than through &lt;code&gt;lncli&lt;/code&gt;. That feels like a natural next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing thought
&lt;/h2&gt;

&lt;p&gt;What surprised me most about this first stretch wasn't any single technical detail, it was how much the whole system relies on aligning incentives rather than adding more rules. Channels, revocation secrets, HTLCs, all of it boils down to making honest behavior the only behavior that makes economic sense.&lt;/p&gt;

&lt;p&gt;That's a different kind of engineering than I'm used to from typical software work, where you're usually trying to prevent bad states through validation and access control. Here, the bad states are allowed to exist, they're just made unprofitable. I'm looking forward to digging into the next layer of this.&lt;/p&gt;

</description>
      <category>bitcoin</category>
      <category>blockchain</category>
      <category>cryptocurrency</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>The Man Who Armed the Internet and Then Spent His Life Trying to Fix It</title>
      <dc:creator>Walter Hrad</dc:creator>
      <pubDate>Fri, 05 Jun 2026 08:44:28 +0000</pubDate>
      <link>https://dev.to/blakcodes/-the-man-who-armed-the-internet-and-then-spent-his-life-trying-to-fix-it-fk7</link>
      <guid>https://dev.to/blakcodes/-the-man-who-armed-the-internet-and-then-spent-his-life-trying-to-fix-it-fk7</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5b1uh0zd7rp3rnwavtcj.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5b1uh0zd7rp3rnwavtcj.webp" alt=" " width="800" height="800"&gt;&lt;/a&gt;There is a particular kind of person in security who does not fit neatly into the categories the industry likes to use. Not purely a researcher, not purely a builder, not purely an attacker or a defender. HD Moore is that person. He built the most widely used hacking tool in the world, handed it to the public for free, watched it get used in ways that made a lot of people nervous, and then spent the next two decades using that same instinct for exploration to help organizations understand exactly how exposed they really are. His career reads less like a resume and more like a long argument with the security industry about what openness actually means.&lt;/p&gt;

&lt;p&gt;To understand him, you have to go back to Austin, Texas, and a teenager with too much time and too many phone lines.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dialing in the Dark
&lt;/h2&gt;

&lt;p&gt;HD Moore grew up around Austin in the 1990s, which meant he grew up in a particular era of the internet that people who were not there tend to romanticize without fully understanding. Before the web took over everything, before social media, before the endless scroll, there was a different kind of exploration available to anyone with a modem and a phone line. You could just dial numbers. Random numbers. Area code, exchange, four digits, and see what picked up.&lt;/p&gt;

&lt;p&gt;Most of the time nothing happened. But occasionally a computer answered. An old UNIX machine somewhere, sitting in a university basement or a corporate server room, configured to accept inbound connections from whoever happened to be curious enough to dial in. Nobody had thought particularly hard about what would happen if a teenager in Austin started working through every number in the 512 area code.&lt;/p&gt;

&lt;p&gt;Moore's mother was a medical transcriptionist, and that job came with an unusual household setup. Multiple phone lines. An ISDN line. Two computers. And a mother who went to bed early. He would run a program called ToneLoc across the entire area code, night after night, logging whatever picked up. HVAC systems at department stores. Radio transmission towers. Machines that had no business being reachable by a kid sitting at home, but were, because nobody had locked the door.&lt;/p&gt;

&lt;p&gt;He was not doing this to cause damage. He was doing it because it was fascinating. There was a whole world connected by phone lines, and almost none of it knew he was there. That feeling, of discovering something that was just sitting there waiting to be found, never really left him.&lt;/p&gt;

&lt;p&gt;This is important context for everything that came later. Moore is not a person who got into security because he wanted to break things. He got into it because he wanted to know what was there.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Phrack Channel and the Air Force
&lt;/h2&gt;

&lt;p&gt;Moore spent a lot of his early online life in the Phrack IRC channel. Phrack was the hacker magazine, the one that had been running since 1985 and had, by the mid-90s, accumulated a serious body of knowledge about how systems worked and how they could be broken. The IRC channel was where the people who read Phrack talked to each other, and it was, apparently, also where defense contractors went to find entry-level talent.&lt;/p&gt;

&lt;p&gt;Someone in that channel sent Moore a message asking if he was looking for work. He was still in high school. The job was with Computer Sciences Corporation, doing work for what was then the Air Force Intelligence Agency, building offensive tools for red teams inside the Air Force. They needed someone who could write exploits, who understood how networks worked at a low level, who was comfortable with the kind of technical problem that does not have clean documentation.&lt;/p&gt;

&lt;p&gt;Moore was, by his own admission, not a particularly good programmer at the time. But he understood the material, and he was willing to learn. His first professional experience was getting vague briefs about tools that needed to scan networks for open registry keys or intercept specific kinds of traffic, and then going off and building them. This was before most of the industry had formalized the idea of red teaming. The people doing it were figuring it out as they went.&lt;/p&gt;

&lt;p&gt;That job led directly to his next move. After doing a penetration test on a local business and basically walking through every layer of their security without being stopped, he and the team went back to CSC and proposed expanding into commercial pen testing. CSC said no. They were a federal contractor, and that was what they did. So the team left and started their own company: Digital Defense.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem That Made Metasploit Necessary
&lt;/h2&gt;

&lt;p&gt;Digital Defense was doing penetration testing for clients, and the work exposed a problem that the industry did not have a good answer to.&lt;/p&gt;

&lt;p&gt;Here is how a pen test worked in the late 1990s: you ran a vulnerability scanner to find out what was running on the network, you matched up the versions against lists of known vulnerabilities, and then you needed to prove to the client that the vulnerability was actually exploitable. It was not enough to tell someone their server was unpatched. Anyone could say that. The value was in demonstrating what an attacker could actually do with it.&lt;/p&gt;

&lt;p&gt;That meant you needed working exploits. And getting working exploits was, at the time, genuinely difficult. There were some hacker sites where you could download exploit code, but that code was often old, often undocumented, and potentially carrying malware. Nobody had taken the time to build a clean, trusted, well-organized collection. The commercial options, like Core Impact, existed but were expensive and limited. For a small firm doing pen tests, the practical answer was to write your own, which required constant reinvestment of time and effort.&lt;/p&gt;

&lt;p&gt;Moore was accumulating exploits. He had bits and pieces scattered across his machines, some written by him, some shared by people he trusted, none of it organized in a way that made it easy to hand to a teammate or use reliably on a client engagement. The information security community had started to dry up around sharing exploits. The people who had been sharing freely in the 90s were either getting real jobs that made sharing complicated, or running into legal concerns, or simply drifting away. What had been available was disappearing.&lt;/p&gt;

&lt;p&gt;Moore's answer was to build a framework. A single application that held exploits in a consistent format, with documentation, with known payloads, without hidden garbage. Something he could trust on a client network. Something he could add to over time. That was the original Metasploit: a practical tool, built to solve a real problem he was having at work, that he happened to release publicly.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Metasploit Actually Was
&lt;/h2&gt;

&lt;p&gt;The first version was menu-based, terminal-based, and not particularly elegant. You picked an exploit, picked an encoder, picked a payload, and sent it. Functional but rigid.&lt;/p&gt;

&lt;p&gt;By Metasploit 2, the architecture had been rethought. The core idea was modularity. An exploit and a payload were separate things, and you could combine them like components. Before this, most exploits came with one or two hardcoded payloads and that was it. What Moore and his collaborators built was a system where any compatible payload could be attached to any compatible exploit, which multiplied the number of possible attack configurations enormously.&lt;/p&gt;

&lt;p&gt;The payload metaphor is worth understanding. If the exploit is the needle that gets through a system's defenses, the payload is what the needle is carrying. It could open a command shell. It could establish a persistent connection back to the attacker. It could do almost anything, depending on what you loaded into it. Metasploit made it easy to swap payloads in and out, which meant that security researchers could test not just whether a system was vulnerable, but what an attacker could realistically do with that vulnerability.&lt;/p&gt;

&lt;p&gt;This was genuinely useful for pen testers. It was also genuinely useful for people who wanted to attack systems without permission. Moore knew this and released it anyway, which is where the controversy started and never really stopped.&lt;/p&gt;

&lt;p&gt;His position was consistent. The information was not secret. The vulnerabilities were already known. Hiding the tools did not make the vulnerabilities go away; it just made it harder for defenders to understand what they were defending against. A security professional who could not replicate what an attacker could do was working with incomplete information. The argument had real merit, and it still does.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Moral Weight of Building Metasploit
&lt;/h2&gt;

&lt;p&gt;The Darknet Diaries episode about Moore frames this tension honestly. Metasploit was crammed with exploits and payloads that could be used to compromise computers. Anyone with the tool and a target could cause serious damage. And Moore put it on the internet for free.&lt;/p&gt;

&lt;p&gt;What makes his position defensible is not that the tool was never misused. It was misused. What makes it defensible is that the alternative, keeping effective security tools restricted to commercial vendors and government agencies, does not actually make systems more secure. It just makes it easier for institutions to charge money for security services while leaving the broader community without the knowledge needed to evaluate the claims those institutions make.&lt;/p&gt;

&lt;p&gt;There is also the question of what Metasploit did for education. For the generation of security professionals who came up in the 2000s and 2010s, Metasploit was often the first real penetration testing tool they got their hands on. It was free, it was documented, and it lowered the barrier to actually understanding how exploits worked at a technical level. People who went on to do serious security work, both offensive and defensive, often trace part of their foundation to time spent with Metasploit. You cannot fully evaluate that against the cases where someone misused it and pretend the accounting is straightforward.&lt;/p&gt;

&lt;p&gt;Moore built something that changed the industry. The people who benefited from it vastly outnumber the people who used it to cause harm, and the harm that was caused would largely have been possible anyway for anyone with the motivation to find the same exploits elsewhere. What would not have existed otherwise is the community, the shared language, the common framework that allowed the field to develop a shared understanding of offensive techniques.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rapid7 and What Came After
&lt;/h2&gt;

&lt;p&gt;In 2009, Rapid7 acquired Metasploit. Moore joined the company as chief security officer and stayed on as chief architect of the framework. He had been building Metasploit largely in his spare time, nights and weekends and lunch breaks, and the acquisition meant there would be actual resources dedicated to it full-time.&lt;/p&gt;

&lt;p&gt;At Rapid7, Moore continued the kind of large-scale internet scanning work that had been a thread through his whole career. The most significant of these projects was what became known as Project Sonar, which involved scanning the entire public internet to understand what was actually exposed. In 2013, this work produced a notable finding: roughly 50 million networked devices were exposed to the internet through flaws in Universal Plug and Play, a protocol that had been designed for home networks and had no business being accessible from the public internet. This was not a targeted discovery. It was the result of simply scanning everything and looking at what came back.&lt;/p&gt;

&lt;p&gt;That kind of work, running probes across the entire address space and analyzing what responds, gave Moore a perspective on internet exposure that almost nobody else had. He had seen the public internet from the outside, systematically, at scale. He knew what was out there and how bad the configuration problems were across every industry.&lt;/p&gt;

&lt;p&gt;He left Rapid7 in 2016.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem He Could Not Stop Thinking About
&lt;/h2&gt;

&lt;p&gt;After Rapid7, Moore spent time at Atredis Partners doing research and development. But the problem that kept pulling at him was one he had run into repeatedly over the course of his career: organizations did not know what was on their own networks.&lt;/p&gt;

&lt;p&gt;This sounds basic, but it is one of the most persistent and serious problems in enterprise security. You cannot protect what you cannot see. And the larger an organization gets, the harder it is to maintain an accurate picture of every device, every service, every connection. Things get added without being tracked. Old systems never get decommissioned. Consumer devices appear on corporate networks because employees connect personal devices to Wi-Fi. IoT devices get deployed in facilities and forgotten about. The picture that the security team has of their own network is almost always incomplete, and often dramatically so.&lt;/p&gt;

&lt;p&gt;Moore had seen this from both sides. As a penetration tester, he had scanned client networks and found things the client did not know were there. As someone who had scanned the entire public internet, he had a clear view of how exposed the outside-facing portion of organizational infrastructure was. The inside of the network was usually worse.&lt;/p&gt;

&lt;p&gt;In 2018, he founded what was initially called Rumble Network Discovery to address this directly. The tool was designed to scan a network and identify everything on it, fingerprinting devices precisely enough to give organizations an accurate picture of their attack surface. Not just identifying hosts, but identifying what they were, what was running on them, and what the security implications were.&lt;/p&gt;

&lt;p&gt;The company later renamed itself runZero, a name that referenced the idea of starting from zero assumptions about what is on the network and building an accurate picture from scratch. Moore found, consistently, that when his tool ran on a corporate network, it found things the security team had not known were there. PlayStation 4s. Amazon Echo devices. Smart TVs. Weather stations that had updated their firmware and opened unauthenticated telnet services on the local network. The gap between what organizations thought they had and what was actually there was often startling.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why His Career Makes Sense as a Whole
&lt;/h2&gt;

&lt;p&gt;HD Moore has spent his career doing a version of the same thing. Finding what is there when nobody has looked carefully. As a teenager, it was running ToneLoc across area codes to see what picked up. As a young pen tester, it was building a framework for discovering what vulnerabilities were actually present and exploitable on client networks. At Rapid7, it was scanning the public internet to see what was exposed. At runZero, it is scanning internal networks to show organizations the gap between their assumed state and their actual state.&lt;/p&gt;

&lt;p&gt;The thread is curiosity about what exists, combined with a commitment to making that knowledge available rather than hoarding it. Metasploit was not hidden. Project Sonar data was published. The research Moore did on UPnP exposure was shared publicly so that device manufacturers and ISPs could address it. The fingerprint database that runZero uses, Recog, is open source and contributed back to the broader community.&lt;/p&gt;

&lt;p&gt;This is a coherent position. Information about how systems work, including information about how they can be broken, is more useful in the open than locked away. The security industry has spent decades trying to argue both sides of this question depending on what was commercially convenient, and Moore has been one of the clearer voices insisting that openness, on balance, makes things better.&lt;/p&gt;

&lt;p&gt;He is also a person who clearly finds the work interesting for its own sake. He described the internet to someone once as just a series of numbers. Make up any random 32-bit address and there is probably something there. That observation has a quality of genuine wonder in it that does not come from someone who got into the field to make money or build influence. It comes from someone who dialed random phone numbers as a teenager because they wanted to know what would pick up, and never really stopped asking that question.&lt;/p&gt;




&lt;h2&gt;
  
  
  What He Leaves Behind
&lt;/h2&gt;

&lt;p&gt;Metasploit is now owned by Rapid7 and maintained by a large team. It remains the dominant open source penetration testing framework. Certifications like OSCP require students to use it. Security courses around the world are built around it. The number of security professionals who developed their foundational skills with Metasploit is enormous.&lt;/p&gt;

&lt;p&gt;RunZero has become a real company with funding and customers. The asset discovery problem Moore identified has not gone away. If anything, the explosion of cloud infrastructure, remote work, and IoT devices has made it worse, which means the work he started in 2018 is more relevant now than when he began it.&lt;/p&gt;

&lt;p&gt;The Darknet Diaries episode that brought Moore to a wider audience in 2022 has over 400,000 plays. The story it tells is not a simple one. It is not a story about a genius who built something and got rich. It is a story about a person who had a particular way of looking at technical problems, who made a decision to share what he built instead of keeping it to himself, and who spent decades living with the consequences of that decision while continuing to push in the same direction.&lt;/p&gt;

&lt;p&gt;That kind of consistency is rare. The security industry is full of people who pivot toward whatever is commercially valuable at a given moment. Moore has been doing essentially the same thing since he was a teenager in Austin dialing random phone numbers at midnight. The tools have changed. The scale has changed. The question he is asking is the same.&lt;/p&gt;

&lt;p&gt;What is actually out there? And who else knows about it?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Lie We Are All Telling Ourselves About AI Code</title>
      <dc:creator>Walter Hrad</dc:creator>
      <pubDate>Wed, 27 May 2026 09:56:28 +0000</pubDate>
      <link>https://dev.to/blakcodes/the-lie-we-are-all-telling-ourselves-about-ai-code-2e2g</link>
      <guid>https://dev.to/blakcodes/the-lie-we-are-all-telling-ourselves-about-ai-code-2e2g</guid>
      <description>&lt;p&gt;There is a number that has been circling developer circles for the past few months and nobody seems to know what to do with it. A broad analysis of 470 GitHub pull requests found that AI-generated code was 1.7 times more likely to have major issues like logic errors, and 2.74 times more prone to security vulnerabilities compared to human-written code.  (daily.dev) Hardcoded API keys. Plain-text passwords. APIs that do not even exist, hallucinated from outdated training data.&lt;br&gt;
And yet, developer adoption is near-universal. Big Tech is generating anywhere from 25 to 90 percent of new code with AI, depending on the organisation.  (Hostinger)&lt;br&gt;
So we have a situation where the tools are objectively producing riskier code, and we are using them more than ever. That is not a contradiction. That is a rationalization.&lt;br&gt;
The most counterintuitive finding of 2026 is this: researchers ran a randomized controlled trial with experienced open-source developers on real codebases doing real tasks, and they were measurably slower when using AI tools. They did not know it. Even after the experiment, they believed AI had helped them.  (Hashnode) Broader survey data shows 95 percent of developers report feeling productive while measurably producing lower-quality code.  (Hashnode)&lt;br&gt;
That gap between feeling productive and being productive is where a lot of careers are quietly getting damaged right now.&lt;br&gt;
I am not writing this to tell you to stop using these tools. I use them too. The point is that we have collectively agreed to stop asking hard questions about what we are actually shipping. A prototype that works in a demo is not the same thing as a product someone depends on. The "three-month black box" problem is real: projects become unmanageable because nobody on the team actually understands the AI-generated codebase they are now responsible for maintaining.  (daily.dev)&lt;br&gt;
Andrej Karpathy coined the term "vibe coding" in early 2025. The idea was straightforward: describe what you want in plain English, let AI write the code, and do not obsess over every line.  (Alex Cloudstar) That framing made sense for personal projects and quick experiments. What happened next is that companies started treating it as a production methodology.&lt;br&gt;
By 2026, 41 percent of all code globally is AI-generated.  (daily.dev) Some of that is fine. A lot of it is sitting in production systems that someone will have to debug at 2am six months from now, written by an AI that had no stake in whether the thing holds up.&lt;br&gt;
The security angle is the part that should concern anyone working in this industry. Security firm Tenzai tested five popular AI coding tools and found that 45 percent of AI-generated code samples contain OWASP Top-10 vulnerabilities.  (Hashnode) These are not obscure edge cases. These are the most well-documented, widely-known vulnerability categories in web development. The kind of mistakes a junior developer would get flagged for in a code review.&lt;br&gt;
What is the fix? Honestly, it is boring. The difference between a vibe-coded prototype and a vibe-coded product is the automated verification layer that runs on every change. Set up tests, type checking, and linting from day one. These quality gates catch AI-generated mistakes before they compound into larger problems.  (DEV Community) That is not revolutionary advice. It is just the fundamentals that people started skipping because the AI made everything feel so fast.&lt;br&gt;
One engineer described it well: the best approach is a pendulum method, going back and forth between AI-assisted and hand-written code. Not because AI is bad, but because staying sharp matters. You do not want to go too long without writing something by hand and lose your mental model of what good code actually looks like.  (Explainx)&lt;br&gt;
That feels right to me. The developers who will be fine are the ones treating AI as a collaborator they have to supervise, not a replacement for thinking. The ones who will struggle are the ones who have outsourced their judgment along with their keystrokes.&lt;br&gt;
We built careers on understanding systems. That understanding is still the job. The keyboard just got faster&lt;/p&gt;

</description>
    </item>
    <item>
      <title>You Understand It. You Just Cannot Build It Yet.</title>
      <dc:creator>Walter Hrad</dc:creator>
      <pubDate>Sat, 23 May 2026 11:42:06 +0000</pubDate>
      <link>https://dev.to/blakcodes/you-understand-it-you-just-cannot-build-it-yet-57de</link>
      <guid>https://dev.to/blakcodes/you-understand-it-you-just-cannot-build-it-yet-57de</guid>
      <description>&lt;p&gt;At some point you will understand something completely and still not be able to use it.&lt;br&gt;
This happens more than people admit. You read the documentation. You watch the tutorial. Someone explains it to you and you nod along and you genuinely follow every step. And then you open a blank file and your hands do not know what to do.&lt;br&gt;
This is not a knowledge gap. The knowledge is there. This is something else and I think it matters to name it correctly because if you think it is a knowledge gap you will go read more documentation and that will not help.&lt;br&gt;
The thing that is actually missing is a different kind of familiarity. Not understanding how something works but having used it enough times that your hands remember what to reach for. There is a difference between knowing that useState takes an initial value and returns a pair and actually reaching for useState without thinking when you need reactive state in a component. The first one lives in your head. The second one lives somewhere else, in your fingers maybe, or in whatever part of your brain handles automatic things.&lt;br&gt;
Musicians talk about this. You can understand music theory completely and still not be able to improvise. The theory is in your head but improvisation requires something that bypasses your head. Athletes talk about it too. The gap between understanding a technique and executing it under pressure is not closed by more understanding. It is closed by repetition. A footballer who has studied penalty kicks all week still has to take thousands of them before his body knows what to do when the stadium goes quiet and it actually matters.&lt;br&gt;
Programming culture is weird about this because we treat it as purely intellectual work. If you understand the concept you should be able to apply it. But that is not true and I think a lot of people quietly suffer from believing it. They understand closures but cannot write one without looking it up. They understand recursion but freeze when they need to use it in a real problem. They feel like frauds because the gap between their understanding and their output is visible to them and they think it should not exist.&lt;br&gt;
It should exist. It always exists. For everyone. The only way it closes is by building things, specifically by building things where you do not fully know what you are doing. Not following a tutorial step by step. Not copy-pasting from a project that already works. Actually sitting in front of a blank file and figuring it out slowly, making mistakes, getting stuck, coming back the next day.&lt;br&gt;
The stuck part is important and I want to stay on it for a moment because people treat being stuck as evidence that they are doing the wrong thing. They take it as a signal to go watch another video or find a better explanation. But getting stuck is where the second kind of familiarity forms. When you cannot remember the syntax and you have to look it up for the fourth time, your brain is doing something with that friction. The fifth time will be easier. Not because you read something new but because you went through the process of not knowing and then finding out again. That cycle is doing something that passive reading cannot do.&lt;br&gt;
There is also something that happens when you make a mistake in code you actually wrote versus code you copied. When you copy code and it breaks you troubleshoot the library, the environment, the tutorial. When you write code and it breaks you are forced to understand why, because you cannot blame anyone else. That accountability is uncomfortable but it is also the thing that makes the knowledge stick. You remember the bug you actually caused in a way you will never remember a bug from a tutorial.&lt;br&gt;
I think the reason people avoid this is that getting stuck feels like not knowing. And not knowing feels like failure. So they stay in the tutorial loop where everything works and they always know what to do next and they understand everything perfectly and they cannot build anything on their own. The tutorial loop is comfortable because someone else has already figured out all the places where things could go wrong and smoothed them over before you arrived. Real projects are not like that. Real projects have rough edges everywhere and no one has prepared the path for you.&lt;br&gt;
This is also why side projects that you actually care about are more valuable than exercises you do just to learn. When you care about the thing you are building, you tolerate more confusion. You come back after the third failed attempt because you actually want the thing to exist. That motivation is what gets you through the part of the process where you do not know what you are doing, which is most of the process for most projects.&lt;br&gt;
There is no comfortable path from understanding to building. The discomfort is the path. That probably sounds like something someone would put on a poster but I mean it practically. If you are comfortable, you are probably in a mode that is not building the second kind of familiarity. If you are uncomfortable, you are probably exactly where you need to be. The goal is not to eliminate the discomfort but to develop a tolerance for it, to learn that confusion is a stage and not a verdict.&lt;br&gt;
The move is to build something you do not know how to build yet. Not something slightly outside your comfort zone. Something where you genuinely do not know how to start. Then start anyway and see what happens. Pick something small enough that finishing it is realistic but unfamiliar enough that you will have to figure things out. The specific thing matters less than the act of choosing and starting.&lt;br&gt;
What usually happens is that you figure it out slower than you wanted to, with more mistakes than you expected, and you come out the other side able to do something your hands did not know before. And the next time you sit in front of a blank file it will feel slightly less empty. Not because you learned more. Because you built something once and your body remembers that it survived.&lt;br&gt;
That is the whole game. Understanding gets you started. Building is what finishes it.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Nobody Wants to Read Your Code (And You Don't Want to Read Theirs)</title>
      <dc:creator>Walter Hrad</dc:creator>
      <pubDate>Sat, 23 May 2026 11:36:02 +0000</pubDate>
      <link>https://dev.to/blakcodes/nobody-wants-to-read-your-code-and-you-dont-want-to-read-theirs-33h5</link>
      <guid>https://dev.to/blakcodes/nobody-wants-to-read-your-code-and-you-dont-want-to-read-theirs-33h5</guid>
      <description>&lt;p&gt;There is a thing that happens when you join a new codebase. You open a file, read maybe fifteen lines, and then you close it. Not because you understood it. Because you stopped wanting to understand it.&lt;/p&gt;

&lt;p&gt;I think about this a lot. We talk about clean code, readable code, well-documented code, as if the problem is always on the writing side. But the reading side has its own psychology and almost nobody talks about it honestly.&lt;/p&gt;

&lt;p&gt;Reading someone else's code is uncomfortable in a way that is hard to name. It is not just that the logic is unfamiliar. It is that you are stepping inside someone else's head. Their assumptions are baked into every variable name. Their shortcuts make sense only if you know what they were rushing toward. Their weird choice of a for loop where a while loop would feel more natural is probably the result of a bug they fixed at 11pm six months ago and never bothered to refactor. You are not reading code. You are reading a person.&lt;/p&gt;

&lt;p&gt;And most of us are not trained for that. We are trained to write, to produce, to ship. Reading feels passive. Reading someone else's bad code feels like losing. Reading someone else's good code feels even worse because then you have to sit with the fact that you would not have done it that way and you are not sure if your way is actually better.&lt;/p&gt;

&lt;p&gt;There is also the time problem. When you write code, you have context. You know what the function is for, what came before it, what will come after. When you read, you have to build that context from scratch and the codebase usually does not help you. Comments are either missing or lying. Variable names like data and result and temp are everywhere. The folder structure made sense to the person who created it and to nobody else.&lt;/p&gt;

&lt;p&gt;So you do what most people do. You read just enough to do your task. You make your change in the one file you actually understand. You submit your PR and you move on.&lt;/p&gt;

&lt;p&gt;The problem is that this pattern compounds. The person after you does the same thing. And the person after them. Over time the codebase becomes a city where everybody knows their own neighborhood and nobody knows the roads in between. When something breaks in the space between neighborhoods, it takes three people and a long afternoon to figure out why.&lt;/p&gt;

&lt;p&gt;I do not think the solution is to force people to read more code through code reviews or documentation requirements. Those things help but they do not fix the underlying discomfort. The discomfort is real and it deserves to be taken seriously.&lt;/p&gt;

&lt;p&gt;What has actually helped me is reading code the way I read a book I am not enjoying. Not to finish it. Not to extract every piece of information. Just to follow one thread. Pick one function. Understand what it calls. Understand what calls it. Stop there. Do that a few times over a few days and the codebase starts to feel less like a foreign country.&lt;/p&gt;

&lt;p&gt;The other thing that helps is accepting that confusion is not a sign of failure. The confusion you feel when you open an unfamiliar file is the same confusion the person who wrote it felt when they started. They just had more time. Time is the only real difference between a file that makes sense and one that does not.&lt;/p&gt;

&lt;p&gt;We should probably talk more openly about how hard reading code actually is. Not as a complaint but as an acknowledgment. It is a skill that takes practice and patience in a field that rewards speed. That tension is not going anywhere. Might as well be honest about it.&lt;/p&gt;

</description>
      <category>career</category>
      <category>coding</category>
      <category>discuss</category>
      <category>programming</category>
    </item>
    <item>
      <title>When "It Works on My Machine" Stops Being Good Enough</title>
      <dc:creator>Walter Hrad</dc:creator>
      <pubDate>Tue, 12 May 2026 12:16:43 +0000</pubDate>
      <link>https://dev.to/blakcodes/when-it-works-on-my-machine-stops-being-good-enough-49bi</link>
      <guid>https://dev.to/blakcodes/when-it-works-on-my-machine-stops-being-good-enough-49bi</guid>
      <description>&lt;p&gt;There is a version of learning to code where the goal is just to make the thing run. You write it, you test it locally, it does what it is supposed to do, and that feels like the finish line.&lt;br&gt;
For a while, that was me.&lt;br&gt;
ascii-art-web changed that. The project was straightforward on paper — take the ASCII art CLI I had already built and put it behind a Go HTTP server with a browser interface. Same logic, new layer. I figured it would be a lighter week.&lt;br&gt;
It was not.&lt;br&gt;
The standard library in Go handles HTTP cleanly but it does not hide anything from you. Every route has to be defined. Every status code has to be intentional. The audit requirements were specific — 400 for bad input, 404 for missing routes, 500 when the server breaks. Not approximately right. Exactly right. I had written code before that worked in the sense that it produced correct output. This was the first time I had to write code that behaved correctly under conditions I had not planned for.&lt;br&gt;
That is a different skill and I did not fully have it yet.&lt;br&gt;
Getting the frontend and the server to actually talk to each other took more back and forth than I expected. Form data that seemed fine in the browser arriving malformed on the Go side. Routes that I was sure existed returning 404s. Small things that each took longer than they should have to debug because I did not yet have a clean mental model of the full request cycle.&lt;br&gt;
By the end of the week I had something that worked and handled edge cases properly. That felt different from previous weeks. Not just "it runs" but "it holds up."&lt;/p&gt;

&lt;p&gt;Then came Docker.&lt;br&gt;
I had heard of Docker the way you hear about things in tech before you understand them — vaguely, as something that matters, without knowing exactly why. The ascii-art-web-dockerize project made the why concrete pretty fast.&lt;br&gt;
The pitch is simple: your app runs the same everywhere because you are packaging not just the code but the environment it needs. A Dockerfile describes that environment. You build an image from it. You run a container from that image. Anywhere that container runs, your app behaves the same way.&lt;br&gt;
In practice the first time you write a Dockerfile for a Go web server there are a few things that will catch you. Getting the build right inside the container. Making sure the port your server listens on is the port you actually expose and map when you run it. These are not hard problems once you understand the model but the model takes a minute to click.&lt;br&gt;
What stuck with me from that week was less the technical detail and more the shift in thinking. Shipping is not the same as building. A lot of people can build something that works. Fewer can package it in a way that works for someone else, on a different machine, without needing to explain anything. That gap is real and Docker is one of the tools that closes it.&lt;/p&gt;

&lt;p&gt;If you are learning and you have not touched deployment or containerisation yet — get there sooner than I did. Writing code is one skill. Shipping it is another. Both matter and the second one does not get talked about enough in beginner spaces.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>go</category>
      <category>learning</category>
      <category>webdev</category>
    </item>
    <item>
      <title>From High School to Hello World: What Nobody Warned Me About</title>
      <dc:creator>Walter Hrad</dc:creator>
      <pubDate>Wed, 25 Mar 2026 08:14:31 +0000</pubDate>
      <link>https://dev.to/blakcodes/from-high-school-to-hello-world-what-nobody-warned-me-about-2j20</link>
      <guid>https://dev.to/blakcodes/from-high-school-to-hello-world-what-nobody-warned-me-about-2j20</guid>
      <description>&lt;p&gt;I did not plan to end up at a coding apprenticeship. I finished high school and my sister told me about Zone01 Kisumu. I applied. I got in. I showed up on the first day with genuinely no idea what I had signed up for.&lt;br&gt;
That is the honest starting point.&lt;/p&gt;

&lt;p&gt;Zone01 does not ease you in. There is no teacher, no syllabus handed to you, no "week one is for beginners." You get a project, a deadline, and a room full of other people who are also confused. The first thing I learned had nothing to do with code it was that asking for help is not weakness. It is just how the place works.&lt;br&gt;
The language we use is Go. I had never touched it before. Go is not forgiving. It will not compile if you have a variable sitting there unused. It will not let things slide. Early on that felt personal, like the language had decided it did not like me. I would stare at an error for an hour and feel like I was the problem.&lt;br&gt;
There was one project that genuinely broke me. I kept running it, kept getting errors, kept changing things that made it worse. I do not even want to say how long I sat with it. What got me through was not a tutorial, not Stack Overflow it was another person in the community sitting with me, not giving me the answer, but asking the right questions until I found it myself. That stayed with me more than the project did.&lt;br&gt;
That is the thing about this environment. The people around you matter more than any resource you find online. A good peer will do what no tutorial can they will watch where you are actually going wrong, not where you think you are going wrong.&lt;/p&gt;

&lt;p&gt;Go still frustrates me sometimes. But I respect it in a way I did not expect to. It has made me slower in a good way I think before I write now instead of just typing and hoping.&lt;br&gt;
If you are just starting out and feeling like everyone else gets it faster than you, they probably do not. They are just not saying it. Find your people inside wherever you are learning. Be honest about what you do not understand. The confusion is not a sign that this is not for you. For most of us it is just what the beginning looks like.&lt;/p&gt;

</description>
      <category>codenewbie</category>
      <category>devjournal</category>
      <category>go</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
