<?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: Jerry Schneider</title>
    <description>The latest articles on DEV Community by Jerry Schneider (@jerry_schneider).</description>
    <link>https://dev.to/jerry_schneider</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3836357%2Fc75d3caa-e8d5-4661-825a-72220d4ae1ac.png</url>
      <title>DEV Community: Jerry Schneider</title>
      <link>https://dev.to/jerry_schneider</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jerry_schneider"/>
    <language>en</language>
    <item>
      <title>Software Is Entering Its IKEA Era</title>
      <dc:creator>Jerry Schneider</dc:creator>
      <pubDate>Sat, 28 Mar 2026 01:45:19 +0000</pubDate>
      <link>https://dev.to/jerry_schneider/software-is-entering-its-ikea-era-5d4i</link>
      <guid>https://dev.to/jerry_schneider/software-is-entering-its-ikea-era-5d4i</guid>
      <description>&lt;p&gt;Every few days, someone confidently declares that AI is about to wipe out software engineering.&lt;/p&gt;

&lt;p&gt;I don't buy that.&lt;/p&gt;

&lt;p&gt;I think software is much closer to woodworking than people realize.&lt;/p&gt;

&lt;p&gt;Before industrialization, furniture was made by hand. If you wanted a table, a chair, a cabinet, or a bed frame, you needed a skilled craftsperson. The work took years of training. It was slow, specialized, and expensive. Good furniture was not broadly accessible because it could not be. Every piece required real human expertise, and that expertise did not scale cheaply.&lt;/p&gt;

&lt;p&gt;Then industrialization happened.&lt;/p&gt;

&lt;p&gt;The craft of making furniture did not disappear. It changed.&lt;/p&gt;

&lt;p&gt;Machines, standardization, and repeatable manufacturing processes made it possible to produce furniture at much larger scale and much lower cost. The average quality was often lower than what a master craftsperson could produce by hand, but that was only part of the story. The bigger change was that an entirely new market opened up. Furniture became accessible to far more people. Businesses like IKEA could exist. A thing that once required expert labor for every unit could now be produced in enormous volume by a system.&lt;/p&gt;

&lt;p&gt;Handmade furniture did not go away. There is still a market for beautiful, custom, ultra-high-quality work. But the center of gravity changed. Some artisans were displaced. Some adapted. Some moved further upmarket. And alongside all of that, a much larger market for cheaper, faster, more standardized output came into existence.&lt;/p&gt;

&lt;p&gt;I think software is going to follow a very similar path.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Software Has Been Craft Work for a Long Time&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;For most of its history, building software has looked a lot like skilled handcraft.&lt;/p&gt;

&lt;p&gt;You needed people with deep, specific knowledge. You needed years of training. You needed teams of specialists who could translate ambiguous human needs into precise technical systems. Every meaningful piece of software required a large amount of manual effort from people who knew what they were doing.&lt;/p&gt;

&lt;p&gt;That made software powerful, but it also made it expensive. There are countless products, automations, internal tools, and business ideas that never got built, not because they were bad ideas, but because the cost of making software was too high. The custom work involved was too specialized. The economics did not work.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;AI Lowers the Cost of Production&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;AI is making it cheaper and faster to produce software that is good enough for many use cases. Not perfect. Not elegant. Not deeply original. But good enough.&lt;/p&gt;

&lt;p&gt;There are companies that will now build internal tools they never would have funded before. There are small businesses that will buy software they could never previously afford. There are workflows that stayed manual for years because no one was going to assign a full engineering team to automate them. There are product ideas that used to die in a spreadsheet that may now become viable.&lt;/p&gt;

&lt;p&gt;The important question is not just whether AI can replace some portion of current engineering labor. The important question is what happens when software itself becomes far more manufacturable.&lt;/p&gt;

&lt;p&gt;When that happens, you don't just get substitution. You get expansion.&lt;/p&gt;

&lt;p&gt;You get more software.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;And Yes, a Lot of It Will Be the IKEA Version&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Good enough code that is customizable enough for most applications.&lt;/p&gt;

&lt;p&gt;When you industrialize a craft, you get standardization. You get lower costs. You get accessibility. You also get a lot more average output.&lt;/p&gt;

&lt;p&gt;Some of it will be flimsy.&lt;br&gt;&lt;br&gt;
Some of it will be ugly.&lt;br&gt;&lt;br&gt;
Some of it will work surprisingly well for the price.&lt;br&gt;&lt;br&gt;
Some of it will be disposable.&lt;br&gt;&lt;br&gt;
Some of it will be “good enough” in exactly the way the buyer needed.&lt;/p&gt;

&lt;p&gt;There will absolutely be a flood of mediocre AI-assisted software. There will be overconfident companies, sloppy implementations, badly framed cost-cutting strategies, and executives who mistake faster production for complete replacement of judgment. There will be chaos, overcorrection, and a lot of noise.&lt;/p&gt;

&lt;p&gt;Some people will be displaced. Some categories of work will shrink. Some tasks that used to justify a full human workflow will get compressed into something smaller.&lt;/p&gt;

&lt;p&gt;That is not the same thing as saying software engineering jobs disappear. It means the structure of the market changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Industrialization Changes the Work Before It Eliminates It&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;People talk as if the only possibilities are: either software engineering remains exactly what it has been, or it vanishes, but that is usually not what happens to crafts or professions.&lt;/p&gt;

&lt;p&gt;The tools change first.&lt;br&gt;&lt;br&gt;
Then the workflow changes.&lt;br&gt;&lt;br&gt;
Then the expectations change.&lt;br&gt;&lt;br&gt;
Then the skill mix changes.&lt;br&gt;&lt;br&gt;
Eventually, the job title survives while the actual work looks very different.&lt;/p&gt;

&lt;p&gt;Software engineers may spend less time manually producing obvious code and more time shaping systems. Less time writing boilerplate and more time defining constraints. Less time assembling known patterns by hand and more time evaluating tradeoffs, validating outputs, debugging weird behavior, understanding real user needs, and deciding what quality means in context.&lt;/p&gt;

&lt;p&gt;That is still engineering. In some ways, it is more engineering and less typing.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;High-Craft Software Will Still Exist&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Even after industrialization, handmade furniture did not become pointless, it became differentiated.&lt;/p&gt;

&lt;p&gt;If you want something deeply customized, unusually durable, aesthetically excellent, or built around exact needs, you still want real craftsmanship. In fact, the abundance of cheap mass-produced alternatives can make true quality stand out more, not less.&lt;/p&gt;

&lt;p&gt;I think the same thing will happen in software.&lt;/p&gt;

&lt;p&gt;There will still be demand for systems that are reliable, maintainable, secure, deeply integrated, and thoughtfully designed. There will still be software where correctness matters. There will still be edge cases, constraints, and human realities that don't fit cleanly into standardized generation.&lt;/p&gt;

&lt;p&gt;When code becomes easier to generate, discernment becomes more valuable. When anyone can produce output, the differentiator shifts toward knowing what should be built, how it should be shaped, where the risks are, what corners cannot be cut, and what makes something actually good.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Bigger Story Is Market Expansion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One reason I am skeptical of the “all jobs go away” framing is that it assumes the size of the software market stays basically fixed. I don't think it will.&lt;/p&gt;

&lt;p&gt;When making something gets cheaper, people don't merely buy the old amount for less money. They start buying things that previously were not worth buying at all.&lt;/p&gt;

&lt;p&gt;Cheap furniture did not just replace expensive furniture. It furnished more homes.&lt;/p&gt;

&lt;p&gt;AI-generated and AI-assisted software will not just compete with existing software teams. It will also create a much larger market for software in places where custom development was previously too expensive, too slow, or too inaccessible.&lt;/p&gt;

&lt;p&gt;Some of the work will move downward into highly standardized production. Some of it will move upward into higher-leverage oversight, architecture, integration, and quality. Some entirely new categories of work will appear because the economics now support them.&lt;/p&gt;

&lt;p&gt;That seems much more plausible to me than “software engineering stops existing.”&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;I’ve Heard This Story Before&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;My dad worked in television for about forty years. He started in the late 1970s, right as one era was ending and another was beginning. The first great golden age of television was fading, and cable was rising into what you could probably call a second golden age.&lt;/p&gt;

&lt;p&gt;For a long time, the industry changed less than people might expect. The scale grew. Availability exploded. More channels, more content, more reach. But the basic operating model and economics stayed recognizable. Television was still television. Prime time still mattered. The machinery of the industry still made sense to the people inside it.&lt;/p&gt;

&lt;p&gt;Then the 2010s happened.&lt;/p&gt;

&lt;p&gt;Streaming did not just compete with network and cable television. It reorganized the entire landscape. Very quickly, the old structure stopped being the center of gravity. The audience changed. The business model changed. Distribution changed. Viewing habits changed. Many of the jobs my dad did over the course of his career simply do not exist anymore.&lt;/p&gt;

&lt;p&gt;The industry did not die, it transformed into something that most people inside it probably could not have fully envisioned until after it happened.&lt;/p&gt;

&lt;p&gt;Johnny Carson was eventually followed by Letterman, Leno, Conan, and then a parade of Jimmys. But even that version of the industry turned out not to be permanent. It was gradually overtaken by something far more fragmented: MrBeast, the Paul brothers, YouTubers, Twitch streamers, podcasters, TikTok creators, and countless internet-native personalities who built audiences outside the old system entirely. The old model was not replaced by a new host. It was replaced by a new landscape.&lt;/p&gt;

&lt;p&gt;My dad jokes that prime time is what paid for his retirement, and I think that line captures a lot. That whole world was real. It was massive. It shaped careers, businesses, and lives. It also largely does not exist anymore in the form that once felt permanent.&lt;/p&gt;

&lt;p&gt;That does not mean television ended. There is more video content than ever. More ways to watch it. More creators. More distribution. More total output. But the old structure was not preserved just because it had once seemed foundational. It was replaced by a new one.&lt;/p&gt;

&lt;p&gt;I think software may be heading for a similar kind of shift.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;For People Who Love Solving Problems, This Could Be a Golden Age&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A lot of programmers don't actually love typing code for its own sake, they love solving problems.&lt;/p&gt;

&lt;p&gt;They love understanding systems. They love building tools. They love making messy things clean. They love turning ambiguity into structure. They love the feeling of gaining leverage over a problem that looked impossible a few hours ago. That part is not going away.&lt;/p&gt;

&lt;p&gt;The problems are changing. The tools are changing. The constraints are changing. The surface area of what one person can build is changing. For people who do this for the love of the game, that is exciting because everything will be in motion.&lt;/p&gt;

&lt;p&gt;There will be new abstractions, new workflows, new product categories, new expectations, and new kinds of technical judgment. There will be new opportunities to build things that were previously out of reach. There will be a massive amount of room for experimentation.&lt;/p&gt;

&lt;p&gt;If you like problem solving more than you like protecting old workflows, this moment is full of possibilities.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;The Transition Will Be Messy&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Industrialization is disruptive.&lt;/p&gt;

&lt;p&gt;There will be companies that overcorrect badly. There will be teams that are reduced too aggressively. There will be low-quality software shipped at scale because someone decided speed mattered more than understanding. There will be a period where many people are asked to operate in systems that have changed faster than the norms around them.&lt;/p&gt;

&lt;p&gt;If your current job is mostly composed of work that is easy to standardize, I don't think denial is a useful response. The market is changing. Pretending otherwise will not help. The better response, at least to me, is adaptation.&lt;/p&gt;

&lt;p&gt;Learn the tools.&lt;br&gt;&lt;br&gt;
Understand the new economics.&lt;br&gt;&lt;br&gt;
Move toward the parts of the work where judgment matters.&lt;br&gt;&lt;br&gt;
Get better at problem framing, system design, evaluation, communication, and taste.&lt;br&gt;&lt;br&gt;
Become the person who can work with the new machinery without mistaking it for magic.&lt;/p&gt;

&lt;p&gt;That seems like a far more grounded response than mourning a version of the craft that was never going to stay frozen in time.&lt;/p&gt;

&lt;p&gt;Some jobs will shrink. Some will disappear. Some will be reborn in altered forms. Entire new layers of software work will appear. New tools will create new expectations. New economics will produce software that would never have existed under the old model. And a lot of what feels central today may someday feel like prime time television: once dominant, deeply real, and eventually no longer the thing that anchors the industry.&lt;/p&gt;

&lt;p&gt;If your goal is to keep building, keep learning, and keep solving interesting problems, it might be the most interesting moment the field has seen in a very long time.&lt;/p&gt;

&lt;p&gt;The craft of making software is not disappearing. But like every craft touched by new machinery, new economics, and new distribution, it is going to change shape.&lt;/p&gt;

&lt;p&gt;Some people will spend too much energy mourning that.&lt;/p&gt;

&lt;p&gt;I would rather spend mine learning the new tools and exploring what becomes possible next.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>discuss</category>
      <category>career</category>
    </item>
    <item>
      <title>The Problem With `git add .`</title>
      <dc:creator>Jerry Schneider</dc:creator>
      <pubDate>Tue, 24 Mar 2026 03:04:20 +0000</pubDate>
      <link>https://dev.to/jerry_schneider/the-problem-with-git-add--30jd</link>
      <guid>https://dev.to/jerry_schneider/the-problem-with-git-add--30jd</guid>
      <description>&lt;p&gt;When I first learned I could run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of adding files one at a time, I felt like I had discovered some secret Git trick.&lt;/p&gt;

&lt;p&gt;No more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add file1
git add file2
git add file3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just stage everything and move on.&lt;/p&gt;

&lt;p&gt;And honestly, for a while, that &lt;em&gt;was&lt;/em&gt; an upgrade. It removed friction. It made committing faster. It felt like the kind of thing that separated people who were comfortable in Git from people who were still fumbling through it.&lt;/p&gt;

&lt;p&gt;But like a lot of “smart” shortcuts, it worked best right up until real life got involved.&lt;/p&gt;

&lt;p&gt;Sometimes I had a tmp file sitting around and committed it by accident.&lt;/p&gt;

&lt;p&gt;Sometimes I had changes I liked in one file and changes I definitely did &lt;em&gt;not&lt;/em&gt; like in another.&lt;/p&gt;

&lt;p&gt;Sometimes I forgot to switch branches before starting a new ticket and ended up with a mix of unrelated edits.&lt;/p&gt;

&lt;p&gt;And lately, with more AI-assisted coding tools in the mix, I’ve ended up with even more repo noise than I used to. Cursor in particular is great at helping me think and explore, but it also tends to leave behind markdown files and other artifacts that are useful while I’m working and absolutely not something I want in a commit.&lt;/p&gt;

&lt;p&gt;That was the point where &lt;code&gt;git add .&lt;/code&gt; stopped feeling smart.&lt;/p&gt;

&lt;p&gt;The problem wasn’t that Git was bad. The problem was that &lt;code&gt;git add .&lt;/code&gt; assumes everything in your working tree belongs together. And a lot of the time, that’s just not true.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Work Is Messy (for me at least, maybe you're different)
&lt;/h2&gt;

&lt;p&gt;In theory, a commit is supposed to represent one coherent idea.&lt;/p&gt;

&lt;p&gt;In practice, a working tree often looks more like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one file with the actual fix&lt;/li&gt;
&lt;li&gt;one file with a failed attempted fix&lt;/li&gt;
&lt;li&gt;one file with a cleanup you’re not sure you want&lt;/li&gt;
&lt;li&gt;one file you edited while debugging&lt;/li&gt;
&lt;li&gt;one random note or markdown file created during exploration&lt;/li&gt;
&lt;li&gt;one change that really belongs on another branch entirely&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How often does your working tree actually contain just one coherent change?&lt;/p&gt;

&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; deal with that using standard Git commands. You can manually add files one by one. You can use patch mode. You can keep opening &lt;code&gt;git status&lt;/code&gt; and re-reading the same list over and over until you convince yourself you know what should be staged.&lt;/p&gt;

&lt;p&gt;I’ve done all of that.&lt;/p&gt;

&lt;p&gt;But in a normal day of development, the issue usually is not “is this possible in Git?” The issue is “what is the lowest-friction way to do the right thing?”&lt;/p&gt;

&lt;p&gt;That is the bar I care about.&lt;/p&gt;

&lt;p&gt;Because if the right thing is annoying, I am much more likely to do the easy thing.&lt;/p&gt;

&lt;p&gt;And for a long time, the easy thing was &lt;code&gt;git add .&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Wanted
&lt;/h2&gt;

&lt;p&gt;I didn’t need some giant Git UI.&lt;/p&gt;

&lt;p&gt;I wanted a very small workflow improvement:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;show me the files that matter&lt;/li&gt;
&lt;li&gt;let me inspect them one at a time&lt;/li&gt;
&lt;li&gt;let me stage only the ones I want&lt;/li&gt;
&lt;li&gt;make the whole thing fast enough that I’ll actually use it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was it.&lt;/p&gt;

&lt;p&gt;So I wrote a tiny &lt;code&gt;add&lt;/code&gt; function for my dotfiles.&lt;/p&gt;

&lt;p&gt;It uses &lt;code&gt;git status --porcelain&lt;/code&gt; to collect files that need attention, pipes them into &lt;code&gt;fzf&lt;/code&gt;, gives me a preview for whatever file is currently selected, and then stages only the files I choose.&lt;/p&gt;

&lt;p&gt;That sounds simple because it is simple. But it ended up changing my workflow more than I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Function
&lt;/h2&gt;

&lt;p&gt;Here’s &lt;a href="https://github.com/schnej7/dotfiles/blob/main/bash/aliases.bash#L10" rel="noopener noreferrer"&gt;the function from my dotfiles&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function &lt;/span&gt;add&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;# Get files that need to be staged during rebase&lt;/span&gt;
  &lt;span class="c"&gt;# This includes: unmerged paths (conflicts), modified files, new files, etc.&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;files
  &lt;span class="nv"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'^(UU|AA|DD|AU|UA|DU|UD)'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/^...//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="c"&gt;# If no merge conflicts, check for regular unstaged files&lt;/span&gt;
  &lt;span class="c"&gt;# Include MM (staged but edited again), M (modified), D (deleted), and ?? (untracked)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$files&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'^MM |^ M |^ D |^D |^\?\?'&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/^...//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="c"&gt;# Sort and remove duplicates&lt;/span&gt;
  &lt;span class="nv"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$files&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$files&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No files need to be staged."&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;0
  &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="c"&gt;# Use fzf to select files with appropriate preview&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;selected_files
  &lt;span class="nv"&gt;selected_files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$files&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | fzf &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--header&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'Select files to stage (Tab: multi-select, Enter: confirm)'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--preview&lt;/span&gt; &lt;span class="s1"&gt;'cd $(git rev-parse --show-toplevel) &amp;amp;&amp;amp; {
      file={};
      status=$(git status --porcelain "$file" 2&amp;gt;/dev/null | cut -c1-2);

      if echo "$status" | grep -q "??"; then
        # New file: show contents in green
        while IFS= read -r line || [ -n "$line" ]; do
          printf "\x1b[32m%s\x1b[0m\n" "$line";
        done &amp;lt; "$file" 2&amp;gt;/dev/null

      elif echo "$status" | grep -qE " D|D "; then
        # Deleted file: show contents in red from HEAD
        git show HEAD:"$file" 2&amp;gt;/dev/null | while IFS= read -r line || [ -n "$line" ]; do
          printf "\x1b[31m%s\x1b[0m\n" "$line";
        done

      else
        # Modified file: show diff
        git diff --color=always "$file" 2&amp;gt;/dev/null ||
        git diff --cached --color=always "$file" 2&amp;gt;/dev/null ||
        echo "Binary file or no changes"
      fi
    }'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--preview-window&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;right:60%:wrap&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$selected_files&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="c"&gt;# Convert newlines to array and add each file&lt;/span&gt;
    &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; file&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--show-toplevel&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        git add &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Staged: &lt;/span&gt;&lt;span class="nv"&gt;$file&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
      &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$selected_files&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is just a focused wrapper around one very common decision: &lt;em&gt;which files should go into this commit?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Feels Better Than &lt;code&gt;git status&lt;/code&gt; + &lt;code&gt;git add&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of treating &lt;code&gt;git add&lt;/code&gt; as the mechanical thing I do before &lt;code&gt;git commit&lt;/code&gt;, I use it as a first review of the changes themselves.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsilp8ca7gf96ti7978lz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsilp8ca7gf96ti7978lz.png" alt="screenshot of add command in action" width="800" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I can move through the list file by file and immediately see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;yes, this belongs&lt;/li&gt;
&lt;li&gt;no, that was an experiment&lt;/li&gt;
&lt;li&gt;that markdown file is just tooling noise&lt;/li&gt;
&lt;li&gt;this file is from a different line of work and should stay out&lt;/li&gt;
&lt;li&gt;this one needs a second look before I stage it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That sounds minor, but it changes the feel of the whole workflow.&lt;/p&gt;

&lt;p&gt;A normal &lt;code&gt;git status&lt;/code&gt; gives you a list.&lt;/p&gt;

&lt;p&gt;This enables intention.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Few Details That Make It Great
&lt;/h2&gt;

&lt;p&gt;What makes this more useful than a generic &lt;code&gt;fzf&lt;/code&gt; wrapper is that it is opinionated in the right places.&lt;/p&gt;

&lt;p&gt;If I’m in the middle of a rebase, it surfaces conflicted files first. It also catches easy-to-miss states like files that were staged and then edited again. And the preview adapts to the file: file contents for new files, file contents from &lt;code&gt;HEAD&lt;/code&gt; for deleted files, diff for modified files.&lt;/p&gt;

&lt;p&gt;That means I’m not staging based on filenames and vague recollection. I’m staging based on the actual code changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI Makes This Matter More
&lt;/h2&gt;

&lt;p&gt;I think this kind of workflow matters more now than it did a few years ago.&lt;/p&gt;

&lt;p&gt;AI coding tools are genuinely useful, but they also make it much easier to generate clutter. Scratch markdown files. notes-to-self. temporary planning documents. throwaway code paths. files that were useful during exploration but were never meant to become part of the final change.&lt;/p&gt;

&lt;p&gt;That is not really a criticism. It is just a side effect of a more exploratory workflow.&lt;/p&gt;

&lt;p&gt;The more your tools help you branch out while thinking, the more you need a clean way to narrow back down before committing.&lt;/p&gt;

&lt;p&gt;That is where selective staging becomes more important.&lt;/p&gt;

&lt;p&gt;Not because the repo is huge.&lt;/p&gt;

&lt;p&gt;Not because Git got harder.&lt;/p&gt;

&lt;p&gt;Just because the path from “working” to “ready to commit” now tends to produce more debris.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Branch Mistake Case
&lt;/h2&gt;

&lt;p&gt;One of the most common real-world cases for this is embarrassingly simple:&lt;/p&gt;

&lt;p&gt;I start changing files before I switch branches.&lt;/p&gt;

&lt;p&gt;Now I have some work that belongs to the new ticket and some work that definitely does not.&lt;/p&gt;

&lt;p&gt;Could I stash things? Sure.&lt;/p&gt;

&lt;p&gt;Could I do some more elaborate cleanup? Of course.&lt;/p&gt;

&lt;p&gt;But sometimes the fastest route is just being able to stage exactly the files that belong to the commit I’m making right now.&lt;/p&gt;

&lt;p&gt;It gets me out of a very normal mess efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  This Made Me Commit More Often
&lt;/h2&gt;

&lt;p&gt;The most important effect of this function is not that it helps me avoid bad commits.&lt;/p&gt;

&lt;p&gt;It is that it lowers the cost of good commits.&lt;/p&gt;

&lt;p&gt;When I know I can quickly inspect what changed, skip the junk, and stage only the relevant files, I feel much less resistance to committing early.&lt;/p&gt;

&lt;p&gt;“Commit early and often” is one of those bits of advice that is obviously correct and weirdly hard to follow in practice.&lt;/p&gt;

&lt;p&gt;The reason it is hard is not philosophical. It is usually just friction.&lt;/p&gt;

&lt;p&gt;If every commit feels like I have to manually sort through a pile of ambiguous changes, I am more likely to wait.&lt;/p&gt;

&lt;p&gt;If I wait, the pile gets bigger.&lt;/p&gt;

&lt;p&gt;If the pile gets bigger, the commit gets messier.&lt;/p&gt;

&lt;p&gt;If the commit gets messier, I wait even longer.&lt;/p&gt;

&lt;p&gt;This function helps break that cycle.&lt;/p&gt;

&lt;p&gt;When committing becomes cheap again, git history gets cleaner almost automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small Shell Improvements Compound
&lt;/h2&gt;

&lt;p&gt;I like this kind of shell customization because it solves a very specific annoyance without introducing much complexity.&lt;/p&gt;

&lt;p&gt;It's a tiny tool that makes one repeated decision easier.&lt;/p&gt;

&lt;p&gt;That is the sweet spot I keep coming back to in my dotfiles.&lt;/p&gt;

&lt;p&gt;The reason I like little workflow tools like this is not that they are clever. It is that they can quietly change your defaults.&lt;/p&gt;

&lt;p&gt;Most of the time, the gap between a sloppy workflow and a clean one is not knowledge. It is friction.&lt;/p&gt;

&lt;p&gt;If the better path asks for more effort, the shortcut usually wins.&lt;/p&gt;

&lt;p&gt;If you can remove enough friction that the better path becomes the simpler one, your habits improve almost by accident.&lt;/p&gt;

&lt;p&gt;That is what this function did for me. It made a careful commit easier to make than a careless one.&lt;/p&gt;

&lt;p&gt;And that is usually when the right choice starts winning without a fight.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>cli</category>
      <category>git</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How I Made fzf Feel Snappy Again in Large Repositories</title>
      <dc:creator>Jerry Schneider</dc:creator>
      <pubDate>Sat, 21 Mar 2026 02:59:59 +0000</pubDate>
      <link>https://dev.to/jerry_schneider/how-i-made-fzf-feel-snappy-again-in-large-repositories-208n</link>
      <guid>https://dev.to/jerry_schneider/how-i-made-fzf-feel-snappy-again-in-large-repositories-208n</guid>
      <description>&lt;p&gt;Most of the time when I’m working in the terminal, I’m navigating files.  Usually that looks like some combination of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd
ls
&lt;/span&gt;find
&lt;span class="nb"&gt;grep&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which works fine… until a project gets large.  Then it turns into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;src
&lt;span class="nb"&gt;ls
cd &lt;/span&gt;components
&lt;span class="nb"&gt;ls
cd &lt;/span&gt;something
find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-iname&lt;/span&gt; something
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I wanted something closer to Ctrl+P in an editor.  A quick fuzzy search that lets me jump to files or directories instantly. So I added a small function to my .bashrc that lets me browse the filesystem with &lt;code&gt;fzf&lt;/code&gt; like a tiny interactive file browser:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fuzzy search files and directories&lt;/li&gt;
&lt;li&gt;preview files&lt;/li&gt;
&lt;li&gt;preview directory contents&lt;/li&gt;
&lt;li&gt;open files in &lt;code&gt;$EDITOR&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cd&lt;/code&gt; into directories&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Function
&lt;/h2&gt;

&lt;p&gt;Here’s &lt;a href="https://github.com/schnej7/dotfiles/blob/main/bash/aliases.bash#L194" rel="noopener noreferrer"&gt;the function from my dotfiles&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Browse files and directories with fzf - cd to directories, open files with $EDITOR&lt;/span&gt;
&lt;span class="k"&gt;function &lt;/span&gt;browse&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; fzf &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"fzf not found"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

 &lt;span class="nb"&gt;local &lt;/span&gt;selection &lt;span class="nv"&gt;fifo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/tmp/browse_&lt;/span&gt;&lt;span class="nv"&gt;$$&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

 &lt;span class="c"&gt;# Create named pipe&lt;/span&gt;
 &lt;span class="nb"&gt;mkfifo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$fifo&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

 &lt;span class="c"&gt;# Run find (or fd if available) in background, writing to named pipe (suppress job control)&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; fd &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
   &lt;span class="o"&gt;{&lt;/span&gt; fd &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; f &lt;span class="nt"&gt;-t&lt;/span&gt; d &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$fifo&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &amp;amp;
 &lt;span class="k"&gt;else&lt;/span&gt;
   &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-type&lt;/span&gt; d 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s|^\./||'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$fifo&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &amp;amp;
 &lt;span class="k"&gt;fi
 &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;find_pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$!&lt;/span&gt;

 &lt;span class="c"&gt;# Run fzf reading from named pipe&lt;/span&gt;
 &lt;span class="nv"&gt;selection&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;fzf &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;:+-q&lt;span class="p"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--preview&lt;/span&gt; &lt;span class="s1"&gt;'if [[ -d {} ]]; then ls -la {}; else bat --style=numbers --color=always --line-range :50 {} 2&amp;gt;/dev/null || cat {} 2&amp;gt;/dev/null || echo "Cannot preview file"; fi'&lt;/span&gt; &amp;lt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$fifo&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
 &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;exit_code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;

 &lt;span class="c"&gt;# Kill background process and cleanup (suppress all output)&lt;/span&gt;
 &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="nv"&gt;$find_pid&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
 &lt;span class="nb"&gt;wait&lt;/span&gt; &lt;span class="nv"&gt;$find_pid&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
 &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$fifo&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

 &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nv"&gt;$exit_code&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$exit_code&lt;/span&gt;

 &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$selection&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return

 if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$selection&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
   &lt;/span&gt;&lt;span class="nb"&gt;builtin cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$selection&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
 &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$selection&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
   &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;EDITOR&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;vim&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$selection&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
 &lt;span class="k"&gt;else
   &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Selection is neither a file nor a directory: &lt;/span&gt;&lt;span class="nv"&gt;$selection&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
   &lt;span class="k"&gt;return &lt;/span&gt;1
 &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or even:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;browse bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And instantly fuzzy-search everything under the current directory.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpm45lj6k7ibdraze7sd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvpm45lj6k7ibdraze7sd.png" alt="browse running searching for bash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Streaming Results to &lt;code&gt;fzf&lt;/code&gt; with a Named Pipe
&lt;/h2&gt;

&lt;p&gt;This is the most interesting part.&lt;/p&gt;

&lt;p&gt;Instead of running &lt;code&gt;find&lt;/code&gt; first and piping into &lt;code&gt;fzf&lt;/code&gt;, the function uses a named pipe.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkfifo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$fifo&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;find&lt;/code&gt; (or &lt;code&gt;fd&lt;/code&gt; for better performance) runs in the background writing results into the pipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fd ... &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$fifo&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And &lt;code&gt;fzf&lt;/code&gt; reads from the pipe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;fzf &amp;lt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$fifo&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means:&lt;br&gt;
&lt;code&gt;fzf&lt;/code&gt; can start immediately while results are still being discovered. For large directories this feels much faster and allows you to make a selection before &lt;code&gt;find&lt;/code&gt; completes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Binding the Function Directly in Bash
&lt;/h2&gt;

&lt;p&gt;Instead of triggering browse by injecting a command into the prompt, I prefer binding it directly to a key in Bash.&lt;/p&gt;

&lt;p&gt;Here’s &lt;a href="https://github.com/schnej7/dotfiles/blob/main/bash/aliases.bash#L571" rel="noopener noreferrer"&gt;the helper I use&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;## Bindings ##&lt;/span&gt;
bind_bash_function&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
 &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
 &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;fn&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
 &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;wrapper&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"__bind_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;//[^a-zA-Z0-9_]/_&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

 &lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"
&lt;/span&gt;&lt;span class="nv"&gt;$wrapper&lt;/span&gt;&lt;span class="s2"&gt;() {
 local __line=&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;READLINE_LINE
 local __point=&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;READLINE_POINT

 &lt;/span&gt;&lt;span class="nv"&gt;$fn&lt;/span&gt;&lt;span class="s2"&gt;

 READLINE_LINE=&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;__line
 READLINE_POINT=&lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;__point
}
"&lt;/span&gt;

 &lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; emacs-standard &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="nv"&gt;$wrapper&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
 &lt;span class="nb"&gt;bind&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; vi-insert      &lt;span class="nt"&gt;-x&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="nv"&gt;$wrapper&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

bind_bash_function &lt;span class="s1"&gt;'\C-f'&lt;/span&gt; browse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This binds Ctrl+F directly to the browse function.&lt;br&gt;
The small wrapper saves the current command line (READLINE_LINE and cursor position), runs the function, and then restores the line afterwards. That way the binding behaves more like a temporary tool: it runs, does its work, and then returns you to exactly the same prompt state you were in before.&lt;/p&gt;

&lt;p&gt;This makes the interaction feel much smoother, especially if you trigger it while you’re in the middle of typing a command. The helper also binds the key for both Emacs-style and vi-style editing modes so the behavior stays consistent regardless of the editing mode in use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I Prefer This Over Other Tools
&lt;/h2&gt;

&lt;p&gt;There are plenty of file navigation tools out there, but I like this approach because it stays simple and fits naturally into my shell. It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lives entirely in my &lt;code&gt;.bashrc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;only depends on &lt;code&gt;fzf&lt;/code&gt; (and &lt;code&gt;bat&lt;/code&gt; if you want color)&lt;/li&gt;
&lt;li&gt;can be bound directly to a key like Ctrl+F&lt;/li&gt;
&lt;li&gt;returns you to exactly the same command line after it runs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Binding it directly to a key makes it feel less like a command I have to remember and more like a built-in capability of the shell. It’s just a small shell upgrade, but it removes a surprising amount of friction from everyday workflows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small Shell Improvements Compound
&lt;/h2&gt;

&lt;p&gt;I treat my dotfiles like a collection of tiny productivity improvements.  Each one only saves a few seconds, but when you run thousands of shell commands every week, those seconds add up. This browse function ended up being one of my favorites.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>cli</category>
      <category>productivity</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
