<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.3.3">Jekyll</generator><link href="https://jeffmorhous.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://jeffmorhous.com/" rel="alternate" type="text/html" /><updated>2026-02-02T16:58:14+00:00</updated><id>https://jeffmorhous.com/feed.xml</id><title type="html">Jeff Morhous</title><subtitle>Jeff is a senior software engineer building with AI and writing about it.</subtitle><author><name>Jeff Morhous</name></author><entry><title type="html">You should still bother to learn to actually program</title><link href="https://jeffmorhous.com/learn-to-program-with-ai/" rel="alternate" type="text/html" title="You should still bother to learn to actually program" /><published>2025-12-27T12:01:35+00:00</published><updated>2025-12-27T12:01:35+00:00</updated><id>https://jeffmorhous.com/learn-to-program-with-ai</id><content type="html" xml:base="https://jeffmorhous.com/learn-to-program-with-ai/"><![CDATA[<p>Learning to code today is weird.</p>

<p>The software engineering job market was NUTS just 4 years ago. Now, the job market is constricting. Sure, some of it’s due to macro stuff, but plenty of the pressure is from AI.</p>

<p>When ChatGPT came out, I quickly started using to help me program. Some syntax lookups here, some bug hunting there.</p>

<p>Then came Cursor. Shortly after, Claude Code. In-between, Replit, Lovable, v0, etc.
Programming has changed, and it changed FAST. You can build apps that used to take days-to-weeks in less than an hour. You can do <em>real software engineering work</em> without actually writing a single line of code.</p>

<p>Some people are interpreting this as the end of the profession, but <a href="https://www.augmentedswe.com/p/ai-wont-replace-most-software-engineers">I don’t think software engineering</a> is going away completely.</p>

<p>I think the best engineers 5 years from now are going to be the ones that understand programming incredibly well <strong>and leverage AI tools effectively</strong>.</p>

<p>If you want to be a software engineer, you should still learn to program (even if vibe coding tool startups are telling you otherwise).</p>

<h2 id="learning-to-code-is-changing">Learning to code is changing</h2>

<p>If I were starting from zero today, I’d approach learning to code very differently than people did even five years ago.</p>

<p>The optimal <em>path</em> to a career in tech has changed. <a href="https://www.augmentedswe.com/p/how-generative-ai-is-already-destroying">Junior developer jobs are getting eaten by AI</a> because AI can write a shocking amount of code now. That’s both a cheat code and a trap.</p>

<p>You can ship real things faster than ever without ever building the mental models that make you dangerous in a codebase. This will cause you problems in a real development job.</p>

<p>You should bother to learn to actually program.</p>

<p>Here’s exactly how I’d do it.</p>

<h2 id="60-fundamentals-40-building-stuff-with-ai">60% fundamentals, 40% building stuff (with AI)</h2>

<p>If you only build projects with AI, you’ll get dopamine and cool screenshots for a portfolio, but you’ll build a shaky foundation that will cause you real problems.</p>

<p>If you only study fundamentals, you’ll get theory and no momentum, forcing you to move slower than you actually have to move.</p>

<p>So I’d split it:</p>

<ul>
  <li><strong>60% fundamentals</strong>: learn the mechanics and mental models</li>
  <li><strong>40% building</strong>: ship small apps using tools like Cursor to accelerate learning</li>
</ul>

<p>You need both at the same time. I’ll show you the exact steps I’d follow, but first, a word about debugging.</p>

<h2 id="debugging-is-the-skill-that-makes-you-a-real-programmer">Debugging is the skill that makes you a real programmer</h2>

<p>This is the part beginners skip in the age of LLMs, because AI “fixes” lots of bugs without much effort from you.</p>

<p>I would make debugging a first-class skill from day one.</p>

<p>You need to get comfortable with:</p>

<ul>
  <li>reading an error message without panicking</li>
  <li><strong>reading a stack trace</strong> and finding the real failing line</li>
  <li>reproducing bugs reliably</li>
  <li>inserting print statements or using a debugger</li>
  <li>narrowing the problem (what changed, what assumptions are false)</li>
  <li>understanding what the program is doing, not what you wish it were doing</li>
</ul>

<p>I can NOT stress enough how important it is to be able to read and understand a stack trace.</p>

<p>Here’s a rule I’d follow:</p>

<p><strong>Never paste an error into AI until you’ve read it, located it in code, and formed a guess about what it means.</strong></p>

<p>Even if your guess is wrong, that act is building your brain.</p>

<p>Because the people who “make it” in software are the ones who can stare at a broken system and methodically force it to make sense.</p>

<h2 id="step-1-pick-one-mainstream-language-and-commit-for-3-months">Step 1: Pick one mainstream language and commit for 3 months</h2>

<p>Pick <strong>Python</strong> or <strong>JavaScript</strong>. I’d say pick Ruby because I love Ruby, but the job market for Rubyists is weird.</p>

<p>Pick one, commit, and start.</p>

<ul>
  <li>Pick <strong>Python</strong> if you want the smoothest “learn to code” ramp.</li>
  <li>Pick <strong>JavaScript</strong> if you’re excited about web apps and want to build in the browser.</li>
</ul>

<p>You can learn the other later. The first language is mostly about learning how <em>programming</em> works.</p>

<h2 id="step-2-do-one-intro-course-then-stop-taking-courses">Step 2: Do one intro course, then stop taking courses</h2>

<p>I’d do one solid beginner YouTube course to get the basics:</p>

<ul>
  <li>variables, functions, loops, conditionals</li>
  <li>data structures (arrays/lists, hashes/objects)</li>
  <li>basic I/O</li>
  <li>modules, packages</li>
  <li>how to run code and install dependencies</li>
</ul>

<p>Courses feel productive, but they’re a passive form of learning past the initial ramp.</p>

<p>After that, you need actual reps.</p>

<h2 id="step-3-grind-leetcode-easy-for-one-reason-fluency">Step 3: Grind LeetCode (Easy) for one reason: fluency</h2>

<p>Yes, LeetCode. This is good training for interviews at least for now (<a href="https://www.augmentedswe.com/p/meta-ai-assisted-interviews">tech companies are quickly making interviews AI-first</a>).</p>

<p>The main reason to do this is it to build muscle memory and learn the core data structures and algorithms without hiding behind frameworks.</p>

<p>What I’d do:</p>

<ul>
  <li>Do a <strong>ton</strong> of LeetCode Easy.</li>
  <li>When you get stuck, ask AI for hints, not answers.</li>
  <li>After you solve it, rewrite it from scratch the next day.</li>
</ul>

<p>You’re trying to internalize arrays/lists, strings, hash maps / dictionaries, stacks, queues, recursion basics, sorting/searching patterns, and time complexity intuition (you don’t need to be a wizard, just not clueless about how your code handles growing inputs).</p>

<p>This is where you build the “I can reason about code” brain. You can even <a href="https://www.augmentedswe.com/p/using-ai-as-a-leetcode-coach">use AI as a LeetCode coach!</a></p>

<p>I would do step 3 <strong>in parallel</strong> with step 4.</p>

<h2 id="step-4-build-tiny-projects-but-earn-the-right-to-use-less-ai">Step 4: Build tiny projects, but earn the right to use less AI</h2>

<p><em>I would do this step <strong>in parallel</strong> with step 4 (the above).</em></p>

<p>This is where Cursor (or similar tools) becomes a huge advantage.</p>

<p>But I would be intentional about it.</p>

<p>I’d build 3 to 5 basic apps:</p>

<ul>
  <li><strong>To-do app</strong></li>
  <li><strong>Flashcard app</strong></li>
  <li><strong>Medicine tracker</strong></li>
  <li>(optional) habit tracker, simple budget app, workout logger</li>
</ul>

<p>The rule is: small, boring, complete.</p>

<p>Use AI to speed you up, but add constraints so you’re still learning:</p>

<p>You keep doing this until you can build one of these apps <strong>without leaning heavily on AI</strong>.</p>

<h2 id="step-5-dont-go-fully-agentic-until-youve-gotten-reps">Step 5: Don’t go fully agentic until you’ve gotten reps</h2>

<p>Agentic tools like <a href="https://www.augmentedswe.com/p/building-a-feature-with-claude-code">Claude Code</a> are incredible. They’re also dangerous for beginners.</p>

<p>So early on, I’d skip the “AI goes off and does a bunch of stuff” approach.</p>

<p>Stick to an interactive tool like <strong>Cursor</strong>, where you’re still steering, still reading diffs, still making decisions.</p>

<p>Earn the right to go more agentic later.</p>

<h2 id="the-milestone-id-hit-before-committing-to-a-career-in-programming">The milestone I’d hit before “committing” to a career in programming</h2>

<p>Before spending years on a CS degree, bootcamp, or career switch, I’d want to prove something to myself:</p>

<p><strong>I can build a small app end-to-end, understand most of the code, and debug common issues and not hate the process.</strong></p>

<p>It will take time to gain this level of competency. If you can’t get there, you might still eventually become a great engineer, but you should learn that <em>before</em> you commit major time and money.</p>

<h2 id="if-you-do-commit-use-more-agentic-tools-but-keep-deliberate-practice">If you do commit, use more agentic tools, but keep deliberate practice</h2>

<p>Once you have baseline competence, it’s completely fine to lean harder into AI, including agentic workflows.</p>

<p>Just keep one habit alive to avoid skill atrophy:</p>

<p><strong>Always have a “learning mode” for some portion of your work.</strong></p>]]></content><author><name>Jeff Morhous</name></author><category term="career" /><category term="growth" /><category term="programming" /><summary type="html"><![CDATA[AI has changed software engineering. Many are wondering if they should bother to learn to actually program or if they can just use AI to program. Yes, you should still bother to actually learn...]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jeffmorhous.com/images/learning-to-code-with-ai.png" /><media:content medium="image" url="https://jeffmorhous.com/images/learning-to-code-with-ai.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Redefining Career Specialization</title><link href="https://jeffmorhous.com/redefining-career-specialization/" rel="alternate" type="text/html" title="Redefining Career Specialization" /><published>2024-05-30T12:01:35+00:00</published><updated>2024-05-30T12:01:35+00:00</updated><id>https://jeffmorhous.com/redefining-career-specialization</id><content type="html" xml:base="https://jeffmorhous.com/redefining-career-specialization/"><![CDATA[<p>In my short career, I’ve spent a bit of time thinking about:</p>

<ul>
  <li>How do I be the best software engineer?</li>
  <li>What goes into an outstanding career?</li>
  <li>How can I be considered a valuable individual contributor?</li>
</ul>

<p>Conversations I’ve had with peers and mentors quickly turn to the topic of specialization. If you read around online, you’ll find a lot of arguments about whether it’s better to be a specialist or a generalist in terms of the skills you build.</p>

<p>This is an especially notable topic when considering software engineering careers, because there’s <em>so much to learn, you can’t possibly be the best at all of it</em>.</p>

<h2 id="specialist-vs-generalist">Specialist vs Generalist</h2>

<p>Cal Newport writes often that if you want something above-average from your career, you have to provide above-average value.</p>

<p>In his “So Good They Can’t Ignore You”, he puts it clearly:</p>

<blockquote>
  <p>If you want something that’s both rare and valuable, you need something rare and valuable to offer in return—this is Supply and Demand 101. It follows that if you want a great job, you need something of great value to offer in return.</p>
</blockquote>

<p>The whole question of specializing vs generalizing for me is really a question of <strong>how I can offer the best value to my team.</strong> If I were superhuman, I would just become the best at all programming things and while I’m at it become the best at business things too. But that’s not realistic, so many of us find ourselves wondering where best to focus our limited energy.</p>

<p>It seems to be common advice, at least in software, that specializing <em>after building a foundation of knowledge</em> is a good way to differentiate yourself. <strong>But what does it mean to specialize?</strong></p>

<p>Saying that it’s good to specialize isn’t specific enough.</p>

<ul>
  <li>If I specialize in Ruby, am I really specialized?</li>
  <li>What about Ruby on Rails?</li>
  <li>What about backend Ruby on Rails?</li>
</ul>

<p>Even knowing that it’s a good move to specialize still isn’t enough information to make an actionable decision about what to get good at.</p>

<h2 id="keep-redefining-what-you-do">Keep Redefining What You Do</h2>

<p>We already know that being the best at what you do is great.</p>

<p>I don’t know about you, but becoming the world’s best programmer seems like a daunting task for me. <a href="https://nav.al/rich">Naval talks a bit about how redefining what you do</a> to make it easier to stand out:</p>

<blockquote>
  <p>If you really want to get paid in this world, you want to be number one at whatever you do. It can be niche—that’s the point. Become the best in the world at what you do. Keep redefining what you do until this is true.</p>
</blockquote>

<p>Just becoming the best programmer is too hard. Becoming the best ruby programmer is hard, but not as hard. Becoming the best Ruby on Rails backend developer is a little easier. Becoming the best person at improving tests for Ruby on Rails apps is niche enough to be achievable.</p>

<p><strong>It’s a lot easier to be the best in a category of 10 than best in a category of 10,000.</strong></p>

<h2 id="dual-mastery">Dual Mastery</h2>

<p>I’ve found that a good way to continue refining what I do is to develop mastery in more than one thing. This is sort of specializing - Steph Ango, creator of Obsidian, calls it <strong>hybridizing</strong>:</p>

<blockquote>
  <p>The hybrid path means developing expertise in two or more distinct areas. Having several specialties allows you to see patterns that no one else can see, and make contributions that no one else would think of. The world needs more hybrid people.</p>
</blockquote>

<p>My favorite flavor of Hybrid that Steph describes is the <strong>U-shaped hybrid, where one develops a wide base of skills with 2 specialties.</strong></p>

<p>I’m still figuring out what this means for myself. I like to write backend code with Rails, but I’m also trying to develop my skills in infrastructure. I like to write code, but I also like to teach people how to write code through long-form tutorials. Fortunately, both of these paths are complimentary.</p>

<p>In the end, careers are long and these aren’t one-way doors. I’m having fun experimenting and learning - I hope you are too.</p>]]></content><author><name>Jeff Morhous</name></author><category term="career" /><category term="growth" /><category term="programming" /><summary type="html"><![CDATA[Just becoming the best programmer is too hard. Becoming the best ruby programmer is hard, but not as hard. Becoming the best Ruby on Rails backend developer is a little easier...]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jeffmorhous.com/images/specialize.webp" /><media:content medium="image" url="https://jeffmorhous.com/images/specialize.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Software Engineers Need a Brag Doc</title><link href="https://jeffmorhous.com/software-engineers-need-a-brag-doc/" rel="alternate" type="text/html" title="Software Engineers Need a Brag Doc" /><published>2024-03-01T12:01:35+00:00</published><updated>2024-03-01T12:01:35+00:00</updated><id>https://jeffmorhous.com/software-engineers-need-a-brag-doc</id><content type="html" xml:base="https://jeffmorhous.com/software-engineers-need-a-brag-doc/"><![CDATA[<p>A brag doc is a place where you keep track of (and ideally show off) the work you’re proud of. You can call it what you want - A brag doc, personal wins, and impact list, whatever makes you feel the most comfortable sharing. Brag documents are helpful in any knowledge work career, but especially important in software engineering roles where career progress is largely connected to how well you demonstrate impact on the business.</p>

<h2 id="how-a-brag-doc-helps-software-engineers">How a brag doc helps software engineers</h2>

<p>Maintaining a brag doc takes time, so many people are hesitant to start. There are <strong>plenty of reasons</strong> why a brag doc is worth the work. Most of these reasons point towards ways for you and your manager to avoid recency bias when thinking about your performance.</p>

<p>For starters, a brag document <strong>helps you remember what you did at work</strong>. This is a huge benefit to your career for a number of reasons, but a little-appreciated reason is that it gives you visibility into your time spent.</p>

<p>You can work ten times harder than anyone around you and it still not matter for your growth or career if you spent that energy working on the wrong things. A brag doc helps you retrospect on whether you’re actually executing on things that push you towards you goals.</p>

<p>The second benefit is that <strong>a brag doc helps your manager (and maybe even their manager) appreciate the value you bring to the business.</strong> Whether they’re thinking about your performance for review season or trying to justify a promotion, a brag document helps your leadership avoid recency bias. An organized list (we’ll get to that later) of what you did <em>and why it matters</em> gives clear visibility into your impact, which can only help you in performance reviews and promotion committees.</p>

<h2 id="how-to-make-a-brag-doc-as-a-software-engineer">How to make a brag doc as a software engineer</h2>

<p>If you’re still reading, maybe you’re convinced to make and maintain a brag document. You could start by just keeping a note of all the work you’ve done, but putting in a little extra effort goes a long way.</p>

<p>These days, I recommend <a href="https://www.augmentedswe.com/p/how-to-write-a-brag-doc-using-ai">using AI to make the best brag doc</a>.</p>

<p>As a software engineer, you can start by going to your issue tracker or version control system like Jira or Github and copying work you’ve completed recently. Each item should have a “what you did” along with a “why it matters”. Something like “shipped this linked pr that fulfilled my team’s xyz OKR” is a great general format. Half an hour spent doing this ought to yield at least a quarter’s worth of work, which should give you enough space to start organizing.</p>

<p><strong>The way you format your brag doc is largely dependent on your primary goal or audience</strong>, so there isn’t a one-size-fits all template. Most recently, the primary purpose of my brag doc was to make it easier for my manager to advocate for my promotion. I wanted to <a href="https://jeffmorhous.com/learning-faster-as-a-software-engineer/">grow faster as a developer</a>, and I wanted to get recognized for that growth.</p>

<p>Because of my goal, the format of my brag doc looked a lot like a promotion justification. I started tracking how the work I did aligned with the expectations outlined for the level above mine. After a year or so, I had a dense document that outlined how I was fulfilling the expectations of the role above mine, supported with links to design documents, code reviews, and pull requests.</p>

<p>These days I’m still sorting out the format of my brag doc, but I’m leaning towards aligning work under headers that represent goals I’m working towards for both my performance and areas I’d like to improve in.</p>

<p>It’s just as important to track work that isn’t explicitly found in a pull request or Jira ticket. Glue work like helping peers and responding to incidents is all valuable to the business, so it belongs on your brag doc. This is the hardest work to remember, so you may have to ask your peers or leadership if any work you did comes to mind as particularly helpful to them.</p>

<h2 id="conclusion">Conclusion</h2>

<p>A brag doc is best done when updated regularly. Trying to do this on a yearly or even quarterly basis is playing life on hard mode. I have a regular calendar reminder to update my document every 2 weeks, which makes it pretty painless.</p>

<p>It’s up to you to shamelessly advocate for yourself. Keeping a running list of the impact you have definitely isn’t the most fulfilling work you’ll do as a developer, but it might be the most valuable for your career.</p>]]></content><author><name>Jeff Morhous</name></author><category term="career" /><category term="growth" /><category term="programming" /><summary type="html"><![CDATA[A brag doc is a place where you keep track of (and ideally show off) the work you're proud of. You can call it what you...]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jeffmorhous.com/images/brag-doc.webp" /><media:content medium="image" url="https://jeffmorhous.com/images/brag-doc.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Block Ads for Your Entire Home Network with a Raspberry Pi</title><link href="https://jeffmorhous.com/block-ads-for-your-entire-network-with-a-raspberry-pi/" rel="alternate" type="text/html" title="Block Ads for Your Entire Home Network with a Raspberry Pi" /><published>2024-02-18T12:01:35+00:00</published><updated>2024-02-18T12:01:35+00:00</updated><id>https://jeffmorhous.com/block-ads-for-your-entire-network-with-a-raspberry-pi</id><content type="html" xml:base="https://jeffmorhous.com/block-ads-for-your-entire-network-with-a-raspberry-pi/"><![CDATA[<p>Updated for 2024! Run a DNS sinkhole on a Raspberry Pi, including the Raspberry Pi 5.</p>

<p>I got a Raspberry Pi for my birthday last year and have been <em>suffering analysis paralysis</em>, trying to figure out what I want to do with it.</p>

<p>I recently came across some  <a href="https://pi-hole.net/">open source software called Pi-hole</a>, that boasts the ability to block ads for your entire network. Perplexed, I had to see how this worked, so here’s how I set up my Raspberry Pi to act as a black hole for all things advertisement related. Not only will you experience a (mostly)  <strong>ad-free internet</strong>, but things like Smart-TV’s and other “sketchy” analytics tools won’t be able to send their data home.  <strong>Hurray privacy!</strong></p>

<p>If you have any questions, leave a response below or feel free to  <a href="http://twitter.com/jeffmorhous">tweet me!</a></p>

<h2 id="step-1-aquire-hardware-">Step 1: Aquire Hardware 🖥</h2>

<p>The Raspberry Pi is an incredibly cheap computer, which makes it a great candidate for such a specific task.</p>

<p>I have tried this on both the Raspberry Pi Model 3 Model B and the new Raspberry Pi 5. It’s wireless, so I didn’t need an adapter. However, if you buy a model without WiFi, you will need to either wire it into your router with an ethernet cable or buy a WiFi adapter. You’ll need a power supply if you don’t have one already, as well as a usb mouse and keyboard. You’ll</p>

<p>You’ll also need a  microSD cardto store the operating system (more on that later). You’ll need an HDMI cable for the Raspberry Pi 3, or a microHDMI to HDMI cable for the Raspberry Pi 5.</p>

<h2 id="step-2-install-a-headless-operating-system"><strong>Step 2: Install a Headless Operating System</strong></h2>

<p>The Raspberry Pi is pretty bare-bones. You can choose your operating system from a number of interesting choices based on the task you’re trying to accomplish. First, install the official <a href="https://www.raspberrypi.com/software/">Raspberry Pi imager from the website.</a>. Given that we’re just going to run the Pi-hole software, I went with  <strong>Raspbian Lite</strong>, an open source port of the Linux operating system. The lite version comes headless (with no desktop), which is perfect since we will only connect to the Pi-hole remotely. When you launch the imager, it will have you select your hardware, the operating system you’d like, and where to install it (your SD card!).</p>

<p>You should also customize settings to enable SSH with a password, as we’ll use that to connect to the Raspberry Pi in the next step.</p>

<p>When you’re done installing the operating system, you can eject the SD card and insert into into the Raspberry Pi. You can run the Pi-hole wirelessly, but if you can connect it directly to your router via ethernet you can avoid any wireless connectivity problems.</p>

<h2 id="step-3-ssh-to-raspberry-pi-and-install-pi-hole">Step 3: SSH to Raspberry Pi and Install Pi-hole</h2>

<p>You’ll first need the IP address of the Raspberry Pi. You can find this in your network settings or by running:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>arp <span class="nt">-a</span>
</code></pre></div></div>

<p>If you haven’t already, now is a good time to allocate a static IP address to the Raspberry Pi in your routers settings.</p>

<p>To connect to the Raspberry Pi, run:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh YOUR.PI.IP.ADDRESS
</code></pre></div></div>

<p>Here, substitute the IP addres you found for your Raspberry Pi. It will prompt you for the password you configured when you installed the operating system.</p>

<p>Either in your Pi’s terminal or your computer’s terminal connected to the Pi via SSH, run the following command from Pi-hole’s website to install it:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-sSL</span> https://install.pi-hole.net | bash
</code></pre></div></div>

<p>The installer will run and ask a bunch of questions. It’s okay to choose the defaults, but feel free to customize like I did.</p>

<p>When the installer finishes it’ll show you the password it selected. Note this and copy it, then change the web dashboard password by executing:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pihole <span class="nt">-a</span> <span class="nt">-p</span>
</code></pre></div></div>

<h2 id="step-5-configure-devices-or-router">Step 5: Configure Devices or Router</h2>

<p>Now that your Pi-hole is acting as a DNS server, we need to make sure that your devices use it instead of your ISP. You can do this on a per-device basis, but I’d recommend changing it at your router. That way, every time a device on your network makes a DNS request, it will first go through the Pi-hole.</p>

<p>This step is very specific to the brand of router, so I won’t go into detail. Essentially you need to tell your router to treat your Pi-hole as a DNS server, rather than your ISP. You should also reserve a static IP address for your Pi to ensure it’s IP does not change because this would break your setup.</p>

<p><strong>And Voila, enjoy your ad-free internet! 🙌</strong></p>

<h2 id="how-pi-hole-blocks-ads">How Pi-hole Blocks Ads</h2>

<p>Normal Ad-Blockers allow advertisements to load and just hide them from your view. Once you’ve gotten the Pi-hole up and running, advertisements will stop dead in their tracks, before they even begin to download.</p>

<p>Pi-hole is a very specific  <a href="https://en.wikipedia.org/wiki/DNS_sinkhole">DNS sinkhole</a>. DNS stands for Domain Name System, and it is the protocol for connecting names (like medium.com) to IP addresses, which is where websites/servers really live on the internet.</p>

<p>A DNS sinkhole acts in place of your usual DNS provider to provide false results. This pretty much breaks your internet — NEAT! With Pi-hole, everything that isn’t an Ad gets forwarded to your DNS provider,  <em>but</em> if the domain name is in Pi-hole’s blacklist (which is customizable for the nerdiest among you), then the Pi-hole sends back a fake IP address.</p>

<p>So you visit a website and it loads. Next, the website asks its ad provider for ads, and Pi-hole says “What ad provider? That doesn’t exist.” And boom your ads are gone. This can actually speed up your network, as you won’t be using as much bandwidth.</p>

<p>An interesting note is that Pi-hole won’t kill youtube ads, as youtube serves its content and advertisements from the same DNS, so killing youtube ads would also kill youtube.</p>]]></content><author><name>Jeff Morhous</name></author><category term="projects" /><summary type="html"><![CDATA[Run a DNS sinkhole on a Raspberry Pi to block ads, including the Raspberry Pi 5 (updated for 2024!)...]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jeffmorhous.com/images/raspberry-pi.webp" /><media:content medium="image" url="https://jeffmorhous.com/images/raspberry-pi.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Eloquent Ruby In 10 Minutes</title><link href="https://jeffmorhous.com/eloquent-ruby/" rel="alternate" type="text/html" title="Eloquent Ruby In 10 Minutes" /><published>2023-03-18T12:01:35+00:00</published><updated>2023-03-18T12:01:35+00:00</updated><id>https://jeffmorhous.com/eloquent-ruby</id><content type="html" xml:base="https://jeffmorhous.com/eloquent-ruby/"><![CDATA[<p>Over the last few month’s I’ve been diving much deeper into Ruby, going through the famous Eloquent Ruby book by Russ Olsen.</p>

<p>It’s been a crazy helpful book, about both rules and conventions.</p>

<p>Here’s a massive brain dump of of Ruby goodness</p>

<h2 id="writing-code-that-looks-like-ruby">Writing Code That Looks Like Ruby</h2>

<ul>
  <li>Variable names start with lowercase letter unless they start with <code class="language-plaintext highlighter-rouge">@</code></li>
  <li>Knowing the idioms and norms of a language is just as important as the syntax rules</li>
  <li>Good code tells the reader exactly what it is trying to do. Great code shouts its intent</li>
  <li>Good code is clear AND concise</li>
  <li>Ruby convention is 2 spaces for indentation (Sorry Tab People 🤷‍♂️)</li>
</ul>

<h2 id="comments">Comments</h2>

<ul>
  <li>Anything following as <code class="language-plaintext highlighter-rouge">#</code> is a comment</li>
  <li>Comments should just explain how to use a chunk of software, not whys or other hows</li>
  <li>Keep background comments separate from instruction</li>
  <li>Don’t fall into the trap of explaining lines of code that can/should speak for themselves</li>
</ul>

<h2 id="some-ruby-rules-and-conventions">Some Ruby Rules and Conventions</h2>

<ul>
  <li>Everything except classes should be snake_case</li>
  <li>Class names should be CamelCase</li>
  <li>Constants should be ALL_CAPS_WITH_UNDERSCORES</li>
  <li>Parentheses around methods aren’t technically required but most people do use them</li>
  <li>When it’s just a single argument, some people won’t put parentheses when calling an argument especially with puts or instance_of</li>
  <li>Control statements (ifs) don’t require parentheses, and we generally leave them out</li>
  <li>You can put more than one line on one line by separating with a semicolon, but you shouldn’t
Ruby has lots of things you CAN do but SHOULDN’T do</li>
  <li>If a code block has only a single statement, put it on a single line with { } otherwise use <code class="language-plaintext highlighter-rouge">do end</code> syntax</li>
  <li>Ruby doesn’t actually enforce constants. you can change them 💩</li>
  <li>methods should end in ? for things that return a binary</li>
  <li>methods should end in ! for things that could be dangerous</li>
  <li>There is no <code class="language-plaintext highlighter-rouge">do</code> after an <code class="language-plaintext highlighter-rouge">if</code>, just the <code class="language-plaintext highlighter-rouge">if</code>, the condition, the block, and the end</li>
  <li>Ruby lets us use <code class="language-plaintext highlighter-rouge">unless</code> to mean <code class="language-plaintext highlighter-rouge">if not</code></li>
  <li>If the body of a conditional is just one line, we can collapse the whole thing</li>
  <li>Both false and nil are treated as false, EVERYTHING ELSE is true</li>
</ul>

<h2 id="some-fun-code-samples">Some fun Code Samples</h2>

<p>You can create an array of strings the traditional way like this</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">words</span> <span class="o">=</span> <span class="p">[</span><span class="s1">'left'</span><span class="p">,</span> <span class="s1">'right'</span><span class="p">,</span> <span class="s1">'yellow'</span><span class="p">]</span>
</code></pre></div></div>

<p>Or in a fancy faster way like this</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">words</span> <span class="o">=</span> <span class="sx">%w{left right yellow}</span>
</code></pre></div></div>

<p>You can create a hash the traditional way like this</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">frequency</span> <span class="o">=</span> <span class="p">{</span><span class="s2">"left"</span> <span class="o">=&gt;</span> <span class="mi">1</span><span class="p">,</span> <span class="s2">"right"</span> <span class="o">=&gt;</span> <span class="mi">17</span><span class="p">,</span> <span class="s2">"yellow"</span>  <span class="o">=&gt;</span> <span class="mi">99</span><span class="p">}</span>
</code></pre></div></div>
<p>or in a fancy faster way like this only if your keys are symbols</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">frequency</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">left: </span><span class="mi">1</span><span class="p">,</span> <span class="ss">right: </span><span class="mi">17</span><span class="p">,</span> <span class="ss">yellow: </span><span class="mi">99</span> <span class="p">}</span>
</code></pre></div></div>

<p>If you want to make a parameter optional to provide, you can specify a default</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">load_font</span><span class="p">(</span> <span class="nb">name</span><span class="p">,</span> <span class="n">size</span> <span class="o">=</span> <span class="mi">12</span> <span class="p">)</span>
</code></pre></div></div>

<p>You can make a method take an arbitrary number of arguments by passing an astrisk before the argument name. Will create an array of values passed</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">print_all</span> <span class="p">(</span> <span class="o">*</span><span class="n">strings</span> <span class="p">)</span>
</code></pre></div></div>

<p>To run through a Ruby collection, you should use the <code class="language-plaintext highlighter-rouge">.each</code> method!</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>words.each {|word| puts word }
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">find_index</code> method for arrays returns the index of a condition being met</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">words</span><span class="p">.</span><span class="nf">find_index</span> <span class="p">{</span> <span class="o">|</span><span class="n">this_word</span><span class="o">|</span> <span class="n">word</span> <span class="o">==</span> <span class="n">this_word</span> <span class="p">}</span>
</code></pre></div></div>

<h3 id="strings-vs-symbols-in-ruby">Strings vs Symbols in Ruby</h3>
<ul>
  <li>Ruby lets you choose either double or single quotes for strings</li>
  <li>Strings are mutable in Ruby</li>
  <li>You must use double quotes if you’re doing string interpolation or newlines or tabs</li>
  <li>Strings are optimized for processing, symbols are optimized to stand for something</li>
  <li>There can only ever be one instance of a given symbol, unlike a string</li>
  <li>You can turn a symbol into a string with .to_s</li>
  <li>You can turn a string into a symbol with .to_sym</li>
  <li>Dynamic typing means types are checked on the fly as code is executed</li>
  <li>You can change the type of a variable, but you shouldn’t</li>
  <li>Dynamic typing allows more concise code</li>
</ul>

<h2 id="duck-typing-in-ruby">Duck Typing in Ruby</h2>

<p>Ruby assumes if an object has the right kind of methods, then it’s the right kind of object</p>

<p>This is called duck typing</p>

<p>If it walks like a duck, well, you know the saying</p>

<ul>
  <li>The payoff of dynamic typing is not automatic, eventually you don’t have to write base classes</li>
  <li>Ruby’s dynamic typing means you don’t declare the class of parameters, which lets your code be less coupled — you can change things easier</li>
  <li>This does increase cognitive load a bit as it may not be clear what you need to pass to a method</li>
  <li>Ruby is a language for grown ups that expects you to not shoot yourself in the foot like a child</li>
</ul>

<h2 id="tests">Tests</h2>

<ul>
  <li>Test early, test often, and test whenever you make a change</li>
  <li>The ruby style of programming insists that no class is finished without tests</li>
  <li>We have a built in Test::Unit we can use for simple one-off tests, but Rspec gives us much more functionality</li>
  <li>Ideal tests exercises exactly one test at a time</li>
  <li>A stub is an object that implements the same interface as a class that is needed for a test, but returns fake answers</li>
  <li>Mocks are like stubs, but fail the test when wrong methods are called or with wrong arguments</li>
  <li>Unit tests should be fast so they actually get run</li>
  <li>Unit tests should be independent of one another</li>
  <li>Unit tests should be capable of actually failing</li>
  <li>Most Ruby programmers favor really short methods that do exactly one thing</li>
  <li>This makes things more testable (yay!)</li>
  <li>The code should be articulate and explain itself to anyone maintaining it</li>
</ul>

<h2 id="what-is-the-composed-method-in-ruby">What is the Composed Method in Ruby?</h2>

<p>Ruby uses the composed method, where methods have 3 characteristics</p>

<ul>
  <li>They focus on solving a single aspect of a problem</li>
  <li>They operate at a single conceptual level (don’t mix logic)</li>
  <li>They have a name that reflects their purpose</li>
</ul>

<h2 id="overloading">Overloading</h2>

<ul>
  <li>You can overload operators in Ruby like *, - , +, / - this is different than other languages like C or Java, where they hide them</li>
  <li>Unless it’s overloaded <code class="language-plaintext highlighter-rouge">==</code> behaves the same way as .equal?</li>
</ul>

<h2 id="ruby-classes">Ruby Classes</h2>

<ul>
  <li>We build classes to represent groups of things, and have instances represent the things themselves</li>
  <li>Every Ruby object is an instance of some class</li>
  <li>Every class (except Object) has a superclass</li>
  <li>If you don’t specify, the superclass is <code class="language-plaintext highlighter-rouge">Object</code></li>
  <li>They act as containers for methods</li>
  <li>They are factories for making instances</li>
  <li>If doesn’t need an instance, it should just be a module</li>
  <li>Singletons are objects whose behavior is not completely controlled by a class</li>
  <li>A singleton method is a method that is defined for exactly one object instance</li>
  <li>Singleton methods override any regular class-defined methods</li>
  <li>Class methods are singleton methods in disguise</li>
  <li>Class variables start with two @s instead of 1, and they’re associated with the class rather than an instance of the class</li>
  <li>If the current class variable is not defined in the current class, Ruby looks to the parent (and so on)</li>
  <li>This can cause problems with child classes if you change the parent - it’s coupling</li>
  <li>The class instance variable, represented with a single <code class="language-plaintext highlighter-rouge">@</code>, is attached to a single object, an instance of the given class</li>
  <li>Ruby gives <code class="language-plaintext highlighter-rouge">:attr_accessor</code> methods you can use to have getter methods for a class instance variable</li>
  <li>Classes are containers for holding things AND factories that produce objects</li>
</ul>

<h2 id="ruby-modules-vs-classes">Ruby Modules vs Classes</h2>

<ul>
  <li>A Ruby module is the container part of a class without the factory</li>
  <li>You can put things in a module, like methods, constants, classes, and even other modules</li>
  <li>You can’t instantiate a module, there’s no factory 🙂</li>
  <li>You can use modules to include methods and then call those methods with <code class="language-plaintext highlighter-rouge">ModuleName.method_name</code></li>
  <li>Ruby classes are arranged in an inheritance tree, so a key part of constructing a new class is picking a superclass</li>
  <li>You can “mixin” modules into the inheritance tree of a class</li>
  <li>Mixins let you share common code among unrelated classes</li>
</ul>

<h2 id="conclusion">Conclusion</h2>

<p>There’s a LOT more to this book, especially metaprogramming
Honestly, that was a lot less interesting to me than what .I’ve talked about here, so I hope you can find it useful</p>]]></content><author><name>Jeff Morhous</name></author><category term="programming" /><category term="books" /><category term="ruby" /><category term="rails" /><summary type="html"><![CDATA[Over the last few month's I've been diving much deeper into Ruby, going through the famous Eloquent Ruby book...]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jeffmorhous.com/images/finding-rubys.webp" /><media:content medium="image" url="https://jeffmorhous.com/images/finding-rubys.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How to Make a belongs_to Relationship in Rails</title><link href="https://jeffmorhous.com/how-to-use-belongs-to-rails/" rel="alternate" type="text/html" title="How to Make a belongs_to Relationship in Rails" /><published>2023-01-22T12:01:35+00:00</published><updated>2023-01-22T12:01:35+00:00</updated><id>https://jeffmorhous.com/how-to-use-belongs-to-rails</id><content type="html" xml:base="https://jeffmorhous.com/how-to-use-belongs-to-rails/"><![CDATA[<h2 id="what-is-belongs_to-and-has_many-in-ruby-on-rails">What is belongs_to and has_many in Ruby on Rails</h2>

<p>ActiveRecord makes it really easy to tie models in a Rails app to other models.
In fact, you can even specify when you create a model what it should be associated
with. If you already have to models and you would like one model to belong to the other,
you’ll need to change both models and create a migration.</p>

<p>Let’s say you have a model, Tweet, and another model, User. A user can tweet many times,
and a tweet only belongs to one user. In ActiveRecord speak, User <code class="language-plaintext highlighter-rouge">has_many</code> tweets, and
a Tweet <code class="language-plaintext highlighter-rouge">belongs_to</code> a User. The database records are tied together, and you can build
features around the association easily.</p>

<h2 id="how-to-implement-belongs_to-and-has_many">How to Implement belongs_to and has_many</h2>

<p>Continuing on the User/Tweet example, in the Tweet class, simply add <code class="language-plaintext highlighter-rouge">belongs_to :user</code>.
The class should look like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Tweet</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
  <span class="n">belongs_to</span> <span class="ss">:user</span>
<span class="k">end</span>
</code></pre></div></div>

<p>In the User class, simply add <code class="language-plaintext highlighter-rouge">has_many :tweets</code>.
The class should look like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Tweet</span> <span class="o">&lt;</span> <span class="no">ApplicationRecord</span>
    <span class="n">has_many</span> <span class="ss">:tweets</span>
<span class="k">end</span>
</code></pre></div></div>

<p>This will tell rails to generate some methods you can use, but you still need to tie
the database records together. Create a new migration by running:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rails generate migration AddUserIdToTweets
</code></pre></div></div>

<p>In the migration file, make the change method look like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="k">def</span> <span class="nf">change</span>
    <span class="n">add_column</span> <span class="ss">:tweets</span><span class="p">,</span> <span class="ss">:user_id</span><span class="p">,</span> <span class="ss">:integer</span>
  <span class="k">end</span>
</code></pre></div></div>

<p>This adds a <code class="language-plaintext highlighter-rouge">user_id</code> column to the Tweets database table, which rails will use
to keep track of which user a tweet object belongs to.</p>

<h2 id="how-to-get-use-a-belong_to-and-has_many-relationship">How to Get Use a belong_to and has_many relationship</h2>

<p>You can use the association to do a number of things, including getting all
tweets that belong to a given user. The call is simple enough, and assuming
<code class="language-plaintext highlighter-rouge">current_user</code> refers to a User model, it looks like this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">current_user</span><span class="p">.</span><span class="nf">tweets</span>
</code></pre></div></div>

<p>To create a new Tweet that belong to a user, you can do this:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">current_user</span><span class="p">.</span><span class="nf">tweets</span><span class="p">.</span><span class="nf">build</span><span class="p">(</span><span class="n">tweet_params</span><span class="p">)</span>
</code></pre></div></div>

<p>Hope this was helpful!</p>]]></content><author><name>Jeff Morhous</name></author><category term="programming" /><category term="ruby" /><category term="rails" /><summary type="html"><![CDATA[ActiveRecord makes it really easy to tie models in a Rails app to...]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jeffmorhous.com/images/belongs-to.webp" /><media:content medium="image" url="https://jeffmorhous.com/images/belongs-to.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How to Fix Tailwind CSS Not Loading in Rails 7</title><link href="https://jeffmorhous.com/rails-7-tailwind-css-not-loading/" rel="alternate" type="text/html" title="How to Fix Tailwind CSS Not Loading in Rails 7" /><published>2023-01-17T12:01:35+00:00</published><updated>2023-01-17T12:01:35+00:00</updated><id>https://jeffmorhous.com/rails-7-tailwind-css-not-loading</id><content type="html" xml:base="https://jeffmorhous.com/rails-7-tailwind-css-not-loading/"><![CDATA[<p>I recently started a new Rails app. I knew right off the bat I wanted to use Tailwind,
and since I was using Rails 7, I specified that when I created the app with:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rails new app-name <span class="nt">--css</span> tailwind
</code></pre></div></div>

<p>Then I got to work making my views and adding styles. When I started the app with <code class="language-plaintext highlighter-rouge">rails s</code>
I noticed none of the styles loaded. You can quickly fix this by running the following before
you run <code class="language-plaintext highlighter-rouge">rails s</code></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>rails tailwindcss:build 
</code></pre></div></div>

<p>This is annoying though, because you’ll have to run it anytime you include a new class.
You can instead start your rails server with this command for hot reloading:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>bin/dev
</code></pre></div></div>

<p>Hope this helps!</p>]]></content><author><name>Jeff Morhous</name></author><category term="programming" /><category term="rails" /><summary type="html"><![CDATA[Have you started a rails app and your page has no styling even though you're using Tailwind classes...]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jeffmorhous.com/images/building-railroad.webp" /><media:content medium="image" url="https://jeffmorhous.com/images/building-railroad.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Thoughts on A Philosophy of Software Design</title><link href="https://jeffmorhous.com/thoughts-from-a-philosophy-of-software-design/" rel="alternate" type="text/html" title="Thoughts on A Philosophy of Software Design" /><published>2022-10-07T12:01:35+00:00</published><updated>2022-10-07T12:01:35+00:00</updated><id>https://jeffmorhous.com/thoughts-from-a-philosophy-of-software-design</id><content type="html" xml:base="https://jeffmorhous.com/thoughts-from-a-philosophy-of-software-design/"><![CDATA[<p>I recently finished reading “A Philosophy of Software Design” by John Ousterhout. It’s a brief but dense book full of the author’s opinions on designing software well.</p>

<p>Right from the beginning, John calls out that complexity is the enemy of well-designed software. Most of the book is spent discussing either why complexity is bad or ways to avoid it when writing code.</p>

<p>I really enjoyed this perspective and it’s already affecting how I’m writing code, so I thought I’d share a bit of what I found most impactful.</p>

<h2 id="what-is-complexity-in-software">What is Complexity in Software?</h2>

<p>For the purposes of the book and this discussion, <strong>complexity is something in the code that makes it harder to understand, maintain, or modify.</strong> To some extent, complexity is unavoidable.</p>

<p>For this reason, every decision around how our software is structured and written becomes a tradeoff.</p>

<p>Firstly complexity increases the cognitive load of working in a codebase. More complex apps require a developer to have (or find) more information to accomplish a task. Even further, complexity causes simple changes to be amplified into more complex changes. Anyone who has ever worked with microservices knows exactly how coupling can amplify a simple change into one that requires half a dozen pull requests.</p>

<p>Complexity makes it harder to make changes and introduces risk into those changes.</p>

<h2 id="tactical-vs-strategic-programming">Tactical vs Strategic Programming</h2>

<p>Obviously, complexity cannot always be avoided.</p>

<p>The author of this book introduces the concept of <strong>“tactical programming”</strong>, a style of writing code that focuses on finishing a task as fast as possible. Oftentimes this approach is taken by newer developers (myself included) or those trying to optimize for velocity in an agile approach (guilty as charged).</p>

<p>There’s a benefit to tactical programming. Businesses need to make money, and developers get paid to make that happen. Like I said, everything is a tradeoff. The author argues that developers should strive to do what he calls <strong>“strategic programming”</strong>, where the primary goal is to produce a good software design that also happens to work*.</p>

<p>Strategic programming can be justified in software projects as an upfront investment in the future development velocity of a project.</p>

<p>Tactical programming naturally results in more complex codebases that grow in complexity with exponential speed as simple changes continue to amplify into complex ones. Left unchecked, this results in a mountain of technical debt, with teams paralyzed by necessary refactors before any features can be built.</p>

<p>Conversely, the author argues strategic programming has the opposite effect on a project. Taking longer to implement things because of an investment in good design pays dividends, as all future changes are easier to make.</p>

<p>John has a strong opinion here and seems to insist that tactical programming is always bad. I personally think there are times where it makes sense to just get something done, though after reading this book the likelihood I would do that is greatly reduced.</p>

<h2 id="deep-software-modules">Deep Software Modules</h2>

<p><strong>Modular design</strong> puts emphasis on writing code so that only a fraction of a systems complexity must be dealt with at a given time, reducing cognitive load, and therefore complexity.</p>

<p>Breaking things into modules, classes, or even microservices is an incredibly common way to write software. What is unfortunately not as common, is breaking things into <em>deep modules</em>.</p>

<p>Modules that are deep have minimized dependencies on other modules. Modules that are deep abstract functionality away from other modules. Modules that are deep provide functionality while requiring little understanding or information from another module.</p>

<p>The author argues that we should design software with modules that have simple interfaces, but powerful functionality.</p>

<p>Specifically, the author pushes the idea that we should pull complexity downward into a module. When designing a method, it’s more important that the method have a simple interface than a simple implementation. This scales to classes or even services as a whole. Every piece of complexity in the interface of a module introduces coupling to anything that uses it.</p>

<h2 id="how-naming-in-software-affects-complexity">How Naming in Software Affects Complexity</h2>

<p>One really easy way to reduce complexity in code and improve readability is to <strong>use meaningful names</strong>. I’ve been guilty of using abstract variable names like <code class="language-plaintext highlighter-rouge">data</code> only to later annotate the variable with a comment like <code class="language-plaintext highlighter-rouge">// the location the user entered</code> to describe the usage.</p>

<p>It’s far more useful to just name the variable what it actually represents, as it makes the code far easier to understand. Easier to read means less complexity!</p>

<h2 id="conclusion">Conclusion</h2>

<p>This book contains a lot more detail (and a few more topics) that I didn’t get into here. These are the things that stood out the most to me as immediately useful in my day-to-day of writing code and squashing bugs. Still, you should go read it, no way you regret it 🙂</p>]]></content><author><name>Jeff Morhous</name></author><category term="programming" /><category term="books" /><category term="growth" /><summary type="html"><![CDATA[I recently read “A Philosophy of Software Design”, a brief but dense book of opinions on designing software well...]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jeffmorhous.com/images/software-design.webp" /><media:content medium="image" url="https://jeffmorhous.com/images/software-design.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Eliminating Code Complexity by Finding Double Negatives</title><link href="https://jeffmorhous.com/eliminating-code-complexity/" rel="alternate" type="text/html" title="Eliminating Code Complexity by Finding Double Negatives" /><published>2022-09-04T12:01:35+00:00</published><updated>2022-09-04T12:01:35+00:00</updated><id>https://jeffmorhous.com/eliminating-code-complexity</id><content type="html" xml:base="https://jeffmorhous.com/eliminating-code-complexity/"><![CDATA[<p>I have been reading “A Philosophy of Software Design,” and almost the entire book is spent
either explaining the dangers of complexity in a codebase, or eliminating complexity.</p>

<p>Code, especially Ruby code, should make its intent loud and clear to the reader. Clear
code is the opposite of complex code. It’s easy to understand its purpose, so it’s easier
and less risky to modify, extend, or remove.</p>

<h2 id="double-negatives">Double Negatives</h2>

<p>One thing that I haven’t seen explicitly discussed in this book is double negatives.</p>

<p>Whether it’s spoken or written, double negatives make sentences less clear.
Something like “There are not no eggs left at the store” is obviously poor grammar,
but it also makes the sentence harder to understand.</p>

<p>When our code reads like a sentence (well written Ruby should), double negatives
increase complexity. Things like <code class="language-plaintext highlighter-rouge">unless !some_condition</code> can be more clearly written
as <code class="language-plaintext highlighter-rouge">if some_condition</code>.</p>

<p>Most programmers will find writing in-line conditionals like this to be straightforward,
but it gets less intuitive when extracting this logic into methods.</p>

<h2 id="an-example">An example</h2>

<p>A bit ago, I was writing some ruby code with a friend. We wanted to take an action
if something was false, and like the good ruby programmers we are, we extracted
that complex conditional into its own short method.</p>

<p>We found ourselves going around and around trying to make this as clean as possible.
One of our first iterations contained a programming double negative - see if you
can find it:</p>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Method call</span>
<span class="k">return</span> <span class="p">[]</span> <span class="k">unless</span> <span class="n">items_found</span><span class="p">(</span><span class="n">users</span><span class="p">)</span>


<span class="c1"># Method implementation</span>
<span class="k">def</span> <span class="nf">items_found</span><span class="p">(</span><span class="n">users</span><span class="p">)</span>
  <span class="n">no_items_found</span> <span class="o">=</span> <span class="kp">false</span>
  <span class="n">users</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">volunteer</span><span class="o">|</span>
    <span class="k">if</span> <span class="n">volunteer</span><span class="p">.</span><span class="nf">items</span><span class="p">.</span><span class="nf">present?</span>
      <span class="n">no_items_found</span> <span class="o">=</span> <span class="kp">true</span>
    <span class="k">end</span>
  <span class="k">end</span>
  <span class="n">no_items_found</span> 
<span class="k">end</span>
</code></pre></div></div>

<h3 id="contrast-this-to-what-we-ended-up-with">Contrast this to what we ended up with</h3>

<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># Method call</span>
<span class="k">return</span> <span class="p">[]</span> <span class="k">if</span> <span class="n">no_items_found_for</span><span class="p">(</span><span class="n">users</span><span class="p">)</span>


<span class="c1"># Method implementation</span>
<span class="k">def</span> <span class="nf">no_items_found_for</span><span class="p">(</span><span class="n">users</span><span class="p">)</span>
  <span class="n">no_items_found</span> <span class="o">=</span> <span class="kp">true</span>
  <span class="n">users</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">volunteer</span><span class="o">|</span>
    <span class="k">if</span> <span class="n">volunteer</span><span class="p">.</span><span class="nf">items</span><span class="p">.</span><span class="nf">present?</span>
      <span class="n">no_items_found</span> <span class="o">=</span> <span class="kp">false</span>
    <span class="k">end</span>
  <span class="k">end</span>
  <span class="n">no_items_found</span> 
<span class="k">end</span>
</code></pre></div></div>

<p>It’s a pretty small change, but I think it highlights what the code actually
does much better than the first time around. I’m trying to be more intentional
about writing my code with these things in mind.</p>]]></content><author><name>Jeff Morhous</name></author><category term="programming" /><summary type="html"><![CDATA[Making code easier to maintain by writing it like we would in english...]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jeffmorhous.com/images/complexity.webp" /><media:content medium="image" url="https://jeffmorhous.com/images/complexity.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How I’m Learning Faster as a Software Engineer</title><link href="https://jeffmorhous.com/learning-faster-as-a-software-engineer/" rel="alternate" type="text/html" title="How I’m Learning Faster as a Software Engineer" /><published>2022-08-05T12:01:35+00:00</published><updated>2022-08-05T12:01:35+00:00</updated><id>https://jeffmorhous.com/learning-faster-as-a-software-engineer</id><content type="html" xml:base="https://jeffmorhous.com/learning-faster-as-a-software-engineer/"><![CDATA[<p>It’s been a long time since I’ve written anything on my own website, over a year actually.</p>

<p>It’s also been a busy year for me - I got a job as a Software Engineer and worked hard at it while finishing my degree,
I got my degree, and I got married. Writing for giggles on the internet took last place in my mind. Even so,
I have been writing!</p>

<h2 id="getting-paid-to-write-technical-articles">Getting Paid to Write Technical Articles</h2>

<p>I spent a lot of time in college sharing what I’ve learned about programming online. I accelerated this when I started contracting for companies doing iOS development. Everyday I was faced with task that was completely novel
to me. It was constant learning.</p>

<p>I got into the habit of writing “how-to” articles on technical things that I was learning. My words would serve as a “learning
exhaust” - a direct byproduct of the work I was doing. By forcing myself to condense what I was doing for
work into some words to be shared, I learn faster, understand deeper, and retain longer.</p>

<p>Eventually, this led to some paid work. My portfolio of learning exhaust showcased my ability to teach
technical concepts well. In the last year, all my writing has been on other companies’ sites for money. For the most part, I tried to propose and choose articles that
would allow me to explore and learn while I write. It’s been a wonderful way to continue to learn and grow quickly - I mean
getting paid to learn is the ultimate luxury.</p>

<p>I’ve written for Honeybadger and been featured many times in Ruby Weekly, a newsletter in my field with 40k readers. I’ve written
articles for Docker about Docker, specifically while I was learning Docker for my actual job! I’ve written articles about testing
for CircleCI and Linktree about software testing, a skill that I’m intentionally growing in for my actual job (again!). I have even
more articles in-progress and in the pipeline about topics relevant to areas I’m focusing my technical growth in.</p>

<h2 id="how-writing-helps-me-learn-new-things">How Writing Helps Me Learn New Things</h2>

<p>One of the best and also worst things about writing on the internet is that people read what you write. Some things I write get little traction,
others have been seen by tens (and occasionally hundreds) of thousands. This reveals a now-obvious problem with writing about things that
are on the edge of what I’m comfortable with - <strong>People are quick to tell me when I’m wrong.</strong></p>

<p>Yeah, it hurt when the first few people pointed out mistakes or nitpicked, but it’s actually been a multiplier for how fast I grow.</p>

<p><strong>Some of my best growth in the last year has followed this formula:</strong></p>

<ul>
  <li>I run into a technical problem in my job as a software engineer</li>
  <li>I learn about the preferred solution at work and at home</li>
  <li>I write about my understanding of the preferred solution, summarizing and explaining</li>
  <li>Random internet people point out any misunderstandings of the preferred solution</li>
  <li>Profit</li>
</ul>

<p>Putting myself out there still feels silly - even writing this is a little unnerving. Still, I’ve found this pattern to be incredibly
helpful in supercharging my growth. Learning in public is the fastest strategy for growing technical skills that I’ve found yet.</p>

<h2 id="growing-even-faster">Growing Even Faster</h2>

<p>I’ve been using Ruby on Rails for over a year now in my job, and I’m liking it more and more. Still, I feel like a novice. I work through
problems for my company and retain a good deal of what I learn, but I haven’t yet crossed the threshold where I saw I really <strong>know</strong> the
language or framework <strong>well</strong>.</p>

<p>Over the next year, I’m putting most of my professional energy into becoming incredibly comfortable with both Ruby and Rails. I’m going to
learn <strong>and write</strong> about Ruby, what makes it tick, what makes it great, and what makes it not so great. I’m going to learn <strong>and write</strong>
about Rails in the same manner. My goal is that by August of 2023 I have moved clearly beyond novice in this
niche, delivering more value in less time in my work.</p>

<p>This intentionality comes with a trade-off. I’m going to have to pass on some technical articles that would take up less time so that I can
use my extra intellectual energy outside of work to learn, even if it means missing out on the extra income. I’ll probably be writing more on
my website, but I’ll still be writing for places when the topics match up to my goals.</p>

<p>So here’s to the next year of intentional growth in my technical skills, and all the writing along the way!</p>]]></content><author><name>Jeff Morhous</name></author><category term="programming" /><category term="growth" /><category term="career" /><summary type="html"><![CDATA[What I've been up to, and how I'm growing as a software engineer...]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://jeffmorhous.com/images/growing-faster.webp" /><media:content medium="image" url="https://jeffmorhous.com/images/growing-faster.webp" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>