<?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: Yechiel Kalmenson</title>
    <description>The latest articles on DEV Community by Yechiel Kalmenson (@yechielk).</description>
    <link>https://dev.to/yechielk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F14816%2Fe88e23df-00dd-4d43-8454-65abd51780c8.jpg</url>
      <title>DEV Community: Yechiel Kalmenson</title>
      <link>https://dev.to/yechielk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yechielk"/>
    <language>en</language>
    <item>
      <title>The Friction Is A Feature, Not A Bug: Teaching and Mentoring in the Age of AI</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Wed, 22 Jul 2026 15:36:03 +0000</pubDate>
      <link>https://dev.to/yechielk/the-friction-is-a-feature-not-a-bug-teaching-and-mentoring-in-the-age-of-ai-23k9</link>
      <guid>https://dev.to/yechielk/the-friction-is-a-feature-not-a-bug-teaching-and-mentoring-in-the-age-of-ai-23k9</guid>
      <description>&lt;p&gt;Those who have been following me for a while will know that teaching and mentoring &lt;a href="https://blog.yechiel.me/tags/#mentorship" rel="noopener noreferrer"&gt;are a Big Deal™️&lt;/a&gt; to me.&lt;/p&gt;

&lt;p&gt;Before I got into tech I was a teacher, and still consider myself a teacher at heart. Being a teacher and mentor &lt;a href="https://blog.yechiel.me/are-you-a-10x-engineer-348aa74c82d5" rel="noopener noreferrer"&gt;was never separate&lt;/a&gt; in my eyes from being a good programmer and engineer; on the contrary, teaching was a tool that helped me become better at my craft at every stage of the journey.&lt;/p&gt;

&lt;p&gt;But in the last few years, and accelerating in the last few months, the landscape for teaching and learning has been changing at a scary pace. The advent of LLMs and "AI" coding assistants has drastically shifted how we acquire engineering skills in ways that we are definitely not prepared for.&lt;/p&gt;

&lt;p&gt;All of that has prompted many thoughts and conversations, and I hope to distill some of them in this blog post.&lt;/p&gt;

&lt;p&gt;In true Talmudic fashion, this post doesn't contain too many answers and will hopefully leave you with more questions than you started with. But asking the questions is how we start these conversations, and these conversations &lt;em&gt;need&lt;/em&gt; to be happening if we are to do right by the coming generation of programmers and engineers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't Spoon-Feed Me
&lt;/h3&gt;

&lt;p&gt;As a student, whether in Yeshiva when I used to spend hours each day poring over dense Talmudic legal debates and esoteric Chassidic philosophy or later while learning Rails and React at the Flatiron bootcamp, I quickly realized an uncomfortable truth about skill acquisition. &lt;/p&gt;

&lt;p&gt;My best, most profound learning never happened when a lesson went smoothly; it happened when I was painfully stuck, banging my head against a cryptic error message or wrestling with a concept that just wouldn't click (usually at 2 AM, fueled by cold coffee and sheer stubbornness).&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2yn9d76yrr80pnos5paq.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2yn9d76yrr80pnos5paq.png" alt="A programmer sitting in front of a computer with a thoughtful look on their face with a caption " width="480" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you strip away that struggle, you get rid of the growth. And when you get rid of the growth, the learning just doesn't happen.&lt;/p&gt;

&lt;p&gt;Even if you memorize just enough to pass the test, "easy come easy go." Without that cognitive friction, the knowledge evaporates the moment you close your editor.&lt;/p&gt;

&lt;p&gt;The best teachers and mentors I ever had understood this intuitively. When I was staring at a broken script knowing there was probably a stupidly easy fix, they didn’t just swoop in and drop the solution onto my screen (much to the vocal frustration of my sleep-deprived brain). &lt;/p&gt;

&lt;p&gt;Instead, they wielded tools like the Socratic method. They would point to a specific line of code and start asking gentle, probing questions to expose my flawed assumptions.&lt;/p&gt;

&lt;p&gt;They actively leaned into the friction to create just enough cognitive space for me to connect the dots on my own.&lt;/p&gt;

&lt;p&gt;They understood a fundamental rule of our teaching craft: that good mentorship is not about getting to the code. The goal wasn't just to get the tests turning green today; it was to build the mental resilience required to debug the &lt;em&gt;next&lt;/em&gt; inevitable failure tomorrow.&lt;/p&gt;

&lt;p&gt;And it was these skills that I took with. me when it was &lt;em&gt;my&lt;/em&gt; turn to become the teacher, whether it was teaching second graders at the Jewish Online School, as a TA at Flatiron, or when mentoring new teammates joining my team.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Yes-Man in Your IDE
&lt;/h3&gt;

&lt;p&gt;Fast forward to today, and our industry has largely outsourced that initial layer of mentorship to LLMs and coding assistants. And here is the uncomfortable truth: AI makes a terrible Socratic tutor.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbe0qus7mwk4on3qinvdd.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbe0qus7mwk4on3qinvdd.jpg" alt="Will Smith asks a robot " width="500" height="756"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’ve spent any serious time pairing (or even just chatting) with modern LLMs, you know they are pathological yes-men. Tell an AI you want to architect a distributed microservice mesh for a todo-list app with twelve active users, and it won't push back. It will chime in with "That is a brilliant insight!" and instantly spit out 400 lines of boilerplate.&lt;/p&gt;

&lt;p&gt;AI defaults to blind trust. It rarely challenges a mistaken assumption, and when you ask it why a stack trace is blowing up, it immediately serves up a copypastable fix instead of helping you understand the underlying bug.&lt;/p&gt;

&lt;p&gt;By acting like an algorithmic vending machine (&lt;code&gt;error_in =&amp;gt; fix_out&lt;/code&gt;), AI systematically removes all the cognitive friction from the development process. &lt;/p&gt;

&lt;p&gt;In our rush for 10x velocity, we forgot a foundational rule of skill acquisition: friction is a feature, not a bug. That was a load-bearing friction we removed!&lt;/p&gt;

&lt;p&gt;This isn't just philosophical speculation; there's hard data to back it up. A recent &lt;a href="https://www.anthropic.com/research/AI-assistance-coding-skills" rel="noopener noreferrer"&gt;study by Anthropic&lt;/a&gt; (or dive into the &lt;a href="https://arxiv.org/abs/2601.20245" rel="noopener noreferrer"&gt;raw paper here&lt;/a&gt; if you love parsing methodology) took 52 software engineers learning an unfamiliar programming library and split them into two groups: one with AI coding assistants, and one coding by hand. &lt;/p&gt;

&lt;p&gt;The overall results were as damning as they were not surprising. The engineers who delegated their coding to AI suffered a 17% drop in conceptual mastery and debugging skills (the equivalent of two full letter grades!) often without completing their tasks meaningfully faster than the manual control group. &lt;/p&gt;

&lt;p&gt;Why? Because when you offload the cognitive friction of getting painfully stuck, your brain never builds the deep neural pathways required to evaluate whether the generated code is actually any good.&lt;/p&gt;

&lt;p&gt;Now, to be fair, there &lt;em&gt;was&lt;/em&gt; some nuance in the data: a handful of the AI-assisted engineers actually outperformed the manual control group. But who were they? They were the developers who refused to trust the AI blindly. They treated the LLM like a study partner, asking probing follow-up questions and manually injecting their own friction back into the process. &lt;/p&gt;

&lt;p&gt;For everyone else, the automated yes-man just quietly destroyed their understanding.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Learner’s Dilemma: Manually Injecting Friction
&lt;/h3&gt;

&lt;p&gt;This dynamic leaves junior developers in a massive bind. &lt;/p&gt;

&lt;p&gt;Friends and mentees who are earlier in their career &lt;a href="https://madelinecaples.hashnode.dev/how-do-i-use-ai-in-my-learning" rel="noopener noreferrer"&gt;have complained&lt;/a&gt; that they feel stuck in a frustrating Catch-22: &lt;/p&gt;

&lt;p&gt;On one hand, they have to pragmatically accept the reality that AI isn't going anywhere. If an LLM can generate working boilerplate from a single prompt, you can't just stubborn your way out of using it. Ignoring AI entirely feels irresponsible, considering that you will be using these tools heavily in your day-to-day workflows.&lt;/p&gt;

&lt;p&gt;But on the other hand, if you rely on that autocomplete blindly, you get trapped in what's known as "superficial learning." You get a quick answer that allows you to pass a test suite or get a PR approved without truly understanding the mechanics under the hood. You miss out on the "deep learning" where the actual, career-sustaining growth happens.&lt;/p&gt;

&lt;p&gt;In my experience, that superficiality is a direct pipeline to developer anxiety and impostor syndrome. True professional confidence doesn't come from hitting Tab to accept a prompt; it comes from code ownership and intense intellectual toil and sweat.&lt;/p&gt;

&lt;p&gt;To survive this, diligent learners are now being forced to manually inject friction &lt;em&gt;back&lt;/em&gt; into their workflows just to keep their brains from turning off.&lt;/p&gt;

&lt;p&gt;A junior developer friend of mine &lt;a href="https://madelinecaples.hashnode.dev/learning-at-ruby-conf-a-workflow-for-ai-assisted-study" rel="noopener noreferrer"&gt;has resorted&lt;/a&gt; to explicitly prompting their IDE with guardrail instructions like: "I want to learn how to code it myself, rather than copying and pasting, so enable that however you can."&lt;/p&gt;

&lt;p&gt;They take notes by hand (yes, actual paper and pen!) and set physical timers just to recreate the safeguards of active listening that a tool designed for instant gratification systematically tries to bypass.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why We Need the Messy Web of Context
&lt;/h3&gt;

&lt;p&gt;This need for self-imposed struggle makes total sense when you look at Emily Bender and Alex Hanna’s book, &lt;a href="https://thecon.ai/" rel="noopener noreferrer"&gt;The AI Con&lt;/a&gt;, which highlights why friction in information access is actually a feature, not a bug.&lt;/p&gt;

&lt;p&gt;Think about what happens when you manually hunt down an obscure bug using a search engine.&lt;/p&gt;

&lt;p&gt;You read messy documentation, scan through twelve-comment GitHub issue threads (usually ending with a maintainer closing it as "not a bug" in 2014), weigh the credibility of conflicting answers on Stack Overflow, and stumble across related gotchas along the way.&lt;/p&gt;

&lt;p&gt;That messy web of meta-information is where real engineering context is built.&lt;/p&gt;

&lt;p&gt;An AI chatbot flattens all of that rich, frustrating struggle into a single, authoritative-sounding (and occasionally "hallucinated") summary. It gives you the answer to your immediate problem while robbing your brain of the surrounding ecosystem knowledge required to solve the next one.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Chavrusa Model: Pair Programming &amp;amp;&amp;amp; The Adversarial Rubber Duck
&lt;/h3&gt;

&lt;p&gt;This brings me to an ancient pedagogical model that survives to this day in Yeshivas (Talmudic academies) around the world, and one that software engineering desperately needs right about now.&lt;/p&gt;

&lt;p&gt;In a traditional Yeshiva, you rarely see people sitting in silent isolation reading a book or passively listening to a lecture. Instead, the room is loud, energetic, and filled with people arguing fiercely in pairs.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F00gkgpppvsugxqxve130.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F00gkgpppvsugxqxve130.jpg" alt="A group of rabbis arguing over a book of Talmud. " width="800" height="657"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This study partner system is known as a &lt;a href="https://en.wikipedia.org/wiki/Chavrusa" rel="noopener noreferrer"&gt;Chavrusa&lt;/a&gt; (from the Aramaic word for "friend" or "companion").&lt;/p&gt;

&lt;p&gt;When you learn with a Chavrusa, your partner isn't there to read the text to you, nor do they nod along and sycophantically affirm how brilliant your theories are. A real Chavrusa regularly challenges your logic, pokes holes in your arguments, pushees back against shaky assumptions, and generally refuses to let you get away with intellectual laziness.&lt;/p&gt;

&lt;p&gt;You don't master the Talmud by reading cliff notes or listening to a brilliant lecture (though there's some of that as well); you master it through &lt;em&gt;Yegiah&lt;/em&gt; (a Hebrew term that translates to intense intellectual toil and sweat).&lt;/p&gt;

&lt;p&gt;If you think about it (and as I mentioned in my &lt;a href="https://blog.yechiel.me/talmudic-gems-for-rails-developers-77f6cb98c9ad" rel="noopener noreferrer"&gt;2021 RailsConf talk&lt;/a&gt;), pair programming is just the tech industry's recreation of the Chavrusa.&lt;/p&gt;

&lt;p&gt;And here is the most important part of that system: it's not only the junior member of the pair who's learning in a true Chavrusa.&lt;/p&gt;

&lt;p&gt;It doesn't matter if you are pairing with a 10-year staff engineer or a junior developer who just finished a bootcamp; the learning is always a two-way street!&lt;/p&gt;

&lt;p&gt;When your partner acts as an adversarial rubber duck (one that actually talks back!), they force you to slow down, articulate your hidden assumptions, and defend your code logic from start to finish. Both of you gain from that friction, and both of you walk away with a significantly better mental model than you started with.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rabbi Ḥama, son of Rabbi Ḥanina, said: What is the meaning of that which is written: “Iron sharpens iron, so a man sharpens the countenance of his friend” &lt;em&gt;(Proverbs 27:17)&lt;/em&gt;? This verse comes to tell you that just as with iron blades, one sharpens the other when they are rubbed against each other, so too, when Torah scholars study together, they sharpen one another.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;-Ta'anit 7a&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx9yu5aqc7z6msjizts48.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fx9yu5aqc7z6msjizts48.jpg" alt="Close up of two blades n a sword fight hitting against each other. " width="640" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our rush for 10x velocity, we tried to replace human pair programming with AI copilots. But in doing so, we forgot a critical dependency: we exchanged the adversarial Chavrusa and Yegiah for a sycophantic robo-duck.&lt;/p&gt;

&lt;h3&gt;
  
  
  Empathetic Balancing Act
&lt;/h3&gt;

&lt;p&gt;A common response I hear from the tech crowd is: "Well, let's just fix this with better system prompts! Let's build guardrails that instruct the AI to act like a Socratic tutor instead of just giving away the answer!"&lt;/p&gt;

&lt;p&gt;There are plenty of tools attempting this right now, but I am deeply skeptical that technical guardrails can solve a fundamentally human problem.&lt;/p&gt;

&lt;p&gt;First of all, relying on a prompt to withhold answers requires elite learner discipline. Some students can stick to it, but that level of willpower is rare, especially when your cognitive battery is hitting empty.&lt;/p&gt;

&lt;p&gt;When a junior developer is staring at a cryptic &lt;code&gt;NullPointerException&lt;/code&gt; at 2 AM and their brain is fried, a prompt asking them to "reflect on the lifecycle of the object" is getting bypassed immediately. If all it takes is typing &lt;code&gt;/solution&lt;/code&gt; or opening a new browser tab to get the dopamine hit of copypastable code, not too many people I know will power through.&lt;/p&gt;

&lt;p&gt;More importantly, LLMs are fundamentally architected to please. Even if you prompt-engineer them to be tough Socratic tutors, their core tuning means that the moment a user expresses genuine frustration, the AI folds like a cheap lawn chair and hands over the syntax anyway.&lt;/p&gt;

&lt;p&gt;Why? Because an LLM lacks the quality most important in an educator: &lt;em&gt;empathy&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Mentorship is a human pedagogical balancing act. A great mentor understands the fine line between productive struggle and demoralizing suffering. A good educator can read the room. They notice the heavy sighs, the mounting panic, and the sheer exhaustion.&lt;/p&gt;

&lt;p&gt;Sure, as a student I learned best when I was given the tools to figure things out on my own. But sometimes I maxed out my energy on the stupid error message, and all of the leading questions my tutors were asking me felt like they were just causing me to circle and circle around the one insight that would make it all work, an insight my brain just couldn't reach.&lt;/p&gt;

&lt;p&gt;That is the point a good teacher knows to step back, smile, say "Hey, JavaScript is just weird sometimes," and hand the exact line of code I needed to get unstuck so I could finally go get some sleep.&lt;/p&gt;

&lt;p&gt;An algorithm cannot read a learner's emotional state. It cannot dynamically toggle between a challenging sparring partner and a supportive safety net. Code can never replicate that relationship.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flezpcfwb451et5a5anww.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flezpcfwb451et5a5anww.jpg" alt="A teacher and students engaged in a collaborative learning activity in a classroom. The teacher is writing something in the student's notebook. " width="640" height="960"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Unsolved Catch-22
&lt;/h3&gt;

&lt;p&gt;So where does this leave us? If I’m being entirely honest, I have way more questions than answers right now (not exactly what you want to hear from a tech blog, I know!).&lt;/p&gt;

&lt;p&gt;We are sitting on a massive, industry-wide Catch-22:&lt;/p&gt;

&lt;p&gt;On one hand, it would be borderline professional malpractice to forbid junior developers from using AI. It is the modern IDE (which, back when I was learning to code, gatekeepers argued juniors shouldn't use either—I disagreed then and I disagree now). If juniors will be expected to use these tools on the job, it hardly makes sense not to train them to use them effectively.&lt;/p&gt;

&lt;p&gt;On the other hand, the engineers getting that massive 10x velocity boost without introducing architecture-destroying bugs are the seniors who spent years writing code &lt;em&gt;without&lt;/em&gt; it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We&lt;/em&gt; know when an LLM is spinning its wheels on a false premise because &lt;em&gt;we&lt;/em&gt; have a built-in BS-meter, hard-earned through years of manual debugging.&lt;/p&gt;

&lt;p&gt;How does someone without that pre-AI experience build that intuition today? They won't get it from a sycophantic yes-man chat-bot assistant. And manually inventing your own cognitive friction requires a level of self-discipline that is hard to maintain on a tight deadline.&lt;/p&gt;

&lt;p&gt;Maybe the future of onboarding relies on doubling down on human pair programming—having juniors shadow seniors for months on end to soak up that pre-AI intuition by proxy. After all, pair programming has &lt;em&gt;always&lt;/em&gt; been one of the best onboarding methods, even before Claude. But not all companies are interested in the investment that full-time pairing requires in an industry often obsessed with shipping yesterday.&lt;/p&gt;

&lt;p&gt;What I do know is that we cannot afford to ignore the problem or assume that better LLM models will magically fix our talent pipeline.&lt;/p&gt;

&lt;p&gt;If we just hand junior developers an oracle and fail to preserve the empathetic, rigorous struggle of the Chavrusa, we are going to wake up in a few years to a terrifying system error: an industry with a massive, AI-generated codebase to maintain, and not enough senior engineers left who know how to maintain it.&lt;/p&gt;

&lt;p&gt;What are you and your teams doing to keep productive friction alive? I'd love to hear your thoughts.&lt;/p&gt;

</description>
      <category>mentorship</category>
      <category>ai</category>
      <category>learning</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Who's Going To RubyConf 2026?</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Wed, 10 Jun 2026 00:08:27 +0000</pubDate>
      <link>https://dev.to/yechielk/whos-going-to-rubyconf-2026-4j1a</link>
      <guid>https://dev.to/yechielk/whos-going-to-rubyconf-2026-4j1a</guid>
      <description>&lt;p&gt;RubyConf holds a special place in my heart. It was the very first tech conference I attended after receiving a scholarship fresh out of Flatiron School back in 2017 (you can read about my experience &lt;a href="https://blog.yechiel.me/rubyconf-is-nice-dab165842db5" rel="noopener noreferrer"&gt;here&lt;/a&gt;), and then in 2021, it was the stage for my &lt;a href="https://youtu.be/iuVPObgoxF8?si=kQODALd49BQvH3wl" rel="noopener noreferrer"&gt;first conference talk&lt;/a&gt; in Denver.&lt;/p&gt;

&lt;p&gt;Now, in another first, I joined the Program Committee for &lt;a href="https://rubyconf.org/" rel="noopener noreferrer"&gt;RubyConf 2026&lt;/a&gt; to help put the program together, and what a program it is! We have an absolutely amazing lineup this year, and I'm so excited to see it come to life!&lt;/p&gt;

&lt;p&gt;Who else is planning on attending? Let's make plans to meet up and say hi!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>networking</category>
      <category>community</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Shipping Your Machine: Building a Container in 50 Lines of Code (Part 2)</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Tue, 26 May 2026 15:03:07 +0000</pubDate>
      <link>https://dev.to/yechielk/shipping-your-machine-building-a-container-in-50-lines-of-code-part-2-4cm4</link>
      <guid>https://dev.to/yechielk/shipping-your-machine-building-a-container-in-50-lines-of-code-part-2-4cm4</guid>
      <description>&lt;h2&gt;
  
  
  Welcome Back to the Jailhouse
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/yechielk/shipping-your-machine-building-a-container-in-60-lines-of-code-part-1-14ma"&gt;Part 1 of this series&lt;/a&gt;, we built the foundation of our container using Go. We successfully used the &lt;code&gt;CLONE_NEWUTS&lt;/code&gt; namespace and process forking to isolate our container's hostname from the host machine. &lt;/p&gt;

&lt;p&gt;But we still have a massive security flaw. Right now, if we drop into our container's bash shell, we can still see all of the host's files. We could easily &lt;code&gt;cd&lt;/code&gt; straight out of our "isolated" environment and mess with the host machine. &lt;/p&gt;

&lt;p&gt;Let's lock it down.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;chroot&lt;/code&gt; to Jail
&lt;/h3&gt;

&lt;p&gt;Linux has a wonderful system call called &lt;code&gt;chroot&lt;/code&gt; (short for "change root"). It lets us change the root directory (&lt;code&gt;/&lt;/code&gt;) for a given process. As far as the process is concerned, the directory we point &lt;code&gt;chroot&lt;/code&gt; to &lt;em&gt;is the entire universe&lt;/em&gt;. Anything outside of it simply doesn't exist.&lt;/p&gt;

&lt;p&gt;Let's update our &lt;code&gt;child()&lt;/code&gt; function to set the root directory to our current working directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running in new child process %v &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sethostname&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="c"&gt;// Get current directory and lock the process inside it&lt;/span&gt;
    &lt;span class="n"&gt;pwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getwd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chroot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pwd&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c"&gt;// chroot changes the root, but doesn't automatically move us there. &lt;/span&gt;
    &lt;span class="c"&gt;// We must explicitly change our working directory to the new root!&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;sudo go run main.go run /bin/bash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Crash!&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;panic: fork/exec /bin/bash: no such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happened?&lt;/p&gt;

&lt;p&gt;We just told our process that our current directory is the entire universe. So, when we ask &lt;code&gt;exec.Command&lt;/code&gt; to run &lt;code&gt;/bin/bash&lt;/code&gt;, it isn't looking at your computer's actual hard drive anymore. It is looking inside your project folder for a directory called &lt;code&gt;bin&lt;/code&gt; containing an executable called &lt;code&gt;bash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Because our current directory doesn't have those, it fails! We need an actual root filesystem to provide the basic binaries our shell expects.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Note: Production container runtimes like runC actually use a more advanced system call called &lt;code&gt;pivot_root&lt;/code&gt; for better security, but &lt;code&gt;chroot&lt;/code&gt; is perfect for understanding the core concept!).&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Image
&lt;/h3&gt;

&lt;p&gt;To fix this, we need to provide an actual root filesystem that contains the basic folders and binaries (like /bin/bash) that our shell expects.&lt;/p&gt;

&lt;p&gt;You can grab a basic Ubuntu root filesystem yourself using Docker. Open a new terminal tab and run these exact commands in your project directory:&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;# Use docker to start a Ubuntu container and then export its filesystem to a compressed file called ubuntu.tar&lt;/span&gt;
docker &lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;docker create ubuntu&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ubuntu.tar

&lt;span class="c"&gt;# Create a directory called ubuntu-rootfs and unzip your tar file into it&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;ubuntu-rootfs
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xf&lt;/span&gt; ubuntu.tar &lt;span class="nt"&gt;-C&lt;/span&gt; ubuntu-rootfs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a folder called &lt;code&gt;ubuntu-rootfs&lt;/code&gt; containing a complete, brand-new Ubuntu file system.&lt;/p&gt;

&lt;p&gt;Assuming you have that folder in your project directory, let's change our &lt;code&gt;chroot&lt;/code&gt; call to point to it as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chroot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ubuntu-rootfs"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when we run &lt;code&gt;sudo go run main.go run /bin/bash&lt;/code&gt;, everything works perfectly! &lt;/p&gt;

&lt;p&gt;You can run &lt;code&gt;ls /&lt;/code&gt; and you will only see the files inside your &lt;code&gt;ubuntu-rootfs&lt;/code&gt; directory. Try running &lt;code&gt;cd ..&lt;/code&gt; to escape, and you will find yourself in the exact same directory as before. You cannot access the host machine at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  PIDs and /proc
&lt;/h3&gt;

&lt;p&gt;We're ready for the next step. If you remember, when we ran &lt;code&gt;docker run -it ubuntu /bin/bash&lt;/code&gt; back in the beginning of Part 1, one of the ways we could tell we were in an isolated container was by running &lt;code&gt;ps aux&lt;/code&gt; and observing only two processes running with very low PIDs.&lt;/p&gt;

&lt;p&gt;Let's try to replicate that. While inside our new container, try running &lt;code&gt;ps aux&lt;/code&gt; to view the running processes.&lt;/p&gt;

&lt;p&gt;It breaks with an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Error, &lt;span class="k"&gt;do &lt;/span&gt;this: mount &lt;span class="nt"&gt;-t&lt;/span&gt; proc proc /proc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ps&lt;/code&gt; command works by reading the &lt;code&gt;/proc&lt;/code&gt; directory, which is a special virtual filesystem in Linux that contains live data about running processes. Our isolated root filesystem has an empty &lt;code&gt;/proc&lt;/code&gt; folder, and the operating system hasn't been told to attach the live process data to it. Because it's empty, &lt;code&gt;ps&lt;/code&gt; fails!&lt;/p&gt;

&lt;p&gt;To fix this, we need to do two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Give our container its own isolated Process IDs (PIDs) using namespaces.&lt;/li&gt;
&lt;li&gt;Mount the &lt;code&gt;proc&lt;/code&gt; filesystem so commands like &lt;code&gt;ps&lt;/code&gt; can read it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First, update the &lt;code&gt;SysProcAttr&lt;/code&gt; in the &lt;code&gt;run()&lt;/code&gt; function to include the PID and Mount namespaces. (Note: &lt;code&gt;CLONE_NEWNS&lt;/code&gt; stands for "New Namespace", but it specifically refers to the Mount namespace! It just happens to be the first namespace added to the Linux kernel and back then no one thought they might end up needing more so they just called it "namespace" 🤷).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysProcAttr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysProcAttr&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Cloneflags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWUTS&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWPID&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWNS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, mount the &lt;code&gt;proc&lt;/code&gt; directory inside our &lt;code&gt;child()&lt;/code&gt; function, right after we &lt;code&gt;chroot&lt;/code&gt;. We will also use Go's &lt;code&gt;defer&lt;/code&gt; keyword to ensure we unmount it and clean up after ourselves when the function exits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chroot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"ubuntu-rootfs"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Chdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;// Mount the proc filesystem&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"proc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"proc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"proc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="c"&gt;// Clean up after ourselves when the function exits&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Unmount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"proc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, run your container and type &lt;code&gt;ps aux&lt;/code&gt;. You'll see only three processes running: &lt;code&gt;exe&lt;/code&gt; (our Go program) running as PID 1, &lt;code&gt;bash&lt;/code&gt; running as PID 2, and the &lt;code&gt;ps&lt;/code&gt; command we just ran!&lt;/p&gt;

&lt;h3&gt;
  
  
  Cgroups (Keeping it Civil)
&lt;/h3&gt;

&lt;p&gt;We have our invisibility cloak (Namespaces) and our isolated universe (&lt;code&gt;chroot&lt;/code&gt;). But what happens if we write an infinite &lt;code&gt;while&lt;/code&gt; loop inside our container that eats up all the CPU and memory?&lt;/p&gt;

&lt;p&gt;It would completely crash the host machine!&lt;/p&gt;

&lt;p&gt;To prevent our container from using up all of our resources, Linux uses &lt;strong&gt;cgroups&lt;/strong&gt; (Control Groups). Cgroups act as the bouncer, ensuring no single container uses more than its fair share of resources.&lt;/p&gt;

&lt;p&gt;To set up a cgroup, we can lean on a famous Linux philosophy: "Everything is a file." This means we can configure the kernel's resource limits by creating specific directories and writing text into special files.&lt;/p&gt;

&lt;p&gt;Let's add a quick helper function to our &lt;code&gt;main.go&lt;/code&gt; file to limit the maximum number of processes our container is allowed to spawn to 20:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;cg&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cgroups&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="s"&gt;"/sys/fs/cgroup/"&lt;/span&gt;
    &lt;span class="n"&gt;pids&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cgroups&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pids"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// 1. Create a new cgroup for our container&lt;/span&gt;
    &lt;span class="n"&gt;containerCgroup&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pids&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"my-container"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Mkdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerCgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;0755&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// 2. Write the limit into the cgroup file (max 20 processes)&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerCgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"pids.max"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"20"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="m"&gt;0700&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c"&gt;// 3. Add our current process to this cgroup&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filepath&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;containerCgroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"cgroup.procs"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strconv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Itoa&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Getpid&lt;/span&gt;&lt;span class="p"&gt;())),&lt;/span&gt; &lt;span class="m"&gt;0700&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down what this function is doing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Create the group:&lt;/strong&gt; By making a new directory inside &lt;code&gt;/sys/fs/cgroup/pids&lt;/code&gt;, the Linux kernel automatically creates a new Control Group for us.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set the rule:&lt;/strong&gt; Inside that new directory, Linux automatically generates a file called &lt;code&gt;pids.max&lt;/code&gt;. We open that file and write the text &lt;code&gt;"20"&lt;/code&gt; into it. This establishes a rule that our process will only be allowed to run 20 sub-processes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Enforce the rule:&lt;/strong&gt; Linux also generates a file called &lt;code&gt;cgroup.procs&lt;/code&gt;. We get our Go program's current Process ID (&lt;code&gt;os.Getpid()&lt;/code&gt;) and write it into this file. This tells the kernel, &lt;em&gt;"Hey, apply the rules of this folder to me!"&lt;/em&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Finally, let's call this function inside our &lt;code&gt;run()&lt;/code&gt; function, right before we execute our child process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running %v &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"child"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/proc/self/exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;

    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysProcAttr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysProcAttr&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Cloneflags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWUTS&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWPID&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWNS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Set up our resource limits!&lt;/span&gt;
    &lt;span class="n"&gt;cg&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And just like that, our container is officially resource-limited! Because cgroups inherit down to child processes, everything that runs inside our container is bound by this rule. If a malicious script inside tries to execute a "fork-bomb" (a script that endlessly copies itself to freeze the computer), the kernel will step in and aggressively kill it the second it hits 20 processes.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Reveal
&lt;/h3&gt;

&lt;p&gt;If you put all of this together, we just built Docker from scratch in about 50 lines of code.&lt;/p&gt;

&lt;p&gt;Containers aren't magic. They aren't heavyweight VMs. They are simply standard Linux processes wrapped in namespaces, jailed in a specific directory, and policed by cgroups.&lt;/p&gt;

&lt;p&gt;In fact, you don't even need Go to do this. You can trigger the exact same isolation using a single line of bash:&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;sudo &lt;/span&gt;unshare &lt;span class="nt"&gt;--uts&lt;/span&gt; &lt;span class="nt"&gt;--pid&lt;/span&gt; &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nt"&gt;--fork&lt;/span&gt; &lt;span class="nt"&gt;--root&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/ubuntu-rootfs &lt;span class="nt"&gt;--mount-proc&lt;/span&gt; /bin/bash

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there you have it! The next time someone says "it works on my machine," you know exactly what it takes to ship their machine to production.&lt;/p&gt;

</description>
      <category>go</category>
      <category>containers</category>
      <category>beginners</category>
      <category>docker</category>
    </item>
    <item>
      <title>Shipping Your Machine: Building a Container in 50 Lines of Code (Part 1)</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Wed, 20 May 2026 20:09:10 +0000</pubDate>
      <link>https://dev.to/yechielk/shipping-your-machine-building-a-container-in-60-lines-of-code-part-1-14ma</link>
      <guid>https://dev.to/yechielk/shipping-your-machine-building-a-container-in-60-lines-of-code-part-1-14ma</guid>
      <description>&lt;h2&gt;
  
  
  The "Works on My Machine" Problem
&lt;/h2&gt;

&lt;p&gt;We've all been there. You spend days writing a new feature. You test it locally, everything passes, you push it to production, and... &lt;em&gt;boom&lt;/em&gt;. It crashes immediately. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;"But it works on my machine!"&lt;/em&gt; you cry out to your lead engineer.&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%2Ftug3iyan6anpg59tdml8.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftug3iyan6anpg59tdml8.webp" alt="A crying child tells an adult sitting next to him " width="422" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that is exactly what containers are. They are a way to bundle up your application and the environment it runs in—your machine—so that it behaves exactly the same way in production as it does on your laptop. &lt;/p&gt;

&lt;p&gt;But what &lt;em&gt;is&lt;/em&gt; a container, really?&lt;/p&gt;

&lt;p&gt;Most people have an intuition of containers as some kind of complicated application that runs on your computer and simulates another computer. That is the image I had in my head even after working on containers for a few months at Pivotal/VMware... &lt;strong&gt;and it's a myth.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The reality is a lot simpler, and a lot more interesting!&lt;/p&gt;

&lt;p&gt;The truth is that a container is simply a directory on your computer—like any other directory—with a process running inside of it. What makes it a container and not just another process running inside a directory is that we use some clever built-in Linux features to trick the process into thinking that this directory &lt;strong&gt;is the entire computer&lt;/strong&gt;. As far as this process is concerned, nothing exists outside of the directory we "trapped" it in and that directory is the entirety of its universe. &lt;/p&gt;

&lt;p&gt;In this two-part series, we are going to demystify this illusion by building Docker from scratch in exactly 60 lines of Go code. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;em&gt;Because containers rely heavily on the Linux Kernel, this tutorial will only run natively on a Linux machine. If you are following along on a Mac, you'll need to spin up a Linux VM first, as Macs run on the Darwin kernel and don't have these system calls!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting the Stage
&lt;/h3&gt;

&lt;p&gt;We'll be writing our container in Go. Why Go? Because it gives us incredibly clean access to underlying Linux system calls, which we'll need to create our container illusion. This is the reason Docker, Kubernetes, and other cloud-native projects are all written in Go.&lt;/p&gt;

&lt;p&gt;Let's try to replicate the core behavior of Docker.&lt;/p&gt;

&lt;p&gt;Normally, when using Docker, you run a command like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; ubuntu /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run that (assuming you have docker installed) you will see that you have been dropped into a new shell. This shell looks different than your original shell and you can tell it's completely isolated from the rest of your machine (the host) in a few ways (it's worth opening a new tab in your terminal so you can see these changes side by side):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Your prompt:&lt;/strong&gt; Everyone's prompt is different, but most have something in the beginning that looks like &lt;code&gt;[username]@[hostname]&lt;/code&gt;. The prompt you see now probably looks like &lt;code&gt;root@[some-random-string]&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Your hostname:&lt;/strong&gt; If you type &lt;code&gt;hostname&lt;/code&gt; in your host computer's terminal, it will output your computer's actual name. If you run &lt;code&gt;hostname&lt;/code&gt; inside your container, on the other hand, you will see that same random sequence of characters from your prompt. Docker assigned this fake hostname to your container at random.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Your File System:&lt;/strong&gt; If you type &lt;code&gt;ls /&lt;/code&gt; inside your container you will see that none of the files from your computer's actual root directory are there, instead you will see a fresh list of files and directories, exactly like you would find in a brand new Ubuntu installation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Your processes:&lt;/strong&gt; If you type &lt;code&gt;ps aux&lt;/code&gt; in your container you will find only 2 processes with very low Process IDs (PIDs)—typically PID 1 for the shell process you're in and another low number PID for the &lt;code&gt;ps&lt;/code&gt; command you just ran. Meanwhile, running &lt;code&gt;ps aux&lt;/code&gt; on your host machine will show a massive list of running processes, many of them with very high PIDs.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We will try to replicate this behavior from scratch, with a few minor differences.&lt;/p&gt;

&lt;p&gt;Our goal is to run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go run /bin/bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if we do everything right the behavior will be mostly the same. We will drop into a new shell where:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We will see a different hostname than our existing one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The root directory we will have access to will not be the root directory of our computer, instead it will be a new root directory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If we inspect the processes running using &lt;code&gt;ps&lt;/code&gt; we will see only the processes running in our program and not all the processes on our computer.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So let's start!&lt;/p&gt;

&lt;h3&gt;
  
  
  Show Me the Code
&lt;/h3&gt;

&lt;p&gt;Let's build this step by step. Create a new directory on your Linux computer, make a file named main.go, and let's add our initial boilerplate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// os.Args is a list of everything typed in the terminal.&lt;/span&gt;
    &lt;span class="c"&gt;// [0] is the program name (main.go), [1] is our command ("run")&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"run"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid argument"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// os.Args[2:] takes everything AFTER the "run" command&lt;/span&gt;
    &lt;span class="c"&gt;// e.g., ["echo", "hello", "world"]&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running %v &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Go, &lt;code&gt;os.Args&lt;/code&gt; captures every word you type into the terminal as a list (a slice) of strings. We are telling our program to look at the second word (&lt;code&gt;os.Args[1]&lt;/code&gt;). If it says "run", we trigger our &lt;code&gt;run()&lt;/code&gt; function which, for now does nothing but print all our arguments to the terminal.&lt;/p&gt;

&lt;p&gt;If we run this in our terminal:&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="nv"&gt;$ &lt;/span&gt;go run main.go run &lt;span class="nb"&gt;echo &lt;/span&gt;hello world
Running &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;hello world]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome. It successfully reads our command. But it's just printing text; it's not actually executing the command yet.&lt;/p&gt;

&lt;p&gt;To make it execute the command, we need to wire up Go's &lt;code&gt;os/exec&lt;/code&gt; package (a package for executing commands on our computer). Let's update our file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"os/exec"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"run"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid argument"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running %v &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

        &lt;span class="c"&gt;// 1. Define the command we want to execute. This includes the third element in our array &lt;/span&gt;
    &lt;span class="c"&gt;// (the actual command, in our case "echo"), as well as any optional arguments we may &lt;/span&gt;
    &lt;span class="c"&gt;// want to pass to it ("hello world").&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c"&gt;// 2. Wire up the plumbing&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;

    &lt;span class="c"&gt;// 3. Run the command and handle any errors&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// A tiny helper function to catch errors and crash the program &lt;/span&gt;
&lt;span class="c"&gt;// cleanly if something goes wrong.&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's look at what we just added to &lt;code&gt;run()&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Define the command:&lt;/strong&gt; &lt;code&gt;exec.Command&lt;/code&gt; takes the program we want to run (like &lt;code&gt;echo&lt;/code&gt;) and any arguments we want to pass to it (&lt;code&gt;hello world&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Wire up the plumbing:&lt;/strong&gt; This part is crucial. By default, when a program spins up a new process, it runs invisibly in the background. By pointing the new command's Standard Input, Output, and Error to our own &lt;code&gt;os.Stdin&lt;/code&gt;, &lt;code&gt;os.Stdout&lt;/code&gt;, and &lt;code&gt;os.Stderr&lt;/code&gt;, we are attaching its "mouth and ears" directly to our terminal so we can actually interact with it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Run it:&lt;/strong&gt; We execute the command. Because Go requires explicit error handling, we wrapped it in a tiny &lt;code&gt;must()&lt;/code&gt; helper function to keep our code readable.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's test it out:&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="nv"&gt;$ &lt;/span&gt;go run main.go run &lt;span class="nb"&gt;echo &lt;/span&gt;hello world
Running &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;hello world]
hello world
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We actually get &lt;code&gt;hello world&lt;/code&gt; echoed back at us by the system!&lt;/p&gt;

&lt;p&gt;Even better, we can tell our program to drop us into an interactive shell:&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="nv"&gt;$ &lt;/span&gt;go run main.go run /bin/bash
Running &lt;span class="o"&gt;[&lt;/span&gt;/bin/bash]
root@your-computer:/home/yechiel/docker-clone# 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this shell is absolutely not a container yet. If you type &lt;code&gt;hostname&lt;/code&gt;, you'll see your host machine's actual name. If you type &lt;code&gt;ls /&lt;/code&gt;, you can see all your host files. Right now, we have just written a fancy Go wrapper around a regular bash process.&lt;/p&gt;

&lt;p&gt;It is time to start building the walls of our container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing Namespaces (The Invisibility Cloak)
&lt;/h3&gt;

&lt;p&gt;To isolate our process from the rest of the computer, we need to put it inside a &lt;strong&gt;Namespace&lt;/strong&gt;. A namespace is like an invisibility cloak that hides the rest of the computer from our process.&lt;/p&gt;

&lt;p&gt;There are different namespaces that isolate different aspects of the system. Let's start with the UTS namespace, which isolates the hostname.&lt;/p&gt;

&lt;p&gt;To create a new namespace, we pass a specific "clone flag" to the system call spinning up our process. Update your &lt;code&gt;run()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running %v &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;

    &lt;span class="c"&gt;// Set up the namespace!&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysProcAttr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysProcAttr&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Cloneflags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWUTS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you try to run the command now, you'll get a "permission denied" error. Because changing a namespace requires modifying kernel-level structures, you now need root privileges.&lt;/p&gt;

&lt;p&gt;Instead, let's run &lt;code&gt;sudo go run main.go run /bin/bash&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you run &lt;code&gt;hostname&lt;/code&gt; now you will still see your computer's actual hostname. This is because, by default, Linux has your new process inherit the hostname of the host computer.&lt;/p&gt;

&lt;p&gt;However, if you manually run &lt;code&gt;hostname container&lt;/code&gt; inside this new bash shell, and &lt;em&gt;then&lt;/em&gt; type &lt;code&gt;hostname&lt;/code&gt;, you'll see your hostname is now &lt;code&gt;container&lt;/code&gt;. But if you switch to a terminal tab on your host machine and type &lt;code&gt;hostname&lt;/code&gt;, you'll see your actual computer's name hasn't changed at all! Our process is successfully isolated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forking the Process
&lt;/h3&gt;

&lt;p&gt;Did you notice something weird when you manually changed the hostname in the last step? Even though typing &lt;code&gt;hostname&lt;/code&gt; printed out &lt;code&gt;container&lt;/code&gt;, your actual bash prompt likely still said &lt;code&gt;root@your-computer#&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;This happens because the bash shell reads the machine's hostname &lt;em&gt;when it first starts up.&lt;/em&gt; Changing the hostname after the shell is already running doesn't dynamically update your prompt. If we want our container to feel like a truly isolated environment from the second it boots, we need our Go program to change the hostname automatically &lt;em&gt;before&lt;/em&gt; we execute /bin/bash.&lt;/p&gt;

&lt;p&gt;Luckily, Go has a function to change the hostname: &lt;code&gt;syscall.Sethostname()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But here we run into a "chicken and egg" problem: where do we call that function?&lt;/p&gt;

&lt;p&gt;If we call it before &lt;code&gt;cmd.Run()&lt;/code&gt;, it executes on our host machine and changes our actual computer's name (bad!). If we call it after &lt;code&gt;cmd.Run()&lt;/code&gt;, it'll only run after our bash shell has already exited (still bad!).&lt;/p&gt;

&lt;p&gt;We need a middleman. We need our Go program to cross over into the new namespace, set the hostname, and &lt;em&gt;then&lt;/em&gt; execute the &lt;code&gt;/bin/bash&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;To do this, we are going to have our Go program run &lt;strong&gt;itself&lt;/strong&gt; again as a child process.&lt;/p&gt;

&lt;p&gt;Let's update our &lt;code&gt;main.go&lt;/code&gt; file. We will update the &lt;code&gt;main()&lt;/code&gt; and &lt;code&gt;run()&lt;/code&gt; functions, and add a brand new &lt;code&gt;child()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"os"&lt;/span&gt;
    &lt;span class="s"&gt;"os/exec"&lt;/span&gt;
    &lt;span class="s"&gt;"syscall"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"run"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s"&gt;"child"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c"&gt;// We added a new command here!&lt;/span&gt;
    &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid argument"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running %v &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c"&gt;// /proc/self/exe is a special Linux file that points to the program &lt;/span&gt;
    &lt;span class="c"&gt;// currently running. So, our program is calling itself!&lt;/span&gt;
    &lt;span class="c"&gt;// We pass "child" as the first argument, followed by the rest of our commands.&lt;/span&gt;
    &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"child"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/proc/self/exe"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;

    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysProcAttr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SysProcAttr&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Cloneflags&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CLONE_NEWUTS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;child&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running in new child process %v &lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="c"&gt;// We are now inside the namespace! It is safe to change the hostname.&lt;/span&gt;
    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;syscall&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sethostname&lt;/span&gt;&lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;

    &lt;span class="c"&gt;// Now we run the actual command the user requested (like /bin/bash)&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdout&lt;/span&gt;
    &lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stderr&lt;/span&gt;

    &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;must&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down the magic happening in &lt;code&gt;exec.Command("/proc/self/exe", args...)&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;"/proc/self/exe"&lt;/code&gt;: In Linux, &lt;code&gt;/proc&lt;/code&gt; is a special directory that holds information about all running processes. If you check what's in there (by running &lt;code&gt;ls /proc&lt;/code&gt;) you will see a whole bunch of directories that have numbers as names. Each number is a PID and that directory contains information about the process with that PID. &lt;code&gt;/proc/self/exe&lt;/code&gt; is essentially a shortcut to the directory for the process that's currently running (in our case &lt;code&gt;go run main.go&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;args...&lt;/code&gt;: We are taking the word &lt;code&gt;"child"&lt;/code&gt; and appending the rest of the user's commands (like &lt;code&gt;/bin/bash&lt;/code&gt;) to it. The &lt;code&gt;...&lt;/code&gt; syntax in Go simply "unpacks" the list so it can be passed as individual arguments.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, when you type &lt;code&gt;go run main.go run /bin/bash&lt;/code&gt;, here is what happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The program starts, sees &lt;code&gt;"run"&lt;/code&gt;, and triggers the &lt;code&gt;run()&lt;/code&gt; function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;code&gt;run()&lt;/code&gt; function sets up the invisibility cloak (the &lt;code&gt;NEWUTS&lt;/code&gt; namespace).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Inside that cloak, it runs itself again, but this time passing the command &lt;code&gt;"child"&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The program starts a second time, sees &lt;code&gt;"child"&lt;/code&gt;, and triggers the &lt;code&gt;child()&lt;/code&gt; function.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Because we are now safely inside the namespace, it changes the hostname to &lt;code&gt;container&lt;/code&gt; and finally runs &lt;code&gt;/bin/bash&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's test it. Run &lt;code&gt;sudo go run main.go run /bin/bash&lt;/code&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="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;go run main.go run /bin/bash
Running &lt;span class="o"&gt;[&lt;/span&gt;/bin/bash]
Running &lt;span class="k"&gt;in &lt;/span&gt;new child process &lt;span class="o"&gt;[&lt;/span&gt;/bin/bash]
root@container:/home/yechiel/docker-clone# 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at that prompt! We've successfully isolated the hostname programmatically before bash loaded. You'll see both print statements execute, and if you type &lt;code&gt;hostname&lt;/code&gt; in the new shell, it will say &lt;code&gt;container&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We are still not in a proper container, though. If you run &lt;code&gt;ls /&lt;/code&gt;, you will see all the files on your host machine, and you can even use &lt;code&gt;cd ..&lt;/code&gt; to "escape" the directory entirely. Worse, if you were running this in a shared cloud environment, you would theoretically be able to see everyone else's files and running processes—a massive security risk! We need a way to lock our process down so it can't escape its designated environment.&lt;/p&gt;

&lt;p&gt;In Part 2, we will tackle the final pieces of the container puzzle: jailing the file system, isolating the Process IDs (PIDs) so our container can only see itself, and stopping infinite loops from crashing the host computer using &lt;code&gt;cgroups&lt;/code&gt;. Stay tuned!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>go</category>
      <category>containers</category>
      <category>linux</category>
    </item>
    <item>
      <title>Who's Going To RubyConf?</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Fri, 05 Nov 2021 19:00:05 +0000</pubDate>
      <link>https://dev.to/yechielk/whos-going-to-rubyconf-3g9m</link>
      <guid>https://dev.to/yechielk/whos-going-to-rubyconf-3g9m</guid>
      <description>&lt;p&gt;&lt;a href="https://rubyconf.org/" rel="noopener noreferrer"&gt;RubyConf&lt;/a&gt; is starting next week (November 8-0), both in person in Denver and remotely online.&lt;/p&gt;

&lt;p&gt;This year it's especially exciting for me; it's my first in-person conference in exactly two years (the vaccine mandates and mask policy helped me feel safe with the decision to attend in person), AND I'll be giving a talk (which you should totally &lt;a href="https://rubyconf.org/program/sessions#session-1164" rel="noopener noreferrer"&gt;come watch&lt;/a&gt; if you'll be there)!&lt;/p&gt;

&lt;p&gt;Who else is planning on attending?&lt;/p&gt;

&lt;p&gt;Come say hi (either in person or in the discord)!&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>networking</category>
      <category>watercooler</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Talmudic Gems For Rails Developers</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Thu, 20 May 2021 01:40:59 +0000</pubDate>
      <link>https://dev.to/yechielk/talmudic-gems-for-rails-developers-35b3</link>
      <guid>https://dev.to/yechielk/talmudic-gems-for-rails-developers-35b3</guid>
      <description>&lt;p&gt;A few weeks ago, I gave a talk at RailsConf titled Talmudic Gems For Rails Developers.&lt;/p&gt;

&lt;p&gt;In the talk I discussed lessons I learned from a lifetime of Talmudic study that helped me in my journey as a developer, and which I felt could benefit other developers in their growth.&lt;/p&gt;

&lt;p&gt;The talk is geared at any developer looking to grow using the timeless wisdom of the Talmudic sages, not just Rails developers.&lt;/p&gt;

&lt;p&gt;I shared a transcript of the talk in the Torah &amp;amp;&amp;amp; Tech newsletter &lt;a href="https://mailchi.mp/2bc93e8bd182/torah-tech-issue-6751637?e=bb66b91b8e" rel="noopener noreferrer"&gt;issue #123&lt;/a&gt; (if you haven’t yet, you can sign up to the newsletter, and get the first year’s worth of newsletters in book format, at the Torah &amp;amp;&amp;amp; Tech website: &lt;a href="https://torahandtech.dev/" rel="noopener noreferrer"&gt;torahandtech.dev&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The talk has just been posted to YouTube, and I’m happy to share it with my audience!&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/d08GFQDT824"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If you would like to delve into some of the sources I mentioned, you can find links in the source sheet I prepared &lt;a href="https://gist.github.com/achasveachas/000e4e08e193140e372cd4819d025f4a" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Happy Learning!&lt;/p&gt;




</description>
      <category>programming</category>
      <category>rails</category>
      <category>mentoring</category>
      <category>community</category>
    </item>
    <item>
      <title>Part 3: Add A Dead-Man's Switch To A Rails Application</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Sun, 07 Feb 2021 18:47:58 +0000</pubDate>
      <link>https://dev.to/yechielk/part-2-add-a-dead-man-s-switch-to-a-rails-application-2b5f</link>
      <guid>https://dev.to/yechielk/part-2-add-a-dead-man-s-switch-to-a-rails-application-2b5f</guid>
      <description>&lt;h2&gt;
  
  
  Bonus Round!
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/yechielk/part-1-adding-a-dead-man-s-switch-to-a-rails-application-bgp"&gt;part one&lt;/a&gt; and &lt;a href="https://dev.to/yechielk/part-2-add-a-dead-man-s-switch-to-a-rails-application-243j"&gt;part two&lt;/a&gt; of this series, we created a working deadman's switch in our rails app.&lt;/p&gt;

&lt;p&gt;This part isn't necessary, but it can be helpful.&lt;/p&gt;

&lt;p&gt;The deadman's switch we wrote will trip if it isn't reset after seven days (or however long you set it for). That may seem like a safe interval, but it would be nice to have a reminder if you haven't reset it in a few days and the deadline is coming up.&lt;/p&gt;

&lt;p&gt;I set my switch up to send me an SMS reminder if I didn't reset it in 4 days, and then every day after that until the switch is either rest or if it trips.&lt;/p&gt;

&lt;p&gt;That's what we will do in part three of this tutorial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; this part of the tutorial uses Twilio to send SMS messages. You will need to sign up for a Twilio account. If you use my &lt;a href="//www.twilio.com/referral/PWhqJW"&gt;referral link&lt;/a&gt; to sign up, you will get $10 of credit, which should be last a while for our use-case.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;The first thing we need to do before getting started is to make sure we have a Twilio account. For our purposes, a "demo" account is enough. A demo account is free; it means you will have to register your phone number to receive texts, and you can't send SMS messages to other numbers, which is fine. Sending an SMS costs money ($0.0075 in the US); the $10 you get at sign-up by using my referral link should cover that for a long time.&lt;/p&gt;

&lt;p&gt;After creating an account, follow the instructions to create a new project.&lt;/p&gt;

&lt;p&gt;On the dashboard, you will need three pieces of information: The Account SID, Auth Token, and the account phone number.&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%2Fi%2F0owkb1kk5ldug027e0oy.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%2Fi%2F0owkb1kk5ldug027e0oy.png" alt="screenshot of the dashboard showing where to find the needed info. " width="788" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Save these credentials in a safe place; we will need them for the next part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; It's a bad idea to put API keys in your code as plaintext, especially if you put your code somewhere that's publicly accessible like GitHub. Someone can find them and use them to rack up quite the phone bill! Store them as environment variables instead. Heroku makes it easy to set &lt;a href="https://devcenter.heroku.com/articles/config-vars#using-the-heroku-dashboard" rel="noopener noreferrer"&gt;environment variables&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Text me, maybe?
&lt;/h2&gt;

&lt;p&gt;Now we have everything we need to set up our script to text us when it doesn't hear from us in a while.&lt;/p&gt;

&lt;p&gt;First, put the following near the top of your rake task:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActionView&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Helpers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DateHelper&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't strictly necessary, but it will let us use &lt;code&gt;ActionView&lt;/code&gt;'s &lt;code&gt;time_ago_in_words&lt;/code&gt; method to format a nice human-readable message.&lt;/p&gt;

&lt;p&gt;Next, let's set up a conditional to send the message only after the specified time that the app didn't hear from you. I set mine up to start bugging me after three days:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ago&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="no"&gt;Deadman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_reset&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(though for testing purposes, you can make the interval as small as a few seconds so you can trigger the SMS to send and make sure everything is working. Just remember to change it back before pushing to production).&lt;/p&gt;

&lt;p&gt;Now, inside the &lt;code&gt;if&lt;/code&gt; block, the first thing to do is to construct a message that the script will send:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;time_to_trigger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time_ago_in_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Deadman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_reset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;message_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Your deadman switch will trigger in &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;time_to_trigger&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, please log in to your account and reset it."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can, of course, edit this to fit your needs and taste.&lt;/p&gt;

&lt;p&gt;Next, let's set up a Twilio client using the credentials we got from the Twilio dashboard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;twilio_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Twilio&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;REST&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TWILIO_ACCOUNT_SID'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TWILIO_ACCOUNT_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, you can  use the client to send an SMS message to your phone:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;twilio_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;from: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TWILIO_PHONE_NUMBER'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'PHONE_NUMBER'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;body: &lt;/span&gt;&lt;span class="n"&gt;message_body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;from:&lt;/code&gt; field is the Twilio phone number you got from your Twilio dashboard, and the &lt;code&gt;to:&lt;/code&gt; field is your personal phone number (make sure to register it with your Twilio account if you're on the free "Demo" tier). The &lt;code&gt;body:&lt;/code&gt; field is the message we constructed earlier.&lt;/p&gt;

&lt;p&gt;The complete code should look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;ActionView&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Helpers&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DateHelper&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ago&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="no"&gt;Deadman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_reset&lt;/span&gt;
    &lt;span class="n"&gt;time_to_trigger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time_ago_in_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Deadman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_reset&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;days&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;message_body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Your deadman switch will trigger in &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;time_to_trigger&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, please log in to your account and reset it."&lt;/span&gt;
    &lt;span class="n"&gt;twilio_client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Twilio&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;REST&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TWILIO_ACCOUNT_SID'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TWILIO_ACCOUNT_TOKEN'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;twilio_client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;from: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'TWILIO_PHONE_NUMBER'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'PHONE_NUMBER'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="ss"&gt;body: &lt;/span&gt;&lt;span class="n"&gt;message_body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! That's all there is to it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;I considered adding the capability of resetting my switch via SMS as well, by replying to one of the reminders.&lt;/p&gt;

&lt;p&gt;I didn't end up going with that for various reasons, but if you would like to, here is a &lt;a href="https://www.twilio.com/blog/2018/04/sms-notifications-ruby-on-rails.html" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; I wrote a while ago on the Twilio blog that should help get you started.&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>twilio</category>
    </item>
    <item>
      <title>My Favorite Bash Tips, Tricks, and Shortcuts</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Mon, 11 Jan 2021 22:23:39 +0000</pubDate>
      <link>https://dev.to/yechielk/my-favorite-bash-tips-tricks-and-shortcuts-36bj</link>
      <guid>https://dev.to/yechielk/my-favorite-bash-tips-tricks-and-shortcuts-36bj</guid>
      <description>&lt;p&gt;If you're on DEV, chances are you spend at least &lt;strong&gt;some&lt;/strong&gt; time in the terminal, maybe even a &lt;strong&gt;lot&lt;/strong&gt; of time.&lt;/p&gt;

&lt;p&gt;Over the years, I've picked up a number of tips and tricks from fellow developers. Almost every time I pair program with someone new, chances are I'll notice them doing something neat and ask them how they did it.&lt;/p&gt;

&lt;p&gt;Here are some of my favorites.&lt;/p&gt;

&lt;p&gt;I use bash as my default terminal, but most of these tips translate to other terminals as well.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; This post isn't meant to teach the basics of using the terminal. There are many great resources online (I remember doing &lt;a href="https://www.codecademy.com/learn/learn-the-command-line" rel="noopener noreferrer"&gt;Codecademy's Command Line course&lt;/a&gt; when I was starting out.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;-&lt;/code&gt; operator
&lt;/h2&gt;

&lt;p&gt;Do you find yourself switching back and forth between two directories often?&lt;/p&gt;

&lt;p&gt;You can use &lt;code&gt;cd -&lt;/code&gt; to change to the last directory you were in like this:&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="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;directory1
~/directory1 &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;directory2
~/directory2 &lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; -
~/directory1 &lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This also works with git when switching between branches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/my-project&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout feature-branch
~/my-project&lt;span class="o"&gt;(&lt;/span&gt;feature-branch&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git checkout -
~/my-project&lt;span class="o"&gt;(&lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The &lt;code&gt;!!&lt;/code&gt; operator
&lt;/h2&gt;

&lt;p&gt;This happens a lot!&lt;/p&gt;

&lt;p&gt;You type a command, only to get a "Permission denied" so you have to retype the command again, this time using &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;!!&lt;/code&gt; operator echoes the last command you typed into your terminal.&lt;/p&gt;

&lt;p&gt;You can use it like this:&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="nv"&gt;$ &lt;/span&gt;some-dangerous-script.sh
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Error: Permission Denied
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; Enter password &lt;span class="k"&gt;for &lt;/span&gt;some-dangerous-script.sh: 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  {curly brace expansion}
&lt;/h2&gt;

&lt;p&gt;If you ever need to run a series of very similar commands that differ by just a few characters (like for example, if you want to create a few filenames with lightly different extensions) you can use the characters that will be different between two curly braces and the command will run once for each one.&lt;/p&gt;

&lt;p&gt;Like this:&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="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;file-&lt;span class="o"&gt;{&lt;/span&gt;1,2,3&lt;span class="o"&gt;}&lt;/span&gt;.md
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; file-1.md file-2.md file-3.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also pass in a range:&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="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;file-&lt;span class="o"&gt;{&lt;/span&gt;1..3&lt;span class="o"&gt;}&lt;/span&gt;.md
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; file-1.md file-2.md file-3.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Search using Ctrl+R
&lt;/h2&gt;

&lt;p&gt;Are you like me? Would you press the up button 20 times to avoid typing out a 7 character command?&lt;/p&gt;

&lt;p&gt;This next one was a lifesaver for me!&lt;/p&gt;

&lt;p&gt;You can type Ctrl + R followed by the first few letters of the command you want to search through your bash history and bring up the command you need.&lt;/p&gt;

&lt;p&gt;(Sorry, I can't think of how to demonstrate that with a code snippet. Just go to your terminal, type in Ctrl + R and start typing).&lt;/p&gt;

&lt;h2&gt;
  
  
  Aliases
&lt;/h2&gt;

&lt;p&gt;Aliases are a great way to save time and keystrokes. If there's a command or a series of commands you find yourself typing often, it's making an alias can be very helpful.&lt;/p&gt;

&lt;p&gt;In order to set aliases, first open the &lt;code&gt;~/.bashrc&lt;/code&gt; file in your favorite editor and check if it has the following lines in it:&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;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.bash_aliases &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;.&lt;/span&gt; ~/.bash_aliases
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should be there already, if it isn't just add it to the bottom of the file.&lt;/p&gt;

&lt;p&gt;Next open &lt;code&gt;~/.bash_aliases&lt;/code&gt; in your editor (or create it if it doesn't exist) and add your aliases in the following format:&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;alias &lt;/span&gt;&lt;span class="nv"&gt;something&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"definition"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some playful aliases I have in my &lt;code&gt;.bash_aliases&lt;/code&gt; are:&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;alias &lt;/span&gt;&lt;span class="nv"&gt;please&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sudo "&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;yeet&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"rm -rf 
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also have a number of functions defined there, for more complex command series:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mk&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

gclone&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    git clone &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="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&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; .git&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;mk&lt;/code&gt; alias takes a directory name as an argument, &lt;code&gt;mk&lt;/code&gt;s the directory and then &lt;code&gt;cd&lt;/code&gt;s into it.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;gclone&lt;/code&gt; alias takes a git repo, clones it, and then &lt;code&gt;cd&lt;/code&gt;s into it.&lt;/p&gt;

&lt;p&gt;After adding aliases to your &lt;code&gt;.bash_aliases&lt;/code&gt; they should load automatically every time you start a new terminal session.&lt;/p&gt;

&lt;p&gt;If you would like to use your aliases in your current session, run:&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;source&lt;/span&gt; ~/.bash_aliases
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's what I can think of for now.&lt;/p&gt;

&lt;p&gt;Do you have any favorite tips and tricks?&lt;/p&gt;

&lt;p&gt;Please please do share them! I always love learning new ones!&lt;/p&gt;

</description>
      <category>linux</category>
      <category>bash</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>What Are Your Goals For 2021?</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Thu, 31 Dec 2020 15:17:28 +0000</pubDate>
      <link>https://dev.to/yechielk/what-are-your-goals-for-2021-2fck</link>
      <guid>https://dev.to/yechielk/what-are-your-goals-for-2021-2fck</guid>
      <description>&lt;p&gt;It's that time of year when I usually look back at the goals I set for myself a year ago, see how well I did, and set new expectations for the next year.&lt;/p&gt;

&lt;p&gt;This past year of course has been... a year. On December 31st 2019, I don't think any of us imagined where we'd be now, yet here we are.&lt;/p&gt;

&lt;p&gt;Looking back at my goals from last year, one thing is clear. I did not necessarily accomplish everything I set out to do, but I also had quite a few accomplishments I would never have imagined in the beginning of the year!&lt;/p&gt;

&lt;p&gt;Let's first take a look at what I was hoping for:&lt;/p&gt;


&lt;div class="ltag__comment crayons-card my-2 p-0 overflow-hidden"&gt;
    &lt;a href="https://dev.to/yechielk/what-ar-your-goals-for-2020-2oj5" class="flex items-center gap-2 p-3 fs-s color-base-60 hover:color-base-90"&gt;
      

      &lt;span&gt;Comment on &lt;strong class="fw-medium color-base-90"&gt;What Are Your Goals For 2020?&lt;/strong&gt;&lt;/span&gt;
    &lt;/a&gt;
  &lt;div class="p-4"&gt;
    &lt;div class="flex items-center gap-2 mb-3"&gt;
      &lt;a href="/yechielk" class="crayons-avatar crayons-avatar--l"&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%2Fuser%2Fprofile_image%2F14816%2Fe88e23df-00dd-4d43-8454-65abd51780c8.jpg" alt="yechielk" class="crayons-avatar__image" width="800" height="800"&gt;
      &lt;/a&gt;
      &lt;div&gt;
        &lt;a href="/yechielk" class="crayons-link fw-medium"&gt;Yechiel Kalmenson&lt;/a&gt;
        &lt;span class="fs-xs color-base-60 ml-1"&gt;Dec 27 '19&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="text-styles"&gt;
      

&lt;p&gt;I'll start!&lt;/p&gt;

&lt;p&gt;In tech goals, 2020 is the year of Kubernetes for me. I plan on getting more familiar with the eco-system, and maybe even start contributing to the project!&lt;/p&gt;

&lt;p&gt;In career goals this is not an aggressive goal for me, but if I can find a job at a company that has all the pros of where I work now, but in addition to that is a remote-first company I'll be very happy :)&lt;/p&gt;

&lt;p&gt;And in personal goals, I'm taking up Krav Maga and organizing a course at my synagogue for next year.&lt;/p&gt;



    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Of those three goals: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Kubernetes: turns out my career took a bit of a turn, I joined a new team where the focus was on a different direction, and I did not end up learning as much as I hoped (never mind getting involved in the community). I still hope to get more into that eco-system, but that's taken a place on a back burner for now.&lt;/li&gt;
&lt;li&gt;Finding a remote job: in an ironic twist of fate, my current job is now remote (along with many others), and my company communicated that they will be more open to people working from home permanently, even as offices begin to open. So I'll count that as a goal I fulfilled 🙂&lt;/li&gt;
&lt;li&gt;Krav Maga: I did start this one. I put together a group in my neighborhood and we met in my house a few times, but then covid put a halt to that so we'll see when that becomes practical again.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the other hand, there have been a number of accomplishments this year that I would not have imagined a year ago!&lt;/p&gt;

&lt;p&gt;My friend Ben Greenberg and I, published the &lt;a href="https://torahandtech.dev/" rel="noopener noreferrer"&gt;Torah &amp;amp;&amp;amp; Tech&lt;/a&gt; newsletter for two years now.&lt;/p&gt;

&lt;p&gt;In June, we published the first year's worth of newsletters in the Torah &amp;amp;&amp;amp; Tech book:&lt;/p&gt;


&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/yechielk/announcing-torah-tech-the-book-4m39" class="crayons-story__hidden-navigation-link"&gt;Announcing Torah &amp;amp;&amp;amp; Tech; The Book.&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/yechielk" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F14816%2Fe88e23df-00dd-4d43-8454-65abd51780c8.jpg" alt="yechielk profile" class="crayons-avatar__image" width="800" height="800"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/yechielk" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Yechiel Kalmenson
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Yechiel Kalmenson
                
              
              &lt;div id="story-author-preview-content-339279" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/yechielk" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F14816%2Fe88e23df-00dd-4d43-8454-65abd51780c8.jpg" class="crayons-avatar__image" alt="" width="800" height="800"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Yechiel Kalmenson&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/yechielk/announcing-torah-tech-the-book-4m39" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 19 '20&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/yechielk/announcing-torah-tech-the-book-4m39" id="article-link-339279"&gt;
          Announcing Torah &amp;amp;&amp;amp; Tech; The Book.
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/showdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;showdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/books"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;books&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/learning"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;learning&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/coding"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;coding&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/yechielk/announcing-torah-tech-the-book-4m39" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;27&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/yechielk/announcing-torah-tech-the-book-4m39#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              4&lt;span class="hidden s:inline"&gt;&amp;nbsp;comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


&lt;p&gt;In personal news, also in June, my family grew as we welcomed our son Meir to the family.&lt;/p&gt;

&lt;p&gt;So yes, 2020 has been a year. Here's hoping that 2021 only improves.&lt;/p&gt;

&lt;p&gt;What about you? What were your 2020 goals? How did you do on them? What are you hoping to accomplish in 2021?&lt;/p&gt;

&lt;p&gt;Looking forward to hearing from you in the comments!&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>watercooler</category>
      <category>goals</category>
    </item>
    <item>
      <title>Part 2: Add A Dead-Man's Switch To A Rails Application</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Fri, 18 Dec 2020 04:57:34 +0000</pubDate>
      <link>https://dev.to/yechielk/part-2-add-a-dead-man-s-switch-to-a-rails-application-243j</link>
      <guid>https://dev.to/yechielk/part-2-add-a-dead-man-s-switch-to-a-rails-application-243j</guid>
      <description>&lt;h3&gt;
  
  
  Ah Yes, Where Were We?
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://dev.to/yechielk/part-1-adding-a-dead-man-s-switch-to-a-rails-application-bgp"&gt;part 1&lt;/a&gt; of this tutorial, we learned how to set up the part of the deadman's switch that allows us to reset the switch.&lt;/p&gt;

&lt;p&gt;In part 2, we will get to the main part; how to set up a script that will run if something happens and the switch isn't reset in a given amount of time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rake It In
&lt;/h3&gt;

&lt;p&gt;The standard way of running scripts in Rails apps (and Ruby environments generally) is using Rake tasks.&lt;/p&gt;

&lt;p&gt;Rake tasks are, in their most basic form, Ruby scripts that you can run using the command &lt;code&gt;rake [task-name]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In fact, chances are you've run quite a few already! If you've run commands like &lt;code&gt;rails db:setup&lt;/code&gt;, &lt;code&gt;rails db:migrate&lt;/code&gt;, these are rake tasks that come built-in with Rails for setting up your database!&lt;/p&gt;

&lt;p&gt;We can write our own Rake tasks as well. They usually live in the &lt;code&gt;lib/tasks/&lt;/code&gt; directory of our Rails app, and the files use the &lt;code&gt;.rake&lt;/code&gt; extension instead of the `.rb extension Ruby scripts usually use.&lt;/p&gt;

&lt;p&gt;Let's start by creating a file &lt;code&gt;lib/tasks/deadmans-switch.rake&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the file, let's put the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ruby&lt;br&gt;
desc "Deadman's Switch"&lt;/p&gt;

&lt;p&gt;task :deadman_switch do&lt;br&gt;
    puts "Our first task!"&lt;br&gt;
end&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;At this point, we have a very basic Rake task. If you run &lt;code&gt;rake -T&lt;/code&gt; in your terminal, you should get a list of all the Rake asks you have available, including this one:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;shell&lt;br&gt;
rake deadmans_switch                       # Deadman's switch&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On the left side, we have the name of our task, and on the right, the description that we gave it in the first line following the &lt;code&gt;desc&lt;/code&gt; keyword.&lt;/p&gt;

&lt;p&gt;If we run &lt;code&gt;rake deadmans_switch&lt;/code&gt; in our terminal, we should see the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;shell&lt;br&gt;
$  rake deadman_switch &lt;br&gt;
=&amp;gt; Our first task!&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Of course, at this point, our task does nothing more than print a line to let us know it's there, but we can replace that with any Ruby code we want, so let's do that!&lt;/p&gt;

&lt;p&gt;Replace the contents of the task with:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;ruby&lt;br&gt;
    if 7.days.ago &amp;lt; Deadman.last_reset&lt;br&gt;
        puts "Still alive!"&lt;br&gt;
    else&lt;br&gt;
        puts "Executing Deadman Switch!"&lt;br&gt;
        # whatever you want to do goes here&lt;br&gt;
    end&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This script now uses the &lt;code&gt;.last_reset&lt;/code&gt; method we put on our &lt;code&gt;DeadmansSwitch&lt;/code&gt; class to check when you last reset the switch. If it was less than seven days ago, it just prints "Still alive!" and exits; if you didn't reset the switch in over a week, it'll execute whatever script you tell it to.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I put a week in my script because I figured that was a reasonable timeframe for my needs. You can change that to 2 days, two weeks, a month, a year, whatever you feel the right timeframe for your needs. Ruby's &lt;code&gt;Time&lt;/code&gt; class offers great methods for denoting a timeframe, which you can use similar to the &lt;code&gt;7.days.ago&lt;/code&gt; that I used.&lt;/p&gt;

&lt;p&gt;Now if you run &lt;code&gt;rake deadmans_switch&lt;/code&gt; in the console, you should get:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;shell&lt;br&gt;
$  rake deadman_switch &lt;br&gt;
=&amp;gt; Still Alive!&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;At this point, you can start working on the actual body of the script, which will be different depending on what it is you need a dead man's switch, so I'll leave you to it.&lt;/p&gt;

&lt;p&gt;If you want to test and debug the script by running it and don't want to wait seven days for the switch to expire, you can change the &lt;code&gt;7.days.ago&lt;/code&gt; in your &lt;code&gt;if&lt;/code&gt; statement to something more reasonable like &lt;code&gt;7.minutes.ago&lt;/code&gt;. Just don't forget to change it back when you're done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Have You Been Triggered?
&lt;/h3&gt;

&lt;p&gt;Now that we have a script and a way to ensure it only runs after a given time, there's one more thing we need to do. We probably don't want this script to run more than once.&lt;/p&gt;

&lt;p&gt;For local scripts that run using a cron job, this isn't such an issue. You can probably use the script to reset the crontab. But this is a script running on Heroku where we don't have access to the scheduler from within the script, so we'll have to get creative.&lt;/p&gt;

&lt;p&gt;One option I explored, which turned out to be a dead-end for reasons I'll explain soon, but I'll include it here anyway purely for its entertainment value, was to put the following on the last line fo my script:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;ruby&lt;br&gt;
File.delete(__FILE__)&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;__FILE__&lt;/code&gt; is a Ruby constant representing the current file, so what that line of code does is it deletes the current file once it reaches that line. Sort of like those spy movies where you get a note telling you to destroy it once you're finished reading it.&lt;/p&gt;

&lt;p&gt;At first, I didn't think it would work. Spending the better part of last year developing for Windows servers and battling numerous "file in use" errors, I was sure Linux wouldn't let me do it either. But I did it just for kicks, and it turns out Linux is a lot more trusting! You can even try it yourself; create a test file, paste the above line of code, run it using Ruby, and watch it disappear!&lt;/p&gt;

&lt;p&gt;While that's a pretty fun solution, unfortunately, it won't work for our purposes. Heroku, as mentioned in Part 1, uses an ephemeral file system, so even if we delete the file containing the script, the next time the app deploys, Heroku will do a fresh pull from GitHub, and our file will reappear.&lt;/p&gt;

&lt;p&gt;If we want to keep track of whether our script ran, we will need to use something that persists, like a database entry. Fortunately, I'm a psychic who can see into the future, and if you remember, in Part 1, when we created our &lt;code&gt;deamans_switches&lt;/code&gt; table, we added a &lt;code&gt;triggered&lt;/code&gt; column, and we will now make use of it.&lt;/p&gt;

&lt;p&gt;The first thing we will do is add two more methods to our &lt;code&gt;DeadmansSwitch&lt;/code&gt; class in &lt;code&gt;app/models/deadmans_switch.rb&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`ruby&lt;br&gt;
    def self.triggered&lt;br&gt;
        create(triggered: true)&lt;br&gt;
    end&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def self.triggered?
    where(triggered: true).size &amp;gt; 0
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The first one, &lt;code&gt;.triggered&lt;/code&gt;, creates a new row in the database, but unlike a regular row, we set the &lt;code&gt;triggered&lt;/code&gt; column to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The second method, &lt;code&gt;.triggered?&lt;/code&gt;, queries the database and checks if there are any rows where &lt;code&gt;triggered&lt;/code&gt; is set to &lt;code&gt;true&lt;/code&gt; and returns &lt;code&gt;true&lt;/code&gt; if there is at least one such row.&lt;/p&gt;

&lt;p&gt;Now let's go back to our Rake task and put those to use.&lt;/p&gt;

&lt;p&gt;The first thing we will do is add the following line at the end of the script after everything we wanted to run ran:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;ruby&lt;br&gt;
Deadman.triggered&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will enter a row in the database with &lt;code&gt;triggered: true&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Next, all the way at the beginning of our script, in the very first line, we will put the following:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;ruby&lt;br&gt;
abort if Deadman.triggered?&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This will check the database and see if there are any rows where &lt;code&gt;triggered: true&lt;/code&gt; and abort the script if it finds any.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Scheduled
&lt;/h3&gt;

&lt;p&gt;At this point, we have a working deadman's switch. The only thing left is to deploy it along with our Rails app and set up a scheduler to run it.&lt;/p&gt;

&lt;p&gt;My app is deployed on Heroku, so the instructions will be for getting set up on Heroku. If you have a different setup, things will work differently for you.&lt;/p&gt;

&lt;p&gt;So once you've written up all the code, committed it, and pushed it off to Heroku, here is how to go about scheduling your script.&lt;/p&gt;

&lt;p&gt;From the Heroku dashboard, navigate to the "Resources" tab.&lt;/p&gt;

&lt;p&gt;Once in the "Resources" tab, find the button that says "Find more add-ons" and click on it.&lt;/p&gt;

&lt;p&gt;Search for &lt;a href="https://elements.heroku.com/addons/scheduler" rel="noopener noreferrer"&gt;Heroku Scheduler&lt;/a&gt; and install it and provision it to your app.&lt;/p&gt;

&lt;p&gt;Once it's installed properly, you should see it under add-ons in your resources tab. Click on the link to the Scheduler and then on the button that says "Add Job."&lt;/p&gt;

&lt;p&gt;You will be prompted to set a schedule to run your task (I set it to run every night at midnight) and which command to run, which you should set to &lt;code&gt;rake deadmans_switch&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Hit "Save Job," and that's it, you're all set!&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>scripting</category>
    </item>
    <item>
      <title>Part 1: Adding A Dead-Man's Switch To A Rails Application</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Mon, 14 Dec 2020 02:05:09 +0000</pubDate>
      <link>https://dev.to/yechielk/part-1-adding-a-dead-man-s-switch-to-a-rails-application-bgp</link>
      <guid>https://dev.to/yechielk/part-1-adding-a-dead-man-s-switch-to-a-rails-application-bgp</guid>
      <description>&lt;h3&gt;
  
  
  Content Warning
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;The subject of this post is a bit morbid and deals with contemplating our mortality. If that upsets you, feel free to skip the introduction and go straight to the technical parts of the implementation in the section titled &lt;strong&gt;"The Good Stuff."&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;I was recently thinking about the concept of a dead man's switch.&lt;/p&gt;

&lt;p&gt;For those who are unfamiliar, a dead man's switch is a process that is designed to run automatically unless someone steps in to stop it. The idea is to increase the resiliency and/or safety of a system by having a safety mechanism that will stop it unless someone consciously steps in and overrides it.&lt;/p&gt;

&lt;p&gt;One of my favorite examples is in trains where the breaks are engaged by default. To release the breaks, the conductor has to lift a handle or a pedal and keep holding it. The moment the conductor lets go of the pedal, the breaks will engage and stop the train. The idea behind the switch is that if something were to happen and the conductor was to pass out, the train won't continue barreling down the tracks; instead, it would come to a screeching halt immediately.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&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%2Fe2kwhvrtojnz47rk0meq.jpg" alt="A ‘deadman’ pedal in a diesel-electric railway locomotive" width="800" height="533"&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A ‘deadman’ pedal in a diesel-electric railway locomotive&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Many other systems have similar concepts, even if they don't contain physical switches.&lt;/p&gt;

&lt;p&gt;For example, a computer might contain a script designed to run automatically after a few days unless the timer gets reset.&lt;/p&gt;

&lt;p&gt;For example, many of us have things we might want to happen in the event that something terrible happens to us. Maybe we want to pass on the passwords to important accounts or notify loved ones about a life insurance policy, or perhaps to wipe out some deep dark secret we want no one to find out about.&lt;/p&gt;

&lt;p&gt;One way we could go about it is to set a cron job on our computer to run every day. The cron job could look at a file and see when it was last updated, and if it wasn't updated in, say, over a week, it'll run a script that sends an email or does whatever else it is we want it to do.&lt;/p&gt;

&lt;p&gt;There's a limitation with that, and that is that it assumes your computer will be on, which is not a given for my personal computer.&lt;/p&gt;

&lt;p&gt;The next best option is to have my switch hosted in the cloud somewhere, though that comes with some expenses, and access to reset the times is a little more complicated than updating a file on my computer.&lt;/p&gt;

&lt;p&gt;The idea I came up with was to add the switch to my portfolio site. It's the only website I own, so I could do whatever I want with it. I'm already paying for hosting, so there's no extra expense for the switch, so it made sense for me.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Good Stuff
&lt;/h3&gt;

&lt;p&gt;This post assumes you already have a Rails app and that you have a rudimentary knowledge of Ruby and Rails.&lt;/p&gt;

&lt;p&gt;My Rails app is hosted on Heroku. The tutorial's Rails portion should apply to any Rails app no matter where it's hosted, but some of the parts around running the sitch are Heroku specific. However, there are probably parallel mechanisms for other hosting options as well.&lt;/p&gt;

&lt;p&gt;There's one part of the tutorial that requires a Twilio account with an associated project. It's not completely necessary, but if you want your switch to send out reminders as it gets closer to triggering, you might want to create a Twilio account.&lt;/p&gt;

&lt;p&gt;Creating the account is free, and if you use my &lt;a href="//www.twilio.com/referral/PWhqJW"&gt;referral link&lt;/a&gt;, you'll get $10 SMS credit, which should be more than enough for our purposes.&lt;/p&gt;

&lt;p&gt;Our dead man's switch will need a few components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A way to reset the switch.&lt;/li&gt;
&lt;li&gt;A script that will run every day and check if you reset the switch.

&lt;ul&gt;
&lt;li&gt;If you did reset the switch recently, the script would abort.&lt;/li&gt;
&lt;li&gt;If you did not, the script would do whatever it is you want it to do.&lt;/li&gt;
&lt;li&gt;Once the script runs, we want a way to keep track of that, so it doesn't run again.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll start with the way to reset the switch.&lt;/p&gt;

&lt;p&gt;If we were hosting this switch locally, a simple way to do this would be to look at the last time a given file was updated, and then keep updating that file every day.&lt;/p&gt;

&lt;p&gt;Unfortunately, that wouldn't work for a script hosted on Heroku. Heroku uses an ephemeral filesystem for its apps, and every time the app gets redeployed, or if it crashes and has to be restarted, or even if it's taken down for routine maintenance, Heroku makes a fresh pull on the repo and any changes you made to the filesystem get overwritten.&lt;/p&gt;

&lt;p&gt;We have to find a way to keep track of when the switch was reset, and a way that will persist.&lt;/p&gt;

&lt;p&gt;I did it by creating a database table where I could enter rows containing a timestamp and then check the timestamp of the last row added.&lt;/p&gt;

&lt;p&gt;Once I was already making a table, I figured I could add an ActiveRecord model to my app. That would give me some built-in ActiveRecord methods to interact with my table to read and update it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's Start Generating!
&lt;/h3&gt;

&lt;p&gt;So let's start by opening up our terminal and generating our migration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails generate model CreateDeadmansSwitch triggered:boolean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command created a number of files. The ones we care about are &lt;code&gt;db/migrate/[migration-id]_create_deadmans_switches.rb&lt;/code&gt; containing our database migration for a &lt;code&gt;deadman_switches&lt;/code&gt; table that has the standard timestamps columns (as well as a boolean column called &lt;code&gt;triggered&lt;/code&gt;) we will discuss later).&lt;/p&gt;

&lt;p&gt;The second file we care about is in &lt;code&gt;app/models/deadmans_switch.rb&lt;/code&gt; that creates our &lt;code&gt;DeadmansSwitch&lt;/code&gt; class.&lt;/p&gt;

&lt;p&gt;Let's open up &lt;code&gt;db/migrate/20201211142929_create_deadmans_switches.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreateDeadmansSwitches&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;5.2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:deadmans_switches&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt; &lt;span class="ss"&gt;:triggered&lt;/span&gt;

      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timestamps&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Before we run this migration, let's make one small change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-    t.boolean :triggered
&lt;/span&gt;&lt;span class="gi"&gt;+    t.boolean :triggered, default: false
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We added a default value of &lt;code&gt;false&lt;/code&gt; to the &lt;code&gt;triggered&lt;/code&gt; column, so that is the value it will contain unless we specify otherwise.&lt;/p&gt;

&lt;p&gt;Now we can go back to our terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That will run our migration and add the &lt;code&gt;deadmans_switches&lt;/code&gt; table to our database schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Models Models Everywhere
&lt;/h3&gt;

&lt;p&gt;Now that we have our database set up let's look at our model in &lt;code&gt;app/models/deadmans_switch.rb&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Currently, our model is pretty empty:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeadmansSwitch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's add two class methods to our class; one to reset the switch and one to check when it was last reset:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DeadmansSwitch&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;
        &lt;span class="n"&gt;create&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_reset&lt;/span&gt;
        &lt;span class="n"&gt;last&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;created_at&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We added two class methods.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;reset&lt;/code&gt; method creates a new instance of the &lt;code&gt;DeadmansSwitch&lt;/code&gt; class, adding a new row to the database.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;last_reset&lt;/code&gt; method looks at the last row in the database and, if it finds one, returns the date and time it was created on; otherwise, it returns the current time (this is to protect against an edge case where we run the app before we rest the switch for the first time).&lt;/p&gt;

&lt;h3&gt;
  
  
  Routing
&lt;/h3&gt;

&lt;p&gt;Next, we need a route that will listen for, and trigger our switch.&lt;/p&gt;

&lt;p&gt;My setup relies on two assumptions that apply to my app.&lt;/p&gt;

&lt;p&gt;First, I have an &lt;code&gt;AdminController&lt;/code&gt; that handles all the admin-related routing in my app.&lt;/p&gt;

&lt;p&gt;Second, in my &lt;code&gt;AdminController&lt;/code&gt;, I have a helper method called &lt;code&gt;is_admin?&lt;/code&gt; that checks if the current user is an admin.&lt;/p&gt;

&lt;p&gt;The details of your app may vary, so adjust accordingly.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;config/routs.rb&lt;/code&gt; add the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'deadman/reset'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'admin#reset'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets up a route at &lt;code&gt;/deadman/reset&lt;/code&gt; and routes it to the &lt;code&gt;reset&lt;/code&gt; function in my &lt;code&gt;AdminController&lt;/code&gt; and gives us access to a &lt;code&gt;deadman_reset_path&lt;/code&gt; function that points to the new route.&lt;/p&gt;

&lt;p&gt;If you have your app running, don't forget to restart it in the terminal for any routing changes to take effect.&lt;/p&gt;

&lt;p&gt;Next, let's add that function in &lt;code&gt;AdminController&lt;/code&gt; (or whichever controller you decided to use for your app):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;reset&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;is_admin?&lt;/span&gt;
            &lt;span class="no"&gt;DeadmansSwitch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reset&lt;/span&gt;
            &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;admin_root_path&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
            &lt;span class="n"&gt;redirect_to&lt;/span&gt; &lt;span class="n"&gt;login_path&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This checks if the current user is an admin (you don't want just anyone who happens upon the URL to have the ability to reset your switch).&lt;/p&gt;

&lt;p&gt;If the user is an admin, we call &lt;code&gt;DeadmansSwitch.reset&lt;/code&gt; and redirect back to the page that called it; in my case, to the admin page (again, the routing in your app might be different, so adjust accordingly).&lt;/p&gt;

&lt;h3&gt;
  
  
  What A View!
&lt;/h3&gt;

&lt;p&gt;Now that we have that in place let's use it in our app.&lt;/p&gt;

&lt;p&gt;Where exactly you put this in your app depends on the layout of your app. Ideally, you would put it in a part of your app that only you can access, like an admin console or something.&lt;/p&gt;

&lt;p&gt;This is what I have in my app, feel free to style it as elaborately, or plainly, as you wish:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight erb"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;link_to&lt;/span&gt; &lt;span class="s2"&gt;"Reset Switch"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;deadman_reset_path&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;small&amp;gt;&lt;/span&gt;(Last reset &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;time_ago_in_words&lt;/span&gt; &lt;span class="no"&gt;Deadman&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last_reset&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt; ago)&lt;span class="nt"&gt;&amp;lt;/small&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a link to our reset route, followed by a small helpful note which uses the &lt;code&gt;time_ago_in_words&lt;/code&gt; helper method to display when the last time you reset the switch.&lt;/p&gt;

&lt;p&gt;Here is what it looks like on my portfolio:&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%2Fi%2Fwpwey2cu6efdpnwtlorn.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%2Fi%2Fwpwey2cu6efdpnwtlorn.png" alt="A screenshot showing the link and the above text. " width="323" height="96"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now we have part of our dead man's switch in place, the part where we can reset it every day, so it knows we're still around and doesn't trigger our script.&lt;/p&gt;

&lt;p&gt;In part 2, we will speak about how to write the script that checks our switch and runs if we haven't reset it in a while.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>scripting</category>
      <category>ruby</category>
    </item>
    <item>
      <title>What are your favorite Kotlin resources?</title>
      <dc:creator>Yechiel Kalmenson</dc:creator>
      <pubDate>Mon, 26 Oct 2020 13:01:57 +0000</pubDate>
      <link>https://dev.to/yechielk/what-are-your-favorite-kotlin-resources-f43</link>
      <guid>https://dev.to/yechielk/what-are-your-favorite-kotlin-resources-f43</guid>
      <description>&lt;p&gt;Hey all!&lt;/p&gt;

&lt;p&gt;I recently joined a new team and we do most of our work with Kotlin.&lt;/p&gt;

&lt;p&gt;It looks like a great language to work with, and I'm looking for resources to help me ramp up.&lt;br&gt;
Something for someone familiar with programming concepts, and looking to ramp up on the Kotlin specifics, conventions, and best practices.&lt;/p&gt;

&lt;p&gt;I'm a hands-on learner, and I learn best by trying thongs out and breaking them, so those are the resources I'm mostly looking for.&lt;/p&gt;

&lt;p&gt;Something like Go's &lt;a href="https://tour.golang.org/welcome/1" rel="noopener noreferrer"&gt;A Tour Of Go&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Any ideas?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>kotlin</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
