<?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>Forem: Jessica Wang</title>
    <description>The latest articles on Forem by Jessica Wang (@therubberduckiee).</description>
    <link>https://forem.com/therubberduckiee</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F880658%2F0f1cbb1a-03b3-4fa5-b5c0-dab995e09c7a.png</url>
      <title>Forem: Jessica Wang</title>
      <link>https://forem.com/therubberduckiee</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/therubberduckiee"/>
    <language>en</language>
    <item>
      <title>Reverse Engineering DUPR's Pickleball Rating Algorithm</title>
      <dc:creator>Jessica Wang</dc:creator>
      <pubDate>Wed, 31 Dec 2025 00:13:58 +0000</pubDate>
      <link>https://forem.com/therubberduckiee/reverse-engineering-duprs-pickleball-rating-algorithm-40cc</link>
      <guid>https://forem.com/therubberduckiee/reverse-engineering-duprs-pickleball-rating-algorithm-40cc</guid>
      <description>&lt;p&gt;TL;DR: I built a machine learning tool that predicts DUPR rating changes with 86% accuracy using web scraping, Python, and Gradient Boosting. After scraping 6,844 player-match records from 35 players, I discovered DUPR uses a heavily modified ELO system with rating compression at extremes and opponent strength as the dominant factor.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/DaRubberDuckieee/pickleball-dupr-predictor" rel="noopener noreferrer"&gt;https://github.com/DaRubberDuckieee/pickleball-dupr-predictor&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Live Demo: &lt;a href="//dupr-predictor.vercel.app"&gt;dupr-predictor.vercel.app &lt;/a&gt; &lt;/li&gt;
&lt;/ul&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%2Ficzqzwix74wkabpxpdy8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ficzqzwix74wkabpxpdy8.png" alt=" " width="800" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;DUPR (Dynamic Universal Pickleball Rating) is the standard rating system for competitive pickleball, but it's not super clear how it gets calculated. For example, I could play somebody of a much lower DUPR than me, but my DUPR could still go down if I don't absolutely crush them. Or I could play somebody of a higher DUPR and lose, but my DUPR could still go up.&lt;/p&gt;

&lt;p&gt;So I wanted to answer: Can I predict my rating change before playing a match?&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%2F4bbaaiaraj65iqbfh1rq.gif" 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%2F4bbaaiaraj65iqbfh1rq.gif" alt=" " width="498" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Existing solutions didn't help.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;There is some &lt;a href="https://www.dupr.com/how-it-works" rel="noopener noreferrer"&gt;information on their website&lt;/a&gt; around match recency, volume, and reliability that is good conceptually but isn't specific.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DUPR does have an app with a "forecast" tool where I can input 4 players (to simulate doubles) but it will only tell you what it predicts the score &lt;strong&gt;should&lt;/strong&gt; be if you were to play. It won't tell you how ratings will changed based off a hypothetical score you input.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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%2F02iuhl8lrlyct4lspi3y.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F02iuhl8lrlyct4lspi3y.PNG" alt=" " width="800" height="1731"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Goals &amp;amp; Non-Goals
&lt;/h2&gt;

&lt;p&gt;Goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scrape enough real life match data to reverse engineer the algorithm&lt;/li&gt;
&lt;li&gt;Build a predictive model based on that data with a "good enough" accuracy (good enough means I look at it and think it looks good enough)&lt;/li&gt;
&lt;li&gt;Deploy a web interface for predictions&lt;/li&gt;
&lt;/ul&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%2Fn97ou9bnef0d5c74ilpp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn97ou9bnef0d5c74ilpp.png" alt=" " width="800" height="434"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Non-Goals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Perfect accuracy (probably always off by a teeny bit)&lt;/li&gt;
&lt;li&gt;Singles ratings (focused on doubles only)&lt;/li&gt;
&lt;li&gt;Real-time scraping (one-time data collection was sufficient)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  High-Level Architecture
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Scraper: Selenium + BeautifulSoup (Python)
&lt;/li&gt;
&lt;li&gt;Data processing: pandas + numpy
&lt;/li&gt;
&lt;li&gt;ML model: scikit-learn (Gradient Boosting Regressor)
&lt;/li&gt;
&lt;li&gt;Backend: Flask API
&lt;/li&gt;
&lt;li&gt;Frontend: Vanilla HTML/CSS/JS
&lt;/li&gt;
&lt;li&gt;Deployment: Vercel (frontend) + Render (API)
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's how the data flow worked.&lt;/p&gt;

&lt;p&gt;1) Selenium scrapes player match histories from pickleball.com. It will get back some HTML like this:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;div class="hidden md:block"&amp;gt;&lt;br&gt;
  &amp;lt;table&amp;gt;&lt;br&gt;
    &amp;lt;tr&amp;gt;&lt;br&gt;
      &amp;lt;td&amp;gt;Dec 29, 2024&amp;lt;/td&amp;gt;&lt;br&gt;
      &amp;lt;td&amp;gt;Jessica Wang26 | F | WA, USA&amp;lt;/td&amp;gt;&lt;br&gt;
      &amp;lt;td&amp;gt;5.088 → 5.126 (+0.038)&amp;lt;/td&amp;gt;&lt;br&gt;
      &amp;lt;td&amp;gt;Partner: 5.353 → 5.383 (+0.030)&amp;lt;/td&amp;gt;&lt;br&gt;
      &amp;lt;td&amp;gt;Opponents: 5.148, 5.297&amp;lt;/td&amp;gt;&lt;br&gt;
      &amp;lt;td&amp;gt;Score: 15-1&amp;lt;/td&amp;gt;&lt;br&gt;
    &amp;lt;/tr&amp;gt;&lt;br&gt;
  &amp;lt;/table&amp;gt;&lt;br&gt;
&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;2) BeautifulSoup parses rating data from dynamically rendered pages and returns back data in this format:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{&lt;br&gt;
    'date': '2024-12-29',&lt;br&gt;
    'team1_player1_rating_before': 5.088,&lt;br&gt;
    'team1_player1_rating_after': 5.126,&lt;br&gt;
    'team1_player1_rating_change': 0.038,&lt;br&gt;
    'team1_player2_rating_before': 5.353,&lt;br&gt;
    'team1_player2_rating_after': 5.383,&lt;br&gt;
    'team1_player2_rating_change': 0.030,&lt;br&gt;
    'team2_player1_rating_before': 5.148,&lt;br&gt;
    'team2_player2_rating_before': 5.297,&lt;br&gt;
    'team2_player1_rating_change': -0.093,  # Inferred (they lost)&lt;br&gt;
    'team2_player2_rating_change': -0.175,&lt;br&gt;
    'game1_team1_score': 15,&lt;br&gt;
    'game1_team2_score': 1&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;3) pandas processes 6,844 player-match records into training data. &lt;a href="https://github.com/DaRubberDuckieee/pickleball-dupr-predictor/blob/main/deep_analysis.py" rel="noopener noreferrer"&gt;Here's&lt;/a&gt; the code if you want to read it. &lt;/p&gt;

&lt;p&gt;4) Gradient Boosting model trained on 14 engineered features, such as score margin, surprise level, and opponent's average rating.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;# For Team 1 Player 1 (rating 5.088)&lt;br&gt;
features = {&lt;br&gt;
    'won': 1,&lt;br&gt;
    'rating_diff': 5.088 - 5.2225,  # = -0.1345&lt;br&gt;
    'score_margin': 14,&lt;br&gt;
    'total_points': 14,&lt;br&gt;
    'partner_diff': 5.088 - 5.353,  # = -0.265&lt;br&gt;
    'team_vs_opp': ((5.088 + 5.353)/2) - 5.2225,  # = -0.002&lt;br&gt;
    'won_x_rating_diff': 1 * -0.1345,  # = -0.1345&lt;br&gt;
    'won_x_score_margin': 1 * 14,  # = 14&lt;br&gt;
    'rating_squared': 5.088 ** 2,  # = 25.888&lt;br&gt;
    'surprise': 1 - (1 / (1 + 10**((5.2225 - 5.088)/4))),  # = 0.52&lt;br&gt;
    'opp_spread': abs(5.148 - 5.297),  # = 0.149&lt;br&gt;
    'player_rating': 5.088,&lt;br&gt;
    'partner_rating': 5.353,&lt;br&gt;
    'opp_avg': 5.2225&lt;br&gt;
}&lt;br&gt;
Feature vector: [1, -0.1345, 14, 14, -0.265, -0.002, -0.1345, 14, 25.888, 0.52, 0.149, 5.088, 5.353, 5.2225]&lt;br&gt;
Target: 0.038&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;5) Flask API serves predictions, and static frontend sends match scenarios to API. So I could send this hypothetical score:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;curl -X POST http://localhost:8080/predict \&lt;br&gt;
  -H "Content-Type: application/json" \&lt;br&gt;
  -d '{&lt;br&gt;
    "team1_player1": 5.0,&lt;br&gt;
    "team1_player2": 5.2,&lt;br&gt;
    "team2_player1": 4.8,&lt;br&gt;
    "team2_player2": 5.1,&lt;br&gt;
    "team1_score": 11,&lt;br&gt;
    "team2_score": 9&lt;br&gt;
  }'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;And the API would return this back:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{&lt;br&gt;
  "team1": {&lt;br&gt;
    "player1": {&lt;br&gt;
      "rating_before": 5.0,&lt;br&gt;
      "rating_change": 0.022,&lt;br&gt;
      "rating_after": 5.022&lt;br&gt;
    },&lt;br&gt;
    "player2": {&lt;br&gt;
      "rating_before": 5.2,&lt;br&gt;
      "rating_change": 0.016,&lt;br&gt;
      "rating_after": 5.216&lt;br&gt;
    }&lt;br&gt;
  },&lt;br&gt;
  "team2": {&lt;br&gt;
    "player1": {&lt;br&gt;
      "rating_before": 4.8,&lt;br&gt;
      "rating_change": 0.042,&lt;br&gt;
      "rating_after": 4.842&lt;br&gt;
    },&lt;br&gt;
    "player2": {&lt;br&gt;
      "rating_before": 5.1,&lt;br&gt;
      "rating_change": 0.049,&lt;br&gt;
      "rating_after": 5.149&lt;br&gt;
    }&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Model Types and Findings
&lt;/h2&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%2Fjft5phft3i1bitpbjqew.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjft5phft3i1bitpbjqew.png" alt=" " width="800" height="760"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each graph shows how well a model predicts DUPR rating changes across all 13,832 training samples. The X-axis represents the actual rating change DUPR gave, while the Y-axis shows what each model predicted. The diagonal dashed line represents perfect prediction—if a model were 100% accurate, all dots would fall exactly on this line. You can visually see the improvement: poor models create horizontal clouds of dots, while good models create tight diagonal streams.&lt;/p&gt;

&lt;p&gt;Before diving into the models, here's what the metrics mean:&lt;/p&gt;

&lt;p&gt;👉 R² (R-squared): Measures how well the model explains the variance in rating changes. A score of 1.0 is perfect prediction, 0.0 means the model is no better than guessing the average. An R² of 0.86 means the model explains 86% of why ratings change the way they do.&lt;/p&gt;

&lt;p&gt;👉 MAE (Mean Absolute Error): The average prediction error in rating points. An MAE of 0.11 means predictions are off by ±0.11 points on average.&lt;/p&gt;

&lt;p&gt;Here are the 4 models we tested.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Linear Regression (Baseline)&lt;/strong&gt;&lt;br&gt;
What it is: Simple weighted sum of features. Assumes the relationship between inputs and rating change is a straight line.&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%2F0638gmrn06pc01jpz6ly.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0638gmrn06pc01jpz6ly.png" alt=" " width="800" height="722"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Features used: won, rating_diff, score_margin, total_points&lt;/p&gt;

&lt;p&gt;Performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;R² = 0.12&lt;/li&gt;
&lt;li&gt;MAE = 0.389&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why it failed: DUPR doesn't use linear math. A 4.0 player's rating change follows different rules than a 3.0 or 5.0 player. Linear models can't capture these rating-level specific behaviors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Linear Regression with Engineered Features&lt;/strong&gt;&lt;br&gt;
What it is: Same linear model but with smarter features like ELO-style "surprise" (how unexpected the outcome was) and interaction terms.&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%2F4ee81ow6av9n7sgpuwbe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ee81ow6av9n7sgpuwbe.png" alt=" " width="800" height="779"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Features added: surprise (won - expected_outcome), won_x_rating_diff, partner_diff, rating_squared&lt;/p&gt;

&lt;p&gt;Performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;R² = 0.54&lt;/li&gt;
&lt;li&gt;MAE = 0.287&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Breakthrough moment: Adding the surprise feature (ELO expected outcome) nearly 5x'd the R². This confirmed DUPR uses ELO-like logic. However, this still was not good enough, since linear models still fundamentally can't handle DUPR's complex rules.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3) Gradient Boosting - Aggressive&lt;/strong&gt;&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%2F2qitxtfb94701063m2zm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qitxtfb94701063m2zm.png" alt=" " width="800" height="742"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What it is: Gradient Boosting builds predictions by training a sequence of decision trees, where each tree learns to fix the mistakes of all previous trees. The first tree makes initial predictions based on the training data, but it won't be perfect—some predictions will be too high, others too low. The second tree doesn't try to predict the rating changes directly; instead, it predicts the errors (residuals) from the first tree. This correction is added to the first tree's prediction, creating a combined model that's more accurate. The process repeats: tree 3 predicts the remaining errors after trees 1 and 2, tree 4 fixes what tree 3 missed, and so on. Each tree is weighted by the learning rate (0.05 in our case), which controls how aggressively each new tree corrects previous mistakes. Lower learning rates make the model train slower but generalize better. After 100 trees, the final prediction is the sum of all individual tree predictions, creating a powerful non-linear model that can capture complex patterns like "4.5+ players follow different rules than 3.0 players" without being explicitly programmed with those rules.&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%2F9osdaa7lxhx021muplje.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9osdaa7lxhx021muplje.png" alt=" " width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hyperparameters: 150 estimators, max_depth=5, learning_rate=0.1&lt;/p&gt;

&lt;p&gt;Performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;R² = 0.92&lt;/li&gt;
&lt;li&gt;MAE = 0.089&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why we didn't deploy it: Too accurate on training data, which means it is likely overfitting. Would probably perform worse on unseen matches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4) Gradient Boosting - Balanced ✅ (DEPLOYED)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Hyperparameters: 100 estimators, max_depth=3, learning_rate=0.05, min_samples_leaf=10&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%2Fri5w6cxba521i9mirkoj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fri5w6cxba521i9mirkoj.png" alt=" " width="800" height="792"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;R² = 0.86&lt;/li&gt;
&lt;li&gt;MAE = 0.114&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why this won: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10× better R² than baseline (0.12 → 0.86)&lt;/li&gt;
&lt;li&gt;Handles rating-level specific rules (3.0 players vs 5.0 players behave differently)&lt;/li&gt;
&lt;li&gt;Doesn't overfit&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final Architecture
&lt;/h2&gt;

&lt;p&gt;Deployed Model: Gradient Boosting (Balanced) - Model 3&lt;/p&gt;

&lt;p&gt;Feature set (14 features):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic: won, score_margin, total_points&lt;/li&gt;
&lt;li&gt;Ratings: player_rating, partner_rating, opp_avg&lt;/li&gt;
&lt;li&gt;Derived: rating_diff, partner_diff, team_vs_opp, opp_spread&lt;/li&gt;
&lt;li&gt;Interactions: won_x_rating_diff, won_x_score_margin&lt;/li&gt;
&lt;li&gt;Non-linear: rating_squared&lt;/li&gt;
&lt;li&gt;ELO-style: surprise (won - expected_outcome)&lt;/li&gt;
&lt;/ul&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%2Fu6azjtglihon4pecm8ot.jpeg" 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%2Fu6azjtglihon4pecm8ot.jpeg" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Findings About DUPR's Algorithm
&lt;/h2&gt;

&lt;p&gt;After analyzing 6,844 player-match records with the trained model, here's what actually matters:&lt;/p&gt;

&lt;p&gt;What Matters A LOT:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Opponent strength relative to yours (correlation: -0.329)&lt;br&gt;
Playing up (underdog): Gain rating even when losing (+0.049 avg)&lt;br&gt;
Playing down (favorite): Lose rating even when winning (-0.148 avg)&lt;br&gt;
Rating differential is THE dominant factor&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Your absolute rating level (correlation: -0.368)&lt;br&gt;
Higher-rated players experience more deflation&lt;br&gt;
4.5+ players: Wins are worth LESS than losses (-0.006 difference)&lt;br&gt;
&amp;lt;3.0 players: Heavy inflation regardless of outcome&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Expected outcome vs. actual outcome&lt;br&gt;
DUPR uses ELO-style surprise calculation&lt;br&gt;
Upset wins rewarded heavily (+0.400 for underdogs)&lt;br&gt;
Expected wins barely rewarded (-0.148 for favorites)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What Doesn't Matter:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Score margin (correlation: +0.018)
Winning 11-0 vs 11-9 changes your rating by only ~0.05 points
Blowouts don't give bonus points&lt;/li&gt;
&lt;li&gt;Win/loss alone (+0.040 difference)
Winning helps, but barely
WHO you beat matters 50× more than IF you beat them&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Zero-Change Mystery:&lt;br&gt;
•  10.3% of all matches result in exactly 0.000 rating change&lt;br&gt;
•  Happens at all rating levels, both wins and losses&lt;br&gt;
•  Theory: DUPR applies threshold rule when match provides "no new information"&lt;br&gt;
•  More common in balanced matchups (±0.3 rating diff)&lt;/p&gt;

&lt;p&gt;Strategic Implications:&lt;br&gt;
•  ✅ Play against higher-rated opponents (maximize rating gain)&lt;br&gt;
•  ✅ Win close matches as underdog (+0.400 avg boost)&lt;br&gt;
•  ❌ Don't worry about score margin (negligible impact)&lt;br&gt;
•  ❌ Avoid being heavily favored (&amp;gt;0.3 advantage = deflation)&lt;/p&gt;

&lt;p&gt;Bottom line: DUPR is a heavily modified ELO system with rating compression at extremes and aggressive deflation for favorites. The model achieved R²=0.86, meaning it captures 86% of DUPR's logic—the missing 14% is likely hidden factors like match recency or tournament context.&lt;/p&gt;

&lt;p&gt;Live demo: &lt;a href="//dupr-predictor.vercel.app"&gt;dupr-predictor.vercel.app&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/DaRubberDuckieee/pickleball-dupr-predictor" rel="noopener noreferrer"&gt;https://github.com/DaRubberDuckieee/pickleball-dupr-predictor&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;Live Demo: &lt;a href="//dupr-predictor.vercel.app"&gt;dupr-predictor.vercel.app &lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>machinelearning</category>
      <category>programming</category>
      <category>python</category>
      <category>pickleball</category>
    </item>
    <item>
      <title>What We Learned From Building Share-Brewfiles (Astro + React + Clack CLI)</title>
      <dc:creator>Jessica Wang</dc:creator>
      <pubDate>Fri, 31 May 2024 06:42:54 +0000</pubDate>
      <link>https://forem.com/therubberduckiee/what-we-learned-from-building-share-brewfiles-astro-react-clack-cli-2p4c</link>
      <guid>https://forem.com/therubberduckiee/what-we-learned-from-building-share-brewfiles-astro-react-clack-cli-2p4c</guid>
      <description>&lt;h1&gt;
  
  
  INTRO
&lt;/h1&gt;

&lt;h2&gt;
  
  
  What did we build?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="//www.brewfiles.com"&gt;Share Brewfiles project&lt;/a&gt; was built to make it easy for people to share out their full tech stack. We also added a social aspect to the site so that developers could have fun with it.&lt;/p&gt;

&lt;p&gt;Here’s what we built:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/_AaW7VPBY80"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A personalized Brewfile page that is easily shared and just requires one CLI command to be updated whenever you want.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xlozry4ntingdt22r4q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5xlozry4ntingdt22r4q.png" alt="Image description" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates a developer personality profile based off the packages in your Brewfile, really fun to share with friends &amp;amp; social media.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgombb2b2a9splwmnl70.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhgombb2b2a9splwmnl70.png" alt="Image description" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A leaderboard that lets you see what’s popular overall and discover new tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flawvnt9w8wguixwnuj3m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flawvnt9w8wguixwnuj3m.png" alt="Image description" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Search that allows you to search by package or username so you can stalk other developers’ Brewfiles.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdimc0l1b8t63c3cbg1ch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdimc0l1b8t63c3cbg1ch.png" alt="Image description" width="800" height="519"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;npx share-brewfiles&lt;/code&gt; in your CLI to try out the tool.&lt;/p&gt;

&lt;p&gt;Also, this project is &lt;a href="https://github.com/theRubberDuckiee/share-brewfiles-website"&gt;100% open source&lt;/a&gt;. I want to give a huge thank you to &lt;a href="//warp.dev"&gt;Warp&lt;/a&gt; (the company I work at) for encouraging me to build this tool. We hope this project contributes to the developer community in a fun and educational way!&lt;/p&gt;

&lt;h2&gt;
  
  
  Our inspiration
&lt;/h2&gt;

&lt;p&gt;Most developers’ careers can be tracked by the packages they’ve downloaded over time. At school, I downloaded frameworks like Python, Node.js, and Java. Upon graduating, Monokai Pro became my VSCode theme, Droid Sans Mono became my default NerdFont, and I used Starship to customize my terminal prompt. In recent years, I’ve experimented with newer tools like Warp, Raycast, and Github Copilot.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjtxg4p5j6y4wnzgxc36p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjtxg4p5j6y4wnzgxc36p.png" alt="Image description" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What tools we download tell the story of who we are as developers. We enjoy sharing our hand-selected tools in blogs or Youtube videos titled “Must-Have Tools And Apps” or “How To Customize Your Dev Machine In 2024”. But this content can’t be easily updated as you evolve, and usually only contain 5-10 tools when realistically most developers are probably using &amp;gt;50 packages.&lt;/p&gt;

&lt;h1&gt;
  
  
  Technical Overview + Tech Stack
&lt;/h1&gt;

&lt;p&gt;Our tool can be split into 2 parts:&lt;/p&gt;

&lt;p&gt;1: &lt;strong&gt;The command line experience&lt;/strong&gt;&lt;br&gt;
Stack: Clack CLI, Github auth, Node.js&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq168nvdg8jp637wvd3oq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq168nvdg8jp637wvd3oq.png" alt="Image description" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;npx share-brewfiles&lt;/code&gt; to generate your Brewfile package list for you if you don’t already have an existing one in that directory. It collects your Github auth and sanitizes your data before uploading it to the brewfiles.com API endpoint. The UI of the command line interface (white vertical lines, spinning and text animations) is dictated by Clack CLI.&lt;/p&gt;

&lt;p&gt;2: &lt;strong&gt;The brewfiles.com website&lt;/strong&gt;&lt;br&gt;
Stack: Astro, React, Tailwind CSS, Firebase&lt;/p&gt;

&lt;p&gt;Just a reminder that this project is completely &lt;a href="https://github.com/theRubberDuckiee/share-brewfiles-website"&gt;open source&lt;/a&gt;. This code supports the entire web experience (which we described in the “What did we build?” section) which is too much to cover, but we’ll point out a few interesting chunks of our codebase in case you decide to poke around.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;api/uploadBrewfile&lt;/code&gt;&lt;br&gt;
This is the endpoint that the CLI logic will hit when uploading a Brewfile to the site. This logic will sanitize the data and check if this user already has an existing Brewfile (and update it if needed) before uploading it to our Firebase database. It will also asynchronously kick off the process of parsing through the packages to determine the developer’s personality type.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;generatePersonality.ts&lt;/code&gt;&lt;br&gt;
This file contains the logic through which we determine a developer’s personality type. You can see all the different possible personality types, as well as the criteria we set to be categorized for each personality. We realize these categories and criteria may be a bit arbitrary, but cut us some slack - we decided to build this feature 2 weeks before launching! If you want to see improvements, send in a PR.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;labelledBrewfiles.ts&lt;/code&gt;&lt;br&gt;
This file is a dictionary of packages categorized by certain characteristics, like whether it’s frontend/backend/devops/ai, whether it’s related to the CLI, and more. This dictionary is what we use to filter our leaderboard by “top dev apps” and “top CLI tools”, as well as what we use to determine a developer’s personality type. We used ChatGPT to help us label these packages.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  What we learned
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnidhecc64cx66vhy643o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnidhecc64cx66vhy643o.png" alt="Image description" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Astro vs React
&lt;/h2&gt;

&lt;p&gt;When choosing a web framework, we settled on Astro. Admittedly, part of this decision was inherent bias. Astro was a technology that tech influencers were claiming to be “S-tier”, and we thought this newer tool would be fun to write about and attract attention from those who were curious. From a more logical standpoint, we chose Astro because SSR rendering meant better performance and better implications for SEO. Plus, it allowed for a high degree of flexibility by simplifying the management of dependencies like React/Vue/Svelte.&lt;/p&gt;

&lt;p&gt;One downfall of this approach is that Astro is meant for content-heavy, mostly static websites, whereas the Brewfiles website has many dynamic elements. While content-rich sites have their place, our thoughts about Astro mostly came down to planning. Due to the nature of building the site as we were designing the UI/UX, we continued to make adjustments—each one moving more and more dynamic.&lt;/p&gt;

&lt;p&gt;Thankfully, Astro can include React and other UI frameworks with a simple integration, but we lost most of the benefit of Astro as we continued to make the site more dynamic. So while Astro could handle it, a React meta framework would have likely been a cleaner experience. In the end, we were left with a few Astro route shells filled mostly with React components and manual vanilla JavaScript components.&lt;/p&gt;

&lt;p&gt;For example, on our website, we have a screen that pops up an auth code that the user needs to copy when uploading their Brewfile to the site. Here’s what it looks like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphse7ikrabpflkxwkd1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fphse7ikrabpflkxwkd1k.png" alt="Image description" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the user presses “Copy Code”, the text on the button will then change to “Copied!”. Here is what the Astro code looks like (or see code &lt;a href="https://github.com/theRubberDuckiee/share-brewfiles-website/blob/main/src/components/AuthPopup.astro"&gt;here&lt;/a&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvkrkq6iatpt009ihzfu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvkrkq6iatpt009ihzfu.png" alt="Image description" width="800" height="810"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see in the code, we need to interact with the DOM directory using JavaScript to add event listeners and perform actions based on those events. This approach is quite manual. It’s clear that Astro encourages developers to pre-render as much content as possible at build time. If we had written this code in React, we could simplify the logic using React’s state management and event handling capabilities. The code may look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhtxurlusxnm5udr3849x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhtxurlusxnm5udr3849x.png" alt="Image description" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can tell here, the use of useState and onClick() is much cleaner than using the &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag to embed Javascript code directly within the HTML markup. Because of the complexity caused by Astro, we ended up rewriting a few of our files using React instead.&lt;/p&gt;

&lt;p&gt;Though we had to work around some of Astro’s capabilities as a framework, there were still some upsides, which we’ll talk about in the next section when it comes to rendering and performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sever-Side(SSR) vs client side(CSR) rendering
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnkwtmlyerns3ydaep5j4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnkwtmlyerns3ydaep5j4.png" alt="Image description" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Astro SSR adapters enable server-side rendering, meaning as routes are requested, we could tell Astro to generate a finalized HTML page on the server. Some pages, however, require processing a lot of data before rendering the page. With each new brewfile submission, more processing will be required, and yet we want to maintain quick page loading speed as our data entries grow.&lt;/p&gt;

&lt;p&gt;We decided to server-side render each route and embed slower sections as client-side rendered components. As an example, the homepage includes a leaderboard list at the bottom. Generating the list requires fetching all users’ Brewfiles from the database and calculating a top 10 list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtabudibe28j48fnsogs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtabudibe28j48fnsogs.png" alt="Image description" width="800" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We created a server API route to handle the calculations and then embedded a client-side React component to hit the endpoint and display the final list. This combination of SSR and CSR allowed for the best possible performance and user experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Data decisions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ar67ndllckm0k27nvg4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ar67ndllckm0k27nvg4.png" alt="Image description" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Basic data sanitation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We had to take into account the possibility that somebody could add random sentences  into their Brewfile or generally corrupt the file (since it is just a plain .txt) before uploading it to our site. As a result, we checked for a format of  followed by  and sanitized the strings of extra quotes and spaces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs1sccc1vejn3iq1p600g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs1sccc1vejn3iq1p600g.png" alt="Image description" width="800" height="700"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Client-side validation
The reason that the CLI logic hits the API endpoint on the website and not Firebase directly is because we wanted to do some client-side validation before uploading the data to the database. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, we check if this user already has a Brewfile that’s been uploaded. If yes, then we will update the existing Brewfile with the incoming data so we can ensure that 1 user will only ever have 1 uploaded Brewfile on our site.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Database/Server-side validation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Firebase implements security rules to lock down read/write access to your documents, but validating the content of your data is less robust. We decided to approach validation in two ways to provide some basic safety for our data. &lt;/p&gt;

&lt;p&gt;First, we provided some basic rules for reading and writing to our documents in Firestore rules. While Firebase recently started offering an early-access PostgreSQL database solution, we used its traditional document collections. The security rules ensure any document in the collection follows a basic structure with only a particular set of keys, but you can’t easily ensure the shape of your data.&lt;/p&gt;

&lt;p&gt;Secondly, due to some of the limitations of verification in Firestore, we decided to provide more structured validation when fetching documents in our server-side API endpoints. The isValidBrewfile function offers four validating options, one for each piece of data associated with a brewfile. By default, all four options are enabled yet for any particular validation call we can turn off a setting to skip that validation when needed.&lt;/p&gt;

&lt;p&gt;Since so much of the UX depends on valid data, this two-tiered validation system means we can trust the data when processing Brewfiles. It’s not iron-clad validation, but good enough for our purposes.&lt;/p&gt;

&lt;h2&gt;
  
  
  User experience decisions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqho9lm3t72npus9x0cgo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqho9lm3t72npus9x0cgo.png" alt="Image description" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Overall UI/UX
&lt;/h3&gt;

&lt;p&gt;When thinking of the design, we considered a few things.&lt;/p&gt;

&lt;p&gt;First, we are trying to bring some “pizzazz” into Brewfiles, which are normally just normal, boring, black-and-white .txt files. For that reason, the UI should feel cool, sleek, and modern. &lt;/p&gt;

&lt;p&gt;Second, package lists are very text-heavy, meaning that there won’t be many opportunities to show off graphics/images on this website. That’s why we chose an in-your-face font and a bold-blue color scheme that would catch people’s eyes.&lt;/p&gt;

&lt;p&gt;We didn’t have design resources when first starting this project, so one of our engineers decided to try mocking up some initial designs. Luckily, the UI you see today was created by Kyle, one of our very talented designers - but we thought it would be funny to show you some of the initial mockups: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi55o5caefylch73vy0ed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi55o5caefylch73vy0ed.png" alt="Image description" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbrkjzzrvrw3wtdwcqu6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdbrkjzzrvrw3wtdwcqu6.png" alt="Image description" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijd1wex389yx3brl2mq1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fijd1wex389yx3brl2mq1.png" alt="Image description" width="800" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Brewfile Cards
&lt;/h3&gt;

&lt;p&gt;We had some interesting conversations around the design of our Brewfile card. Originally, we had coded our cards to look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbw2bj94rz0yncszitvo4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbw2bj94rz0yncszitvo4.png" alt="Image description" width="494" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Where the card would show the first 7 packages in the developer’s Brewfile, and highlight packages when a certain keyword was being searched. However, we realized this wouldn’t work if the keyword matched with multiple packages within the Brewfile. For example, just the letter “a” would likely match with many packages. This is why product designers are important! Sadly, we had to rethink the UI for the card, which led to some backtracking in terms of our code and implementation. Here were the options we had come up with:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh6y5gobead703yodz9hu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh6y5gobead703yodz9hu.png" alt="Image description" width="800" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And you can check out the &lt;a href="https://www.brewfiles.com/brewfiles"&gt;“All Brewfiles”&lt;/a&gt; page to see which option we decided on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Product decisions + product philosophy
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F67wcrmqf3lzh28h2i3uw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F67wcrmqf3lzh28h2i3uw.png" alt="Image description" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A large part of this project was deciding what features to build. For example, we could have doubled-down on features that analyzed the packages within Brewfiles and gave developers more extensive statistics about the tools they download. We could have also changed the UI to only show the top 10 uploaded packages and only updated it once per week, to encourage people to come back to see the new leaderboard. &lt;/p&gt;

&lt;p&gt;The leading philosophy behind the features we decided to build was education and social fun. We wanted this website to be a place where developers could discover new tools, which is why we have a complete leaderboard of every single package that has been uploaded, as well as different filters to sort by different categories. We also wanted to create a sense of community, which is why we required developers to connect a Github account with their Brewfile and added a search functionality. And our “developer personality” summary added a little bit of fun to the whole experience, and encouraged developers to share their Brewfiles out with the world.&lt;/p&gt;

&lt;h3&gt;
  
  
  Discovering new tech/frameworks
&lt;/h3&gt;

&lt;p&gt;Container queries have been stable in modern browsers since Firefox added them in February 2023 and they &lt;a href="https://caniuse.com/?search=container-type"&gt;currently are supported in 90% of browsers used globally&lt;/a&gt;. In short, container queries provide control over styling decisions based on the size of a parent container (instead of &lt;a class="mentioned-user" href="https://dev.to/media"&gt;@media&lt;/a&gt; queries which are based on the size of the viewport). &lt;/p&gt;

&lt;p&gt;The cards on the /brewfiles route change sizes as the viewport changes, but consistently with the viewport. At first, the cards take up the full viewport width, continuing to increase until 768px, where the list moves to a two-column layout.&lt;/p&gt;

&lt;p&gt;Once again, the cards expand until displaying in three columns at 1280px. A simple &lt;a class="mentioned-user" href="https://dev.to/media"&gt;@media&lt;/a&gt; query would only adjust the typography and other UI elements in a straight line, getting larger with the viewport. However, with container queries, we could adjust all the elements based on the intrinsic size of the cards creating a truly responsive interface! &lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk7ir3n6ny56kj84yv3ez.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk7ir3n6ny56kj84yv3ez.png" alt="Image description" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Future features
&lt;/h3&gt;

&lt;p&gt;Before we end, here are some of the features/improvements we wanted to build, but didn’t get to before we launched. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;“Annotating” packages&lt;/strong&gt;&lt;br&gt;
What if developers could “star” certain packages as ones they used daily or deserved special attention? Or flag other tools as deprecated?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Template Brewfiles&lt;/strong&gt;&lt;br&gt;
What if we had Brewfile templates for a specific project/project type? For example, if I am a frontend dev setting up a brand new MacOS and can run 1 command to download all packages from a specific template Brewfile.&lt;/p&gt;

&lt;h3&gt;
  
  
  Closing remarks
&lt;/h3&gt;

&lt;p&gt;We had so much fun creating this tool. It has been such a rewarding experience taking such a boring, black-and-white Brewfile .txt and turning it into a cool, fun, and social experience for the developer community. If you’d like to try it out, here are 2 things you can do.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;npx share-brewfiles&lt;/code&gt; in your CLI and follow the instructions to upload your Brewfile.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to your &lt;a href="//www.brewfiles.com"&gt;Brewfile page&lt;/a&gt;, generate your developer personality, and share it on social media!&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And HUGE thank you again to &lt;a href="//warp.dev"&gt;Warp&lt;/a&gt; for encouraging me to build this project.&lt;/p&gt;

&lt;p&gt;Thank you!&lt;/p&gt;

</description>
      <category>astro</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
    </item>
    <item>
      <title>How To Change Your Zoom Background With Code</title>
      <dc:creator>Jessica Wang</dc:creator>
      <pubDate>Tue, 09 Jan 2024 05:51:26 +0000</pubDate>
      <link>https://forem.com/therubberduckiee/a-very-engineer-y-hack-to-using-zoom-4n08</link>
      <guid>https://forem.com/therubberduckiee/a-very-engineer-y-hack-to-using-zoom-4n08</guid>
      <description>&lt;p&gt;In this blog, I'm going to share a &lt;a href="https://github.com/theRubberDuckiee/CLI-automations/blob/main/zoom_background.sh"&gt;script&lt;/a&gt; you can use to change your Zoom background straight from your command line. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vljFSYDF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6w4y5d0vh4okgw4ssiqz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vljFSYDF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6w4y5d0vh4okgw4ssiqz.gif" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Context
&lt;/h1&gt;

&lt;p&gt;So, I work remote at &lt;a href="https://warp.dev"&gt;this company&lt;/a&gt;, and part of our work culture is that we set special Zoom backgrounds for birthdays, work anniversaries, and other special events. Here's an example from 2024:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V1N3qTCI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v85ukywq9wyw8pahc139.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V1N3qTCI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v85ukywq9wyw8pahc139.png" alt="Image description" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, I might possibly be the only person in the world who is impatient enough to feel this, but I &lt;strong&gt;hate&lt;/strong&gt; having to go into Zoom to manually upload and change my custom background.&lt;/p&gt;

&lt;p&gt;You have to go into your settings for virtual backgrounds, THEN click on the option to upload an image, THEN fumble around in finder to grab the right image to upload to Zoom.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WeMpU6S6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2itw2g55uyriz1dm8xcn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WeMpU6S6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2itw2g55uyriz1dm8xcn.png" alt="Image description" width="800" height="775"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Preferred Solution
&lt;/h2&gt;

&lt;p&gt;Instead of doing it manually, I wrote a script in bash that would automate this manual work for you. Here is the script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

ZOOMPATH={{zoom_path}}
UUID=$(find "{{zoom_path}}" -type f -not -name ".*" | head -n1 | awk -F: '{print $1}')
RECENTFILE=$(find "{{downloads_path}}" -type f -exec stat -f "%m %N" {} \; | sort -rn | head -n1 | awk -F/ '{print $NF}')
BGPATH="{{downloads_path}}/$RECENTFILE"

echo "Moving $BGPATH to $UUID"
cp -R "$BGPATH" "$UUID"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This script is basically taking your most recently downloaded file and replacing whatever default virtual background you have in Zoom with that downloaded file. &lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;downloads_path&lt;/code&gt; is most likely something like &lt;code&gt;/Users/&amp;lt;your_username&amp;gt;/Downloads&lt;/code&gt; or &lt;code&gt;$HOME/Downloads&lt;/code&gt;, but I trust you'll be able to find that path yourself if you moved around your Downloads folder.&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;zoom_path&lt;/code&gt; can be found by running &lt;code&gt;mdfind -name "VirtualBkgnd_Custom"&lt;/code&gt;. If it helps, my path was &lt;code&gt;$HOME/Library/Application Support/zoom.us/data/VirtualBkgnd_Custom&lt;/code&gt;. When you run the command, you'll get an output that looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dR-bqvXV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/403q0ntx014n7nt44dd5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dR-bqvXV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/403q0ntx014n7nt44dd5.png" alt="Image description" width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Making It Even Easier
&lt;/h2&gt;

&lt;p&gt;After writing the script, I wanted to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Save this script somewhere so I didn't have to rewrite it every time.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make this widely accessible for the rest of my team so they could enjoy this automation as well.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gqKqOHWT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebhl82ahpveskk8bsej7.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gqKqOHWT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ebhl82ahpveskk8bsej7.jpeg" alt="Image description" width="548" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To do this, I created a new workflow in my Warp terminal and pasted in my script and pressed autofill. For those of you who aren't familiar, &lt;a href="https://docs.warp.dev/features/warp-drive/workflows"&gt;workflows&lt;/a&gt; are kind of like aliases that you have in your shell config (.zshrc file, for example) in the sense that it gives you a way to quickly run complex &amp;amp; hard-to-remember commands. I like workflows slightly better because I can share the workflow with my team (if I prefer) and it also auto-parameterizes my command/script so I can vary things like paths, secrets, or more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9rUt4fI8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hxlx0zwjiupc6c3z6tef.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9rUt4fI8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hxlx0zwjiupc6c3z6tef.gif" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you're curious about the terminal I'm referring to above, &lt;a href="https://warp.dev"&gt;Warp&lt;/a&gt; is a Rust-based terminal with AI built in. I like it because it has things like autocompletions, history search, click-to-edit, and theming out-of-the-box. Feels super modern compared to the default Mac terminal or iTerm2. And if you do want to try it out, use my &lt;a href="https://app.warp.dev/referral/7MPKYN"&gt;referral link&lt;/a&gt; &amp;amp; get a free theme. (I work there.)&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;And finally, if you want to see 1-minute demo of this Zoom-background-changing-script in action, you can check this Loom out. Thanks for reading!&lt;/p&gt;


&lt;div&gt;
  &lt;iframe src="https://loom.com/embed/5203b971abe5488fa69b22c795ec0de1"&gt;
  &lt;/iframe&gt;
&lt;/div&gt;



</description>
      <category>coding</category>
      <category>automation</category>
      <category>bash</category>
      <category>script</category>
    </item>
    <item>
      <title>Explaining the Fundamentals of Web Dev</title>
      <dc:creator>Jessica Wang</dc:creator>
      <pubDate>Mon, 18 Dec 2023 23:06:07 +0000</pubDate>
      <link>https://forem.com/therubberduckiee/explaining-web-dev-to-my-uber-driver-1lc7</link>
      <guid>https://forem.com/therubberduckiee/explaining-web-dev-to-my-uber-driver-1lc7</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;A couple years ago, I was working as a software engineer at Microsoft OneNote. I was placed on a frontend team and asked to start coding features in React, Typescript, and Redux. I knew nothing about these technologies, but ended up “learning-while-working”. It was relatively effective - I learned enough that after quitting Microsoft, I was able to secure another job as a frontend developer at DoorDash, even making a higher salary. &lt;/p&gt;

&lt;p&gt;But, there was a downside to it. I never really took the time to learn fundamentals. For example, I knew how to build buttons using HTML, but couldn’t conceptualize how that a browser used that to render a webpage. I knew how to call the &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useReducer&lt;/code&gt; methods, but didn’t understand when to use them in a way that complied with React’s philosophy. As a result, it was harder for me to debug issues because I didn’t have enough knowledge to understand the fundamental problem. My code was sloppy and led to more bugs. And it took me longer to read documentation since I had gaps in my understanding. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tp7R4Dyx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5v1dari7q2ckvs0wzfy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tp7R4Dyx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m5v1dari7q2ckvs0wzfy.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This point of this blog is to explain the fundamentals of web dev from the basics, as if you were my Uber Driver. Let's get started!&lt;/p&gt;

&lt;h2&gt;
  
  
  What IS web development?
&lt;/h2&gt;

&lt;p&gt;Web development is the process of building and maintaining websites or web applications. Think of “websites” like Wikipedia, company homepages, or personal blogs because they are mostly serving content in a static way. Think of “web applications” like TikTok, Google Docs, and Slack which perform many different actions based on how the user interacts with it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Frontend vs Backend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A webpage can be broken down into 2 main components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sL2R2mJF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7svi8uuv2f4x72mu26zo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sL2R2mJF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7svi8uuv2f4x72mu26zo.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The client&lt;br&gt;
For a website, the client is the web browser (like Chrome or Safari). It renders everything that the user sees and interacts with, like text on the screen or buttons.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The server&lt;br&gt;
The server is a powerful computer (it could be the laptop sitting on your desk, or it could be a $1000 supercomputer sitting in Texas, depending) that stores data. The reason why it’s called a server is because it has been designated to be a centralized machine that manages a lot of resources at once (and has the computing power to do so).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The client is always requesting information from the server, and the server sends that information back to the client. For example, let’s say we have an online bookstore and I click on a button to get info on “Harry Potter”. The client (my browser) will need to ask the server (wherever it is) to get that information. The server could fetch the title, author, description, and availability of the book. It could even do some logic to check related books to recommend to the user. It will then send that information back to the client. Once that information is received, I’ll see it displayed all pretty on my screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fr_r01zV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7anfc838hyjprxgdrlbl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fr_r01zV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7anfc838hyjprxgdrlbl.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because of this model, we also have two types of roles when it comes to developer jobs (makes sense, right?)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Frontend engineers work on the client-side, and create the visible part of the website like layout, user interface, and interactivity. Their main technologies are HTML, CSS, and Javascript.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Backend engineers work on the sever-side, usually interacting with the server, databases and application logic. Their main technologies are Node.js, Python, Ruby, MySQL, and MongoDB.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TjSoBA6Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/08c6d8j7lxfu09wu0l3z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TjSoBA6Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/08c6d8j7lxfu09wu0l3z.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, you also got the cool kids (or just people who’ve been working long enough in the industry) who work on both frontend and backend. We call those folks full stack engineers. &lt;/p&gt;

&lt;p&gt;Now let’s dive into some of the fundamental basics of frontend development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;HTML, CSS, and DOM Explained&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s say you’re trying to build out a web page.&lt;/p&gt;

&lt;p&gt;In a perfect world, I would be able to tell Chrome “Hey, I want you to put a big blue button in the middle of the page and text centered in the middle saying ‘Click Me’”. But Chrome wouldn’t understand what the f*ck I was saying.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Er9rD67y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pg7kjr5yrmncgltc6j4c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Er9rD67y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pg7kjr5yrmncgltc6j4c.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s say the webpage you’re trying to build is like building a house.&lt;/p&gt;

&lt;p&gt;HTML elements are building blocks like rooms, doors, and windows that you’d need to arrange when you’re planning the architecture. Each component will have it’s own little symbol so you can grok it quickly in a drawing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5kicm6g1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ddqy2keyho915w8jodp2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5kicm6g1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ddqy2keyho915w8jodp2.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In real life, HTML elements are building blocks like links, images, paragraphs, and headers that you’d need to arrange to build a unique webpage. Each HTML element has its own denotation. A paragraph is represented by &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;, image is represented by &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt;, and bullet point list is presented by &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MEjKy7bE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wfale09uoptlejcp3gpy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MEjKy7bE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wfale09uoptlejcp3gpy.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;HTML is like the blueprint of the house. It outlines where the the components like rooms, doors, and windows should be placed. For example, you want a bigger window on the east side of the building to let more sunlight through in the morning, and a door between the living room and bedroom for privacy. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--awgfR6mW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4w3ushn3srl06p1hnocy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--awgfR6mW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4w3ushn3srl06p1hnocy.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In real life, HTML would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;header&amp;gt;
&amp;lt;h1&amp;gt;Website Header&amp;lt;/h1&amp;gt;
&amp;lt;/header&amp;gt;

&amp;lt;nav&amp;gt;
&amp;lt;ul&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href="#home"&amp;gt;Home&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;!-- ... --&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;/nav&amp;gt;

&amp;lt;main&amp;gt;
&amp;lt;article&amp;gt;
&amp;lt;h2&amp;gt;Article Title&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;Content of the article.&amp;lt;/p&amp;gt;
&amp;lt;/article&amp;gt;

&amp;lt;section&amp;gt;
&amp;lt;h2&amp;gt;Section Title&amp;lt;/h2&amp;gt;
&amp;lt;p&amp;gt;Content of the section.&amp;lt;/p&amp;gt;
&amp;lt;/section&amp;gt;
&amp;lt;/main&amp;gt;

&amp;lt;aside&amp;gt;
&amp;lt;p&amp;gt;Additional information or related content.&amp;lt;/p&amp;gt;
&amp;lt;/aside&amp;gt;

&amp;lt;footer&amp;gt;
&amp;lt;p&amp;gt;© 2023 Your Website&amp;lt;/p&amp;gt;
&amp;lt;/footer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where it arranges our HTML elements (like &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;) into a specific configuration for the website we are trying to build. Similar to the blueprint of the house.&lt;/p&gt;

&lt;p&gt;CSS is the interior design plan that compliments the HTML blueprint. It specifies how each room should be colored, what type of flooring should be used, and what styling should go on the doors and windows.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_j8sYTGH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zpmi5m74wav4u9zf0fp1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_j8sYTGH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zpmi5m74wav4u9zf0fp1.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In real life, CSS could look like this, where there’s specifications for color, font, and padding to stylize each unique HTML element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/* Apply a background color and text color to the body */
body {
  background-color: #f4f4f4;
  color: #333;
  font-family: 'Arial', sans-serif;
}

/* Style the header */
header {
  background-color: #3498db;
  color: #fff;
  padding: 1em;
  text-align: center;
}

/* Style the navigation menu */
nav {
  background-color: #2c3e50;
  padding: 0.5em;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, DOM is like the actual constructed house that is based off the blueprint, with all the rooms, doors, and windows now tangibly built according to the HTML. In real life, here’s what the DOM to the HTML above would look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;|── html
│   ├── head
│   │   └── (head contents)
│   └── body
│       ├── header
│       │   └── h1
│       │       └── Website Header
│       ├── nav
│       │   └── ul
│       │       └── li
│       │           └── a (href="#home")
│       │               └── Home
│       ├── main
│       │   ├── article
│       │   │   ├── h2
│       │   │   │   └── Article Title
│       │   │   └── p
│       │   │       └── Content of the article.
│       │   └── section
│       │       ├── h2
│       │       │   └── Section Title
│       │       └── p
│       │           └── Content of the section.
│       ├── aside
│       │   └── p
│       │       └── Additional information or related content.
│       └── footer
│           └── p
│               └── © 2023 Your Website
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just like a fully built house is a 3D version of a blueprint, the DOM brings the HTML hierarchy to life. You can more clearly see the concept of parent and children elements here. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;(By the way, the reason why developers don’t write directly in the DOM is because it’s pretty inefficient. HTML is more human readable (though honestly after fighting with HTML I’m not so sure about this statement) and directly manipulating the DOM can lead to performance issues.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To put it all together, when a webpage is rendered, it first parses the HTML (that you coded) and creates the DOM to represent the structure of the webpage. Then it parses the CSS associated with the HTML document to understand color, font-size, layout, and positioning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Javascript Explained&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With just HTML and CSS, your webpage may look good but it’s static and has no interactive bells &amp;amp; whistles. That’s where Javascript comes in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c18pcOKw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ve7i91tu3q3ma32ye982.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c18pcOKw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ve7i91tu3q3ma32ye982.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Javascript brings life and interactivity into your webpage. Using the house-building metaphor from before, Javascript is like smart-home automation. For example, it can do things like controlling lights in different rooms, having a virtual doorbell ring, and setting up a your coffee maker so it starts brewing coffee at 8am every morning.&lt;/p&gt;

&lt;p&gt;In real life, here are some of the things Javascript can do (no fun little drawings here, now you get some real life examples in gif form):&lt;/p&gt;

&lt;p&gt;1: Event handling&lt;br&gt;
Responding to clicks, mouse movements keyboard inputs. For example, having more information about a restaurant appear when you click “see more” on Yelp. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PpgfFoYZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0awgek2n585fg0jk0as0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PpgfFoYZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0awgek2n585fg0jk0as0.gif" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2: Form validation&lt;br&gt;
Validate form inputs on the client side before submitting the data to the server. For example, Gmail including a password strength indicator to guide users in creating strong passwords with visual cues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kvGT148a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kov3kbkbsacchgkkw7s1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kvGT148a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kov3kbkbsacchgkkw7s1.gif" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3: Animations and transitions&lt;br&gt;
Fading, sliding, moving elements. For example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Pe_bSYNh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vk52paj0eeg38qn728fy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Pe_bSYNh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vk52paj0eeg38qn728fy.gif" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4: Dynamic styling&lt;br&gt;
Changing colors, sizes, or positions based on user actions/conditions. For example, the fact that a link is colored blue when you’ve never clicked on it before, but colored purple if you’ve clicked on it before. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--l6d59kNx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/br1czzsqosj9tt4n8anr.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--l6d59kNx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/br1czzsqosj9tt4n8anr.gif" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5: Error handling&lt;br&gt;
Javascript can catch and handle errors, providing a more graceful user experience by displaying informative error messages. For example, Bank of America might show you a friendly message saying “You were logged out due to inactivity. Please log in again or contact customer service at this number” instead of just crashing the whole webpage.&lt;/p&gt;

&lt;p&gt;6: Geolocation&lt;br&gt;
Enabling location-based services. For example, Google maps reading your current location to calculate travel times to a destination.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vEf1RMrO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w345y7vnkp8osxgvxi5h.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vEf1RMrO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w345y7vnkp8osxgvxi5h.gif" alt="Image description" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;7: Dynamic updates via websockets&lt;br&gt;
Websocket connections are real-time communications between the client and the server, leading to quick dynamic updates. For example, Slack uses WebSocket connections to enable instant messaging and notifications.&lt;/p&gt;

&lt;p&gt;8: Integration with third-party apps&lt;br&gt;
Javascript can interact with third-party APIs, allowing developers to integrate external services. For example, travel booking platforms accessing the Google Maps API to display locations of hotels and attractions near you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Other Popular Frontend Frameworks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In no particular order, here are some other popular frontend frameworks that are worth knowing. If you want to dive deep into them, I would recommend researching it more yourself. But this list is mostly just so you’ll be familiar when you hear it at work/see it in a blog.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XabYjS6h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t6g0nvw90k16l4x13hw7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XabYjS6h--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t6g0nvw90k16l4x13hw7.png" alt="Image description" width="800" height="525"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React: A Javascript library that allows developers to create reusable UI components and manage the state of an application. I strongly recommend you start taking a React tutorial and build a project using React if you’re looking for your first job in web dev.&lt;/li&gt;
&lt;li&gt;Angular: A Javascript framework for building dynamic web appliactions that follows the MVC architecture.&lt;/li&gt;
&lt;li&gt;Vue.js: A Javascript framework meant to be very adaptable and flexible.&lt;/li&gt;
&lt;li&gt;Next.js: A React-based web framework that focuses on ease of development and optimal performance.&lt;/li&gt;
&lt;li&gt;Ruby on Rails: A framework that emphasizes convention over configuration and provides a lot of defaults for building web applications.&lt;/li&gt;
&lt;li&gt;Svelte: A Javascript framework that compiles components at build time rather than relying on a runtime framework in the browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Wrapping up frontend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Frontend engineers should have a basic understanding of how UI on a webpage is generated and displayed through HTML, CSS, DOM, and Javascript.&lt;/p&gt;

&lt;p&gt;Commonly, they also learn libraries created on top of these fundamentals, like Typescript React, Redux, Next.js, and more. &lt;/p&gt;

&lt;p&gt;On top of that, more senior frontend engineers are familiar with complex implementation of responsive web design, taking into account design for mobile devices, cost effectiveness when implement code/architecture for a website, accessibility, and SEO (search engine optimization).&lt;/p&gt;
&lt;h2&gt;
  
  
  Backend
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lhCBfN-F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fmu3sf9rh8kb6mjllpqf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lhCBfN-F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fmu3sf9rh8kb6mjllpqf.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What happens on the backend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The backend is responsible for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Handling requests from the client (frontend).&lt;/li&gt;
&lt;li&gt;Interacting with the database to get/store/modify data.&lt;/li&gt;
&lt;li&gt;Executing any logic or algorithms needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here are some real-life examples of how the backend works with frontend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;User authentication
Imagine a user trying to login to an online banking application. The client collects the users’ username/password and sends them to the server. The server verifies the credentials agains user data stored in the database. If the credentials are valid,  the backend generates a token and sends it to the client so it can give the user access to the online bank.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VEx7GL6o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bhq1d4l7uthlvtdh3vx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VEx7GL6o--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bhq1d4l7uthlvtdh3vx1.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;File upload
Imagine a user uploads an image to a social media platform. The client allows the user to select an image, then sends it to the server. The server will receive the image, do some logic to validate the format and size, and store it on the server or the cloud. It could also update the database with information about the image.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wRtbNkAD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1b265ftqrwm8pteldm8a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wRtbNkAD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1b265ftqrwm8pteldm8a.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;E-commerce transaction
Imagine a user makes a purchase on an e-commerce website. After the user proceeds to checkout, the client sends the order details to the server. The backend does business logic like check product availability, calculates total cost, and processes the payment. It updates the inventory, generates an order confirmation, and sends it back to the frontend to display to the user.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--A-kNNbxT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nsdhs1xysc3surgbyj3m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A-kNNbxT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nsdhs1xysc3surgbyj3m.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What might a backend engineer work on&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;1: Writing APIs to allow frontend to communicate with backend. We’ll talk more about APIs later in the section labelled “How frontend and backend communicate”.&lt;/p&gt;

&lt;p&gt;2: Manage database to store and retrieve data efficiently, like defining database schemas and creating tables. For example, a backend engineer may create a new table using this SQL command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CREATE TABLE users (
user_id SERIAL PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which creates a table of all the applications’ users, with their user_id, their username, email, and the timestamp when that user was created.&lt;/p&gt;

&lt;p&gt;3: Add middleware like logging, authentication, and caching.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pcMAGeLU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3api9owo46z5kl3k01ad.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pcMAGeLU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3api9owo46z5kl3k01ad.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4: Implementing strategies to scale backend infrastructure to handle increased traffic, like load balancing and horizontal scaling.&lt;/p&gt;

&lt;p&gt;5: Implementing security measures to protect sensitive data, like encryption algorithms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend technologies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In no particular order, here are some popular backend frameworks that are worth knowing. If you want to dive deep into them, I would recommend researching it more yourself. But this list is mostly just so you’ll be familiar when you hear it at work or see it in a blog.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Node.js: Allows developers to use Javascript for both client and server-side scripting.&lt;/li&gt;
&lt;li&gt;Python: A very versatile programming language used in web dev for its readability and efficiency.&lt;/li&gt;
&lt;li&gt;Java: General-purpose language used mainly for building enterprise-level applications.&lt;/li&gt;
&lt;li&gt;.NET (C#): a framework used by Microsoft used to create dynamic and scalable applications.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VgBDGLqN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l5aivodt2tf6oh0n151m.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VgBDGLqN--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l5aivodt2tf6oh0n151m.jpg" alt="Image description" width="800" height="514"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;How frontend and backend communicate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine if the client could just message the backend server and be like “Yo, send me info about Harry Potter ASAP!” and backend server was like “Bet.” Unfortunately, not how computers work in real life. As always, there’s a set of rules defining how messages are formatted and transmitted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTP Requests&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;HTTP requests is a message sent by the client to a server, and is the foundation of data communication. Let’s dive into the main moving parts that go into building a HTTP request.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--e8TGQnXo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmjs52coadqrie5hjg2t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--e8TGQnXo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmjs52coadqrie5hjg2t.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;1: Methods&lt;br&gt;
These are actions you can request from the server. Some of the main ones include:&lt;/p&gt;

&lt;p&gt;GET: Gets data from the server&lt;br&gt;
(For example, let’s say we make an HTTP GET request to &lt;a href="https://example.com"&gt;https://example.com&lt;/a&gt;. You can do this yourself by running &lt;code&gt;curl https://www.example.com&lt;/code&gt; in your terminal.)&lt;br&gt;
POST: Submits data to the server.&lt;br&gt;
DELETE: Deletes a resource from the server.&lt;br&gt;
PATCH: Modifies a resource on the server&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Remember, this is an article about fundamental knowledge. I only put definitions here so you can get a sense of each method, but no need to memorize them now.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;2: Request URL&lt;br&gt;
This is the URL I’m sending this HTTP request to. For example, I could do GET &lt;a href="https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=Twitter"&gt;https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=&lt;/a&gt;Elon Musk to retrieve tweets that Elon Musk posts.&lt;/p&gt;

&lt;p&gt;3: Request Headers&lt;br&gt;
These are just additional information about the request. For example, I could add something like&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authorization: MY_ACCESS_TOKEN to indicate I have access.
or&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Accept: application/json&lt;/code&gt; to indicate that I prefer to receive the response in JSON format.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4: Request Body&lt;br&gt;
For some actions like POST or PUT where I’m modifying information on the server, the request body contains the data I need to make that update. For example, if I did PUT &lt;a href="https://en.wikipedia.org/w/api.php?action=edit&amp;amp;title=Godzilla"&gt;https://en.wikipedia.org/w/api.php?action=edit&amp;amp;title=Godzilla&lt;/a&gt; to update the Wikipedia page on Godzilla, I’d include information in the request body on with the update, like saying Godzilla is 393 feet tall.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;APIs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--y51dbk53--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xzzf0azp0k6x7okvtu85.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--y51dbk53--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xzzf0azp0k6x7okvtu85.png" alt="Image description" width="800" height="571"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another fundamental aspect of communication between frontend and backend is API, which allow different software applications to communicate with each other. The most famous APIs are Google Maps API, Twitter API, Facebook Graph API, OpenWeatherMap API, Youtube API, Github API, and Stripe API. Two good examples are ones I used just above, where I referenced hitting request URLs like &lt;a href="https://en.wikipedia.org/w/api.php?action=edit&amp;amp;title=Godzilla"&gt;https://en.wikipedia.org/w/api.php?action=edit&amp;amp;title=Godzilla&lt;/a&gt; and &lt;a href="https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=Twitter"&gt;https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=&lt;/a&gt;Elon Musk. These APIs allows you to get information about tweets or even lets you update Wikipedia pages. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This is not necessarily an article that teaches you how to get a job in web dev (it’s also not NOT an article that’ll do that), but primarily more for a reader that, as I said, wants to build more of a foundational, conceptual understanding of some of these concepts.&lt;/p&gt;

&lt;p&gt;If you want to see the origin story of "Explaining ___ to my Uber Driver series", check out the intro paragraph to my blog "&lt;a href="https://dev.to/therubberduckiee/explaining-kubernetes-to-my-uber-driver-4f60"&gt;Explaining Kubernetes to by Uber Driver&lt;/a&gt;". &lt;/p&gt;

&lt;p&gt;Where do I currently work? I'm enjoying life as a developer advocate at a startup called Warp. We're building a new command line experience that makes people better developers because the UI is fast and intuitive. Check it out &lt;a href="//warp.dev"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--OYKnAE0H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0fq4wytrnmedkmsa6rze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--OYKnAE0H--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0fq4wytrnmedkmsa6rze.png" alt="Image description" width="800" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And finally, as always, please leave comments below. Any feedback on my drawing, factualness of whatever I've included in this blog, and be nice.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>frontend</category>
      <category>backend</category>
    </item>
    <item>
      <title>Explaining SSH to my Uber Driver</title>
      <dc:creator>Jessica Wang</dc:creator>
      <pubDate>Mon, 27 Nov 2023 22:44:12 +0000</pubDate>
      <link>https://forem.com/therubberduckiee/explaining-ssh-to-my-uber-driver-38a</link>
      <guid>https://forem.com/therubberduckiee/explaining-ssh-to-my-uber-driver-38a</guid>
      <description>&lt;p&gt;In this blog, I'd like to explain SSH to you as if you were my Uber driver. The point of this exercise is to pretend like you have no prior experience with SSH or even much knowledge of technology in general, and get you to a point where you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Aren't afraid by the vague term "SSH" because you have a concrete image in your head.&lt;/li&gt;
&lt;li&gt;Understand how industry-level developers use SSH to test code changes, make server configurations, and more.&lt;/li&gt;
&lt;li&gt;Understand how SSH's encryption/authorization protocols make it better than other tools.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;h2&gt;
  
  
  Conceptualizing SSH
&lt;/h2&gt;

&lt;p&gt;What is SSH? Instead of starting with the conventional tech jumbo you'd see at the top of a Cloudflare article or Wikipedia, let me use an analogy to help you conceptualize it. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fbkzvrhuygk1ihvpl8jyw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fbkzvrhuygk1ihvpl8jyw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine you've just gotten off school and you're back home in your bedroom. You're bored, and decide to put on your VR headset to access the Metaverse - an immersive experience that blocks out the physical world and embeds you in a virtual environment. You log in, punching in your username and password to verify that it's you. Once that's done,  you see your ditigal avatar pop up. It looks like a spitting image of you - tall, great hair, and a fantastic sense of style. In front of you, you see a forest - or rather, a digital representation of a forest. Still standing in your bedroom with your VR headset on, you physically move your head to look around, and your avatar does the same. You say "hello forest" in your bedroom, and your avatar also says "hello forest" in the VR world.&lt;/p&gt;

&lt;p&gt;In real life, this is what the analogy translates to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You == developer.&lt;/li&gt;
&lt;li&gt;Your bedroom == your laptop, or whatever local machine you're typing on.&lt;/li&gt;
&lt;li&gt;The Metaverse == a remote machine/server you are trying to access from your local machine.&lt;/li&gt;
&lt;li&gt;The VR headset == the SSH tunnel that allows you to access the remote machine (Metaverse) from your local machine (bedroom).&lt;/li&gt;
&lt;li&gt;Login screen == the SSH key pair that ensures only authorized parties can use the SSH tunnel. I'll explain this more thoroughly in a later section of this blog.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.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%2F5g8qhentz0jzk11q6dsj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F5g8qhentz0jzk11q6dsj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How does this analogy help you? Next time you hear somebody say "I SSH-ed into the server", you don't need to freak out about this vague concept of "SSH". Instead, reword it in your head to mean "I put on a VR headset to access the Metaverse", and this will ground it something more concrete.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In layman's terms, SSH is like a secure, secret tunnel that lets you connect to another computer over the internet. It helps you do things on that computer as if you were sitting right in front of it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Of course, this is just a surface-level, conceptual understanding of SSH. I'm hoping right now, you're thinking of some questions to help you dig more into SSH. I can't read your mind, but here some questions I had, answered for myself, and I think would be useful to explain to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do I need to use SSH?
&lt;/h2&gt;

&lt;p&gt;SSH is important because developers commonly need to access remote machines. For example, large applications that would consume too much memory and computing power on your own measly laptop is hosted on cloud platforms like AWS, Azure, or Google Cloud, which all need to be accessed remotely. Other times, development teams are distributed across different locations, so remote servers allows team members to collaborate and share code in one consistent place even if they aren't physically in the same location. &lt;/p&gt;

&lt;p&gt;Let me walk you through 2 examples of when an industry-level developer would need to SSH into a remote machine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1: Deploying Code&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As a developer, you need to deploy a new version of your website to your staging server. This is how it might work, using SSH.&lt;/p&gt;

&lt;p&gt;On your local machine, you push your latest code changes up using Git.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F5n568y57dn3zc9yse7n1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F5n568y57dn3zc9yse7n1.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you would SSH into the remote server that has your staging environment.&lt;/p&gt;

&lt;p&gt;(By the way, you might be thinking why I need to have my staging environment in a remote server and not on my local machine. Here are some reasons. One is that it's better to simulate a realistic setup, like checking for performance bottlenecks or scalability concerns that would only happen in a server environment and not your laptop. another is simply ensuring that all team members can test in a consistent environment, instead of them all having a staging environment on their own machines or (god forbid), using your local machine whenever they needed to test something.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F8401y49leoaxiglkl08i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F8401y49leoaxiglkl08i.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, you've SSH'ed in the remote server that hosts the staging environment, you can pull your latest code changes from that remote server, run the build, and test your application (by navigating to &lt;a href="http://staging.example.com" rel="noopener noreferrer"&gt;http://staging.example.com&lt;/a&gt;, for example).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2: Updating a server configuration&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's say the web application you're building has grown, and you need to adjust the server configuration to accomodate increased traffic. Here is an example of some commands you might run:&lt;/p&gt;

&lt;p&gt;a. SSH into the server&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ssh user@your-server-ip&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;b. Locate Apache configuration files&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cd /etc/apache2&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;c. Backup the configuration files&lt;/p&gt;

&lt;p&gt;&lt;code&gt;cp apache2.conf apache2.conf.bak&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;d. Edit configuration files. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;vi apache2.conf&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;e. Verify configuration&lt;/p&gt;

&lt;p&gt;&lt;code&gt;apache2ctl configtest&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Please don't feel like you need to know what these commands do.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In all honesty, I asked ChatGPT to generate an example for me, and don't quite understand it myself. The takeaway here is understanding the types of commands a developer might run while SSH'ed into a server. A lot of it is simple commands to move directories (cd), manipulate files (cp), and edit files (vi).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why SSH?
&lt;/h2&gt;

&lt;p&gt;One question I had while learning about SSH were "what are the alternatives to SSH, and why did SSH win out"?&lt;/p&gt;

&lt;p&gt;I guess now is a good time for a quick history lesson.&lt;/p&gt;

&lt;p&gt;Before SSH was widely adopted, there were other procotols for communication between two machines. Some notable ones include Telnet, RSH, FTP, and Rlogin. But each of them suffered from lack of encryption and security features.&lt;/p&gt;

&lt;p&gt;To paint a picture, here's how it might happen. I (the malicious hacker) gain access to the network where Telnet is passing through - the network through which you're trying to send information from your laptop to another machine through Telnet protocols. I use a tool like Wireshark to capture the traffic. Once I get the traffic on my machine in the form of downloaded "packets", I can just click through them and look for any login attempts. All the data is in plaintext (which means it's human-readable and not some garbled symbols), so if I find it, I can literally use the login information I see in front of me. As you can see, these older tools were not very secure!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F6tqaab8kf12bz4089sh8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F6tqaab8kf12bz4089sh8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In 1995, SSH was invented. It was better for 2 main reasons.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Encrypting data&lt;br&gt;
Let's say that part of the data being transmitted from my machine to a remote machine is the string "username: jesswang password: ?IamC00l!". Without encryption (so with older protocols like Telnet or FTP), a hacker would be able to read that string as you just read it. With encryption, the data they get back might look like "H23kLs*^!d8%PwZx0KsL!9B#N%u@i8". In reality, the encrypted data would be a different sequence of bytes that adheres to encrypted algorithmic standards (that I don't know much about), but this is just to give you an idea.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Authentication&lt;br&gt;
Older programs like Telnet relied solely on password-based authentication. When you connect to a remote system using Telnet, the Telnet server prompts you to enter a username and password to login. However, Telnet transmits your passwords in plaintext, making it easy for hackers to read. SSH primarily uses a different (and better) approach called public-key authentication. I'll explain how it works below so you can understand how your information is better protected from hackers.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;First, your information is put in a briefcase, and you put your own lock on it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fpunmm0o96e0ec1q6294u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fpunmm0o96e0ec1q6294u.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You send the briefcase to your remote server. The remote server doesn't have the key to unlock your lock, so instead the remote server puts its own lock on the briefcase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fslbbwmewrjixfy0jirok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fslbbwmewrjixfy0jirok.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The remote server sends the briefcase back to you, and you unlock your lock.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fg3xm8hl2wzqx4q9gq27a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fg3xm8hl2wzqx4q9gq27a.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You send the briefcase back to the remote server, and it unlocks its lock with its key. Then it opens the briefcase, and reads the note.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fsv9jovtg5gqfrr9zs3i9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fsv9jovtg5gqfrr9zs3i9.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's the gist of public-key authentication! By the way, SSH also still &lt;strong&gt;does&lt;/strong&gt; support password-based authentication, but even then the password is encrypted so if a hacker were to intercept it, they would not be able to read it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I use SSH?
&lt;/h2&gt;

&lt;p&gt;Enough of the conceptual learning. Here's the actual commands I would run to SSH into a server.&lt;/p&gt;

&lt;p&gt;To SSH into a server, you would typically run something like: &lt;code&gt;ssh username@server_ip_or_hostname&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You'd replace username with your actual username on the server&lt;br&gt;
You'd replace server_ip_or_hostname with the IP address or hostname of the server. So for example, &lt;code&gt;ssh john@123.456.789.123&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After running the command, you may be prompted for a password for the username of the server. Or if your server uses the key-based authentication we covered in the previous section, you might not be prompted for a password.&lt;/p&gt;

&lt;p&gt;Once you're in, you can start executing commands. These commands will be executed on the remote server you're connected to. &lt;/p&gt;

&lt;p&gt;I understand it's hard to fully learn if you're not physically typing things yourself and seeing what happens. If you already have a remote server for work or personal that you have set up, feel free to give it a try. If you don't have a remote serve to connect to, that's fine. You can follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="http://sdf.org/" rel="noopener noreferrer"&gt;http://sdf.org/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create an account. You'll probably receive an email to giving you a generated password for your account. You may have to wait 1-2 for that email (at least that was my experience).&lt;/li&gt;
&lt;li&gt;You can SSH into the public UNIX system by running &lt;code&gt;ssh &amp;lt;your_username&amp;gt;@sdf.org&lt;/code&gt;. So for example, I made my username poop123. So I ran &lt;code&gt;ssh poop123@sdf.org.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;When prompted, enter the password you got earlier.&lt;/li&gt;
&lt;li&gt;You've successfully SSH-ed into a server! You can run commands like &lt;code&gt;echo 'hello world'&lt;/code&gt; to print out "hello world" or &lt;code&gt;ls&lt;/code&gt; to see a list of files in your current directory.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fncm79f92w68a034ghiup.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fncm79f92w68a034ghiup.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Note To Readers / Conclusion
&lt;/h2&gt;

&lt;p&gt;Though it's relatively easy to SSH into another server (maybe your coworkers give you a command you just paste into your command line), I find it difficult to use the command if I don't understand, at least on a high-level, what is happening when I run it. Hopefully, others can relate with me and this blog will help scratch that itch.&lt;/p&gt;

&lt;p&gt;If you're curious how I came up with the title, the story is in the intro paragraphs of the original blog of this series, &lt;a href="https://dev.to/therubberduckiee/explaining-kubernetes-to-my-uber-driver-4f60"&gt;"Explaining Kubernetes to my Uber Driver"&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;As always, I would love any feedback on the information, drawings, or storytelling in this blog. And let me know what other topics you'd like for me to write about.&lt;/p&gt;

&lt;p&gt;Here are some resources I referenced when writing this blog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ChatGPT&lt;/li&gt;
&lt;li&gt;Beginners Guide to SSH &lt;a href="https://www.youtube.com/watch?v=qWKK_PNHnnA" rel="noopener noreferrer"&gt;Youtube Video&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;My 60 second &lt;a href="https://www.tiktok.com/@therubberduckiee/video/7213401342403005738?lang=en" rel="noopener noreferrer"&gt;TikTok video summary&lt;/a&gt; of the above video.&lt;/li&gt;
&lt;li&gt;I used &lt;a href="https://github.com/charmbracelet/vhs" rel="noopener noreferrer"&gt;Charm VHS&lt;/a&gt; to generate the last gif you saw. Very cool tool that allows you create &amp;amp; easily edit CLI-related demos.&lt;/li&gt;
&lt;li&gt;The terminal I used to film the demo, &lt;a href="https://www.warp.dev/" rel="noopener noreferrer"&gt;Warp&lt;/a&gt; (it's also the company I work at. I highly recommend you check us out).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ssh</category>
      <category>softwareengineering</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>Explaining Kubernetes To My Uber Driver</title>
      <dc:creator>Jessica Wang</dc:creator>
      <pubDate>Sun, 12 Nov 2023 22:04:36 +0000</pubDate>
      <link>https://forem.com/therubberduckiee/explaining-kubernetes-to-my-uber-driver-4f60</link>
      <guid>https://forem.com/therubberduckiee/explaining-kubernetes-to-my-uber-driver-4f60</guid>
      <description>&lt;p&gt;&lt;a href="https://media.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%2Fgjkqs8dct2kxnditkrlt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fgjkqs8dct2kxnditkrlt.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A week ago, I attended Kubecon 2023 in Chicago. I read a few blogs and attended some 101 tutorials at the conference, but still didn’t have a good understanding of the technology. The worst part was the last day of the conference - I ordered an Uber to bring me back to my hotel. My driver asked me “What’s the convention about?” to which I responded “It’s about Kubernetes", but after some explaining, it was clear I had no idea what I was talking about.&lt;/p&gt;

&lt;p&gt;Imagine coming out of a 3 day conference, and not being able to describe the technology to your Uber driver. &lt;em&gt;Facepalm.&lt;/em&gt; So, in an attempt to redeem myself, here’s my reimagined conversation with my Uber driver.&lt;/p&gt;

&lt;h1&gt;
  
  
  Start of Conversation
&lt;/h1&gt;

&lt;p&gt;Me: Imagine you’re a chef at a busy restaurant kitchen. You’ve got a team of chefs working for you, and each one is preparing a different part of the meal - one group for appetizers, one for main courses, and another for desserts. Coordinating these chefs to make sure the meal is served to the customer on time is your job. You have a picture in your head?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fbn0xlqpdw7vw2lcg4a2u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fbn0xlqpdw7vw2lcg4a2u.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Driver: Got it.&lt;/p&gt;

&lt;p&gt;Me: In this scenario, the head chef is Kubernetes. Just like that head chef needs to manage all the different chefs in the kitchen, Kubernetes helps manage all the different pieces needed to run your software. The official definition of Kubernetes is a “container-orchestration tool”, but since the word “container” is pretty abstract here, you can substitute the word “container” for “chef”. So Kubernetes would be a "chef orchestration tool". That way, you can form a picture in your head of your kitchen every time you hear the word.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fkzyqf05ebfv8tn9kkt4i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fkzyqf05ebfv8tn9kkt4i.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Driver: Okay, makes sense so far. But what are these containers? I can't keep imagining them as chefs forever.&lt;/p&gt;

&lt;p&gt;Me: Yeah good point. So now that you have a picture in your head of your Kubernetes kitchen, let’s dive into how all the different kitchen roles map to Kubernetes concepts, going from smallest to largest.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Container&lt;br&gt;
The smallest part of this puzzle is the container, and it’s basically any piece of software. For example, it could be a Node.js web server that hosts a web application, or a MongoDB database container to store data &lt;em&gt;(this sentence is more for the engineers reading this blog, I wouldn’t say this to my Uber driver 😛)&lt;/em&gt;. In the kitchen, imagine you’re serving soup and a salad for your appetizer. The soup would be your container. The salad would also be it’s own container.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I know this definition seems a bit arbitrary right now, but it’ll make more sense once I explain it &lt;strong&gt;in the context&lt;/strong&gt; of the upcoming components.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Foqpypznztr958j5n8ouh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Foqpypznztr958j5n8ouh.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pod&lt;br&gt;
In the kitchen, a pod would be the plate/tray that holds your soup and salad. In Kubernetes, a pod is something that can hold 1 or more containers. The reason for this is that containers within a pod can communicate with each other.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the engineers: as an example, imagine I have a container for a web server and a container for a database in my pod. They can communicate with each other over localhost.&lt;/p&gt;

&lt;p&gt;As for an appropriate analogy with the kitchen, I can’t really think of anything. Imagine some Sausage Party shenanigans where your anthropomorphized soup and salad start chatting to each other. But the soup and salad from the appetizer plate can’t talk to the steak and potatoes on the dinner plate because they are on different plates (aka, different pods don’t share the same network namespace and therefore can’t communicate with each other.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F7fgcm20wc6fajrmju05x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F7fgcm20wc6fajrmju05x.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Master Node&lt;br&gt;
The head chef who manages and oversees the entire kitchen. This is that concept of “container orchestration” or “chef orchestration” we talked about earlier. Some real life examples of the orchestration work that this master node would do would be:&lt;/p&gt;

&lt;p&gt;Scaling, aka adjusting up or down the number of running pods based on CPU utilization. In a busy kitchen, when there’s a surge in customer demand, chefs may need to scale up their operations by preparing more servings of a dish. By the way, one thing to note in this visualization - you’re probably imagining the kitchen hiring in new chefs, but I want you to imagine it more like the current chefs are getting cloned. When scaling happens, pods are essentially being copied.&lt;/p&gt;

&lt;p&gt;Automated deployment, aka defining your application’s dependencies &amp;amp; runtime instructions in a YAML file so it can deploy based off this configuration. In a kitchen, this YAML file is analogous to a written recipe that tells the chef how to make the dish to ensure consistency and efficiency and preparing it. &lt;/p&gt;

&lt;p&gt;Load balancing, aka distributing network traffic across different pods. In kitchen, load balancing involved assigning tasks to different chefs at a cooking station. Maybe Bob at the dessert station is overloaded with requests to scoop ice cream, so the master chef clones Bob and has Bob 2.0 take some of the ice cream orders off Bob 1.0’s hands.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ftxdtcmoblfg5xk8w250x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ftxdtcmoblfg5xk8w250x.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also important to note: each worker node has something called a "kubelet". In the kitchen scenario, "kubelet" would be analogous to the chef at each table. The chef has a bunch of jobs, like making sure the trays of food are being assembled correctly, helping prep ingredients, and throwing away the scraps. Similarly, "kubelet" does things like ensuring containers within pods are running, helps with initializing pods (e.g. have necessary dependencies installed), help with garbage collection, and more. &lt;/p&gt;

&lt;p&gt;Some extra context for the engineers: Kubelet is an open-source, executable binary (aka file with machine code instructions that a CPU can directly execute), written in Go programming language. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F85hvn3eeq4xj6zxc36q6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F85hvn3eeq4xj6zxc36q6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's stop here for a second. If you understand everything I've said up to this point, you understand the basics of Kubernetes architecture! If you don't want to rely on the kitchen imagery forever, I've replaced all the kitchen drawings with only Kubernetes terminology in the diagram below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fz7pg9fgbojmuvjt4q03q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fz7pg9fgbojmuvjt4q03q.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Driver: Actually, all of this makes a lot of sense. So I get what Kubernetes is. But I still don't get why it's useful. Like what's the point of having it or learning about it?&lt;/p&gt;

&lt;p&gt;Me: Yep, the last part of the puzzle is understanding &lt;strong&gt;how&lt;/strong&gt; Kubernetes is used. How do humans interact with Kubernetes? How is Kubernetes relevant/useful in the world of technology? Let's lean back on our kitchen analogy for a second to explain some more concepts.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The owner of the restaurant/franchise is analogous to the software developers that are building out an app or service. At McDonalds, the franchise owner (Let's say their name is Francis Cockadoodledoo) wants get information on how much money each McDonalds location is making, and have the ability to fire/hire employees as needed. To do this, Francis Cockadoodledoo may pick up their phone and call the master chef to get information and give orders. In Kubernetes, software engineers can't really pick up a phone to interact with their Kubernetes cluster, but the “master node” has an API server that you can call, and this allows you access all tasks. For example, engineers can get information on all the pods, nodes, services, understand the health and metrics information, and have the ability to delete or create resources.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The customers eating at the restaurant is analogous to the users of an app or service. Similar to how the Mcdonalds kitchen produces Big Macs for me to eat, the Spotify Kubernetes cluster is providing me the service of listening to a large selection of music from a web browser.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've factored this new information into the drawing I've included below. What you'll see is actually very similar to the diagrams you'll see when you google "Kubernetes architecture". &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fgjkqs8dct2kxnditkrlt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fgjkqs8dct2kxnditkrlt.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F3ens8psibkxhn8f2oepa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F3ens8psibkxhn8f2oepa.png" alt="A diagram of Kubernetes architecture I pulled from the Internet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And of course, I realize there are some abstractions I left out of my explanation because I felt like they were not important in forming a basic mental model of Kubernetes. Feel free to dig in more. As I dig in more myself, I may add to this blog with some links to resources I find useful.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;The reason why I picked this mode of storytelling (describing tech through the lens of a conversation with an Uber driver) is because I wanted to break down Kubernetes into something that was universally understandable and felt approachable.&lt;/p&gt;

&lt;p&gt;Thank you for reading! If you have any &lt;em&gt;constructive&lt;/em&gt; feedback for improving my writing (or my shitty drawings), please leave them in the comments. &lt;/p&gt;

&lt;p&gt;Enjoy this picture of me and my coworker at #Kubecon 2023. We were there to promote the product I work on, BTW. &lt;/p&gt;

&lt;p&gt;If you're interested, you should check it out. It's a modern take on the outdate terminal (command line) experience &amp;amp; makes you a better developer. Go to &lt;a href="https://www.warp.dev/" rel="noopener noreferrer"&gt;https://www.warp.dev/&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F6c73velmiu22gzx7uk63.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F6c73velmiu22gzx7uk63.png" alt="I'm on the left"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>beginners</category>
      <category>architecture</category>
      <category>devops</category>
    </item>
    <item>
      <title>Beginners Guide to ChatGPT API (Let's Build A Clone)</title>
      <dc:creator>Jessica Wang</dc:creator>
      <pubDate>Fri, 20 Oct 2023 20:44:56 +0000</pubDate>
      <link>https://forem.com/therubberduckiee/beginners-guide-to-chatgpt-api-lets-build-a-clone-4804</link>
      <guid>https://forem.com/therubberduckiee/beginners-guide-to-chatgpt-api-lets-build-a-clone-4804</guid>
      <description>&lt;p&gt;Building a personal ChatGPT CLI is super simple! Who knew you could have a whole conversation in the terminal!&lt;/p&gt;

&lt;p&gt;To get started, you’ll need an &lt;a href="https://openai.com" rel="noopener noreferrer"&gt;OpenAI account&lt;/a&gt; and tokens to burn. That's it! We’ll cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install and setup&lt;/li&gt;
&lt;li&gt;Initialize the project&lt;/li&gt;
&lt;li&gt;Build your ChatGPT clone&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you don't like reading, check out the Youtube video instead:&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/7p7kJvckrFE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Install and Setup
&lt;/h2&gt;

&lt;p&gt;First, let’s focus on setting everything up. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I am going to assume you have Python and Home Brew installed, but if not, you can download them at &lt;a href="https://python.org" rel="noopener noreferrer"&gt;https://python.org&lt;/a&gt; and &lt;a href="https://brew.sh/" rel="noopener noreferrer"&gt;https://brew.sh/&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Create a Python environment
&lt;/h3&gt;

&lt;p&gt;Python environments can be confusing, so I’d strongly recommend you use Pyenv. It’s a tool for managing all your Python installations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;pyenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I’m doing this on a mac, but if you’re on a different OS like Windows or Linux, &lt;a href="https://github.com/pyenv/pyenv#getting-pyenv" rel="noopener noreferrer"&gt;follow the instructions on Pyenv’s repo&lt;/a&gt; for your system before continuing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;curl&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//pyenv.run | bash&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s common in software development to create virtual environments to isolate the project and manage its dependencies. We’ll use Pyenv-evirutalenv to create this virtual environment in a moment.&lt;/p&gt;

&lt;p&gt;For macOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;pyenv-virtualenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For linux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;virtualenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Windows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;virtualenv&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nx"&gt;python&lt;/span&gt; &lt;span class="nx"&gt;C&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;Path&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;To&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;Python&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="nx"&gt;python&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exe&lt;/span&gt; &lt;span class="nx"&gt;venv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install dependencies
&lt;/h3&gt;

&lt;p&gt;Next, we’ll need several dependencies for the project. Run this install command to install each of these packages globally on your system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;typer openai python-dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s a brief explanation for each package&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Typer: a tool for writing command line applications in Python&lt;/li&gt;
&lt;li&gt;OpenAI: handles all things OpenAI &lt;/li&gt;
&lt;li&gt;Python-dotenv: a tool for reading and loading from .env files&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Initialize the Project
&lt;/h2&gt;

&lt;p&gt;Start by creating and navigating to an empty directory called “chatgptclone.”&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;mkdir &lt;/span&gt;chatgptclone
&lt;span class="nb"&gt;cd &lt;/span&gt;chatgptclone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, let’s create a virtual environment. Again, virtual environments isolate different Python installations and dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pyenv virtualenv 3.11 chatgptclone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command generates a virtual environment called ChatGPTClone using Python 3.11. If you get an error, run &lt;code&gt;pyenv install 3.11&lt;/code&gt; to install Python 3.11 in Pyenv.&lt;/p&gt;

&lt;p&gt;Now, let’s activate it!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pyenv activate chatgptclone
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build Your ChatGPT Clone
&lt;/h2&gt;

&lt;p&gt;With everything installed and your Python environment activated, it's time to build. We’ll do this in three stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stage 1: Create a basic version&lt;/li&gt;
&lt;li&gt;Stage 2: Add memory to the program&lt;/li&gt;
&lt;li&gt;Stage 3: Improve the CLI experience&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Stage 1: Create a Basic Version
&lt;/h3&gt;

&lt;p&gt;To start, we’ll need two files: &lt;code&gt;main.py&lt;/code&gt; and &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Create a file in your directory called “main.py.”
&lt;/h4&gt;

&lt;p&gt;At the top of the file, import all the dependencies we downloaded earlier along with the os package from Python. Your main.py file should look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;code&gt;os&lt;/code&gt; stands for “operating system” and is a built-in Python package that will help load the API key securely.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  2. Create a .env file to hold the API key.
&lt;/h4&gt;

&lt;p&gt;Login to your OpenAI account, go to API keys, and click on generate a new secret key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/api%2520keys%2520section%2520on%2520openai%2520with%2520create%2520new%2520secret%2520key%2520highlighted.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/api%2520keys%2520section%2520on%2520openai%2520with%2520create%2520new%2520secret%2520key%2520highlighted.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Copy the key that you get to your clipboard. In your .env file, type OPENAI_KEY="&lt;code&gt;YOUR_API_KEY&lt;/code&gt;".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Ensure you have credit balance (under Billing) to make API requests to OpenAI, since each request will require available credits.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  3. Initialize the CLI application.
&lt;/h4&gt;

&lt;p&gt;Next, set the API_KEY for openAI and create an application object called app.in the &lt;code&gt;main.py&lt;/code&gt; file. Your code should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;opanai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Typer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Awesome! We’re all set up, so let’s start making magic happen!&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Create the chat function.
&lt;/h4&gt;

&lt;p&gt;Create a function called &lt;code&gt;interactive_chat&lt;/code&gt; and then welcome your users with the echo command from Typer recognizes commands by using decorators that go on top of a function. Then we just add the classic loop in Python at the bottom. &lt;/p&gt;

&lt;p&gt;Here’s what we have so far:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;openai_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Typer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.command&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;interactive_chat&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Interactive CLI tool to chat with ChatGPT.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting interactive chat with ChatGPT. Type &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; to end the session.&lt;/span&gt;&lt;span class="sh"&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;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Add the chat interaction loop.
&lt;/h3&gt;

&lt;p&gt;Since we’re building a chat application, we’ll need to add an infinite loop within the &lt;code&gt;interactive_chat&lt;/code&gt; function that prompts the user for a chat input and calls the OpenAI ChatCompletion model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;interactive_chat&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Interactive CLI tool to chat with ChatGPT.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting interactive chat with ChatGPT. Type &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; to end the session.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You: &lt;/span&gt;&lt;span class="sh"&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;prompt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ChatGPT: Goodbye!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatCompletion&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="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;prompt&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; if you get a billing error (like &lt;a href="https://app.warp.dev/block/8jIhXM3tDGEiSvC3Jz0duF" rel="noopener noreferrer"&gt;this&lt;/a&gt;), you may have to wait 5-10 minutes for the Billing information you just entered to be recognized by OpenAI. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On success, OpenAI will return a JSON object, which we’ll use to echo back the response message in the CLI application we’re building.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ChatGPT: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s the current &lt;code&gt;interactive_chat&lt;/code&gt; function code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;interactive_chat&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Interactive CLI tool to chat with ChatGPT.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting interactive chat with ChatGPT. Type &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; to end the session.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You: &lt;/span&gt;&lt;span class="sh"&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;prompt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ChatGPT: Goodbye!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatCompletion&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="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ChatGPT: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&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;python main.py&lt;/code&gt; in your terminal and the chatbot will answer your questions!&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 2: Add memory to the program
&lt;/h3&gt;

&lt;p&gt;While working, the chatbot currently has no memory of previous messages.&lt;/p&gt;

&lt;p&gt;Currently, when calling the &lt;code&gt;create&lt;/code&gt; function, we’re passing it an array of messages, which gives ChatGPT context. To give it all our context (and not only the current prompt) let’s make four changes: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Define an empty list called &lt;code&gt;messages&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Append new prompts to the messages list.&lt;/li&gt;
&lt;li&gt;Append the response from OpenAI to the messages list.&lt;/li&gt;
&lt;li&gt;Send the entire messages list to the create function.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s the current &lt;code&gt;interactive_chat&lt;/code&gt; function code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;interactive_chat&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Interactive CLI tool to chat with ChatGPT.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting interactive chat with ChatGPT. Type &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; to end the session.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;prompt&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;prompt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ChatGPT: Goodbye!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

            &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatCompletion&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="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ChatGPT: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&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;python main.py&lt;/code&gt; in your terminal and not your chatbot has a memory! Awesome!&lt;/p&gt;

&lt;h3&gt;
  
  
  Stage 3: Improve the CLI experience.
&lt;/h3&gt;

&lt;p&gt;So, ChatGPT works exactly as we expect now. But… let’s make it even better.&lt;/p&gt;

&lt;p&gt;Right now all of the OpenAI functionality is hard coded in the API call, so you’d have to go into the code to tweak the algorithm. &lt;/p&gt;

&lt;h4&gt;
  
  
  Add Customization parameters.
&lt;/h4&gt;

&lt;p&gt;We can add a lot of customization as parameters in the CLI, like: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;temperature (how creative ChatGPT is)&lt;/li&gt;
&lt;li&gt;max_tokens (how large the responses will be)&lt;/li&gt;
&lt;li&gt;model (which OpenAI model it should use)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can set user parameters with the &lt;code&gt;typer.Option&lt;/code&gt;. Let’s add all three of these parameters in the function definition along with help messages.&lt;/p&gt;

&lt;p&gt;Here’s the current &lt;code&gt;interactive_chat&lt;/code&gt; function definition code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;interactive_chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Control Randomness. Defaults to 0.7&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Control length of response. Defaults to 150&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Control the model to use. Defaults to gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&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;Running &lt;code&gt;python main.py --help&lt;/code&gt; will return options for each parameter. Very professional! &lt;/p&gt;

&lt;p&gt;To use these parameters, change the API call like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatCompletion&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="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;temperature&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;You can now change any of these parameters when running the application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;python&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should receive a response with only 10 tokens, meaning the max-tokens parameter worked!&lt;/p&gt;

&lt;h4&gt;
  
  
  Add your initial question on call.
&lt;/h4&gt;

&lt;p&gt;Let’s make one final change.  Let's add a text parameter that can let us ask our first message immediately with the &lt;code&gt;--text&lt;/code&gt; or &lt;code&gt;-t&lt;/code&gt; flag.&lt;/p&gt;

&lt;p&gt;Here’s the updated &lt;code&gt;interactive_chat&lt;/code&gt; function definition code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;interactive_chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-t&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Start with text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Control Randomness. Defaults to 0.7&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Control length of response. Defaults to 150&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Control the model to use. Defaults to gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&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;Now in the prompt logic, check if there is text that exists. That's it! &lt;/p&gt;

&lt;h2&gt;
  
  
  Final Code!
&lt;/h2&gt;

&lt;p&gt;Just like that, we made a ChatGPT clone!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OPENAI_KEY&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Typer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.command&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;interactive_chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-t&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Start with text&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Control Randomness. Defaults to 0.7&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Control length of response. Defaults to 150&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Option&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Control the model to use. Defaults to gpt-3.5-turbo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Interactive CLI tool to chat with ChatGPT.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Starting interactive chat with ChatGPT. Type &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt; to end the session.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&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;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;
            &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;You&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prompt&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;prompt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exit&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ChatGPT: Goodbye!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;

        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;openai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ChatCompletion&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="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;temperature&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;typer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;echo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ChatGPT: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;choices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&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;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/theRubberDuckiee/chatgpt-clone" rel="noopener noreferrer"&gt;Check out my source code&lt;/a&gt; if you have any questions.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Demystifying the Terminal: How It Works Behind the Scenes</title>
      <dc:creator>Jessica Wang</dc:creator>
      <pubDate>Wed, 06 Sep 2023 21:03:41 +0000</pubDate>
      <link>https://forem.com/therubberduckiee/demystifying-the-terminal-how-it-works-behind-the-scenes-50h6</link>
      <guid>https://forem.com/therubberduckiee/demystifying-the-terminal-how-it-works-behind-the-scenes-50h6</guid>
      <description>&lt;p&gt;If you've ever opened up a terminal and entered a command, you've witnessed the magic of computing in action. But have you ever wondered what happens under the hood to fulfill that seemingly simple command? The truth is, the inner workings of a terminal are surprisingly complex. In this article, we'll scratch the surface of how a terminal operates from a technical standpoint.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/2P7fcVHxA9o"&gt;
&lt;/iframe&gt;
&lt;br&gt;
^ Youtube video if you prefer that. The visuals are a lot better here :-)&lt;/p&gt;

&lt;h1&gt;
  
  
  The Terminal's Historical Roots
&lt;/h1&gt;

&lt;p&gt;Before we delve into the intricacies of modern terminals, let's take a trip back in time, over 60 years ago. The modern terminal we use today draws inspiration from teletype computers, which were designed to facilitate text-based communication through keyboards and printed output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ff7t9fbo2kr603z1tupg7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ff7t9fbo2kr603z1tupg7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the 1960s, many places with access to computing power relied on mainframe computers connected to teletype interfaces. Here's a simplified breakdown of how this technology functioned:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every keypress transmitted a character over a wire.&lt;/li&gt;
&lt;li&gt;The mainframe computer's kernel received and decoded this input.&lt;/li&gt;
&lt;li&gt;The decoded text was sent to a component called the TTY driver, responsible for relaying user input to programs and collecting output.&lt;/li&gt;
&lt;li&gt;Finally, the output was sent back to the user for display.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What made this system remarkable was the introduction of "line discipline," which buffered characters in memory until the Enter key was pressed. Over time, various optimizations, such as error correction and shortcuts, made modern terminals resemble their teletype predecessors.&lt;/p&gt;

&lt;h1&gt;
  
  
  Evolution of Terminals
&lt;/h1&gt;

&lt;p&gt;In the 1970s, technology took a giant leap with the introduction of video terminals like the VT05 and VT100. These terminals supported ANSI escape codes, colors, sounds, and marked a revolution in computing. However, the core principle remained the same—input and output.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fpxbn78u4enp51qhhmdlz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fpxbn78u4enp51qhhmdlz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As technology progressed, computers ceased being mere tools to communicate with larger machines; they became ubiquitous in operating systems.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Modern Terminal: An Application
&lt;/h1&gt;

&lt;p&gt;Fast forward to today, and the terminals we use have evolved significantly. Computers no longer rely solely on text-based interactions. Instead, they handle a multitude of intricate processes simultaneously.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F0axkelqh7d3c9thty8t1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F0axkelqh7d3c9thty8t1.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Modern terminals are now software applications, overseen by your chosen operating system. When you open a terminal, it listens for user input and instructs the operating system on what to display in the terminal window. Essentially, it emulates the hardware of the terminal.&lt;/p&gt;

&lt;h1&gt;
  
  
  Terminal Emulation
&lt;/h1&gt;

&lt;p&gt;When you click on the terminal icon, behind the scenes, the terminal establishes a communication channel with your shell—the software responsible for executing commands. In the 1960s, this connection was a physical wire. Today, it's known as a pseudo-teletype or PTY, consisting of pairs of file descriptors that bridge communication between the terminal and running processes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F8199gmkfyg0ccw5sq3fv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F8199gmkfyg0ccw5sq3fv.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One end of the PTY is the "leader," used by the terminal to process user input and display output. The other end is the "follower," utilized by the shell and other tasks initiated by the terminal.&lt;/p&gt;

&lt;h1&gt;
  
  
  Spawning the Shell
&lt;/h1&gt;

&lt;p&gt;After creating the necessary files, the terminal's critical task is spawning the shell—the command interpreter and the brains of the operation. Each time you enter a command, the shell runs it as a child process, with the terminal acting as the parent.&lt;/p&gt;

&lt;p&gt;The terminal configures the child process to read and write from the PTY follower, establishing a seamless connection between user input and the shell.&lt;/p&gt;

&lt;h1&gt;
  
  
  Shell Initialization
&lt;/h1&gt;

&lt;p&gt;Once the shell is born, it executes scripts to allow users to customize their experience. Depending on whether you're using a GUI-less server or a personal computer, the behavior may differ. Typically, the shell prints information like the username, time, hostname, and more, preparing itself to accept user input.&lt;/p&gt;

&lt;h1&gt;
  
  
  Running a Command
&lt;/h1&gt;

&lt;p&gt;When you type a command, the characters are translated into Unicode characters, which are then processed by the shell's line editor. Special characters trigger specific functions, while non-special characters are passed to the line discipline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fityok77p2wus0ajdkttn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fityok77p2wus0ajdkttn.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Modern shells often disable the line discipline's canonical features, operating in raw mode. This allows the shell to handle all characters before submitting the command, enabling features like auto-completion, suggestions, and syntax highlighting.&lt;/p&gt;

&lt;h1&gt;
  
  
  Hitting Enter
&lt;/h1&gt;

&lt;p&gt;Pressing Enter is when the real action happens. The shell parses the command by tokenizing and analyzing it. It checks various sources for validity, including aliases, functions, environment variables, built-in commands, and executable files in the PATH.&lt;/p&gt;

&lt;p&gt;Once it identifies a valid source, such as an executable file, it locates it on the filesystem and runs it as a separate child process, passing any additional arguments.&lt;/p&gt;

&lt;h1&gt;
  
  
  Returning Output
&lt;/h1&gt;

&lt;p&gt;When you run a command, like "ls," it spawns a child process that inherits the shell's file descriptors. All output from the command is directed to the PTY follower. The terminal emulator reads and displays the raw bytes streamed from the child process.&lt;/p&gt;

&lt;p&gt;For the colorful text you see in modern terminals, escape sequences play a crucial role. These special codes embedded in text output instruct the terminal on how to render text, including colors, cursor movements, and more.&lt;/p&gt;

&lt;h1&gt;
  
  
  Closing the Terminal
&lt;/h1&gt;

&lt;p&gt;When you close your terminal window, several steps occur behind the scenes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The shell performs cleanup, such as saving command history and running logout scripts.&lt;/li&gt;
&lt;li&gt;Child processes spawned by the shell are terminated.&lt;/li&gt;
&lt;li&gt;Resources like open file descriptors are released.&lt;/li&gt;
&lt;li&gt;The shell process exits and signals the terminal emulator to close.&lt;/li&gt;
&lt;li&gt;The operating system cleans up any remaining resources.&lt;/li&gt;
&lt;li&gt;The terminal window is fully closed, and your session is logged out and cleaned up.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this happens in a matter of seconds, showcasing the complexity hidden behind what seems like a simple command line.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;The terminal, despite its apparent simplicity, remains the most straightforward way to communicate with a computer. Its evolution from teletypes to modern terminal emulators reflects the enduring power and adaptability of this technology.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fdexxfng7yyjteagvckr7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fdexxfng7yyjteagvckr7.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you want to try out a modern terminal that will boost your productivity, check out &lt;a href="//warp.dev"&gt;Warp&lt;/a&gt;. &lt;/p&gt;

</description>
    </item>
    <item>
      <title>Build Your Own CLI App in 5 Minutes (ClackJS + Node + NPM Tutorial)</title>
      <dc:creator>Jessica Wang</dc:creator>
      <pubDate>Sat, 05 Aug 2023 21:57:48 +0000</pubDate>
      <link>https://forem.com/therubberduckiee/build-your-own-cli-app-in-5-minutes-clackjs-node-npm-tutorial-4nkl</link>
      <guid>https://forem.com/therubberduckiee/build-your-own-cli-app-in-5-minutes-clackjs-node-npm-tutorial-4nkl</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Today, I’m going to show you how to build your own command line application and deploy it as a NPM package. I will be using npm, Node.js, and &lt;a href="https://www.clack.cc/" rel="noopener noreferrer"&gt;Clack&lt;/a&gt; to build this.&lt;/p&gt;

&lt;p&gt;You can even try it out for yourself right now by running:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx cli-trivia-game&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here's a sneak peek of the trivia game (full .gif of play-through + answers at the bottom):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fkx9g0i8xxyfpsbbeytt2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fkx9g0i8xxyfpsbbeytt2.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Getting Started
&lt;/h1&gt;

&lt;p&gt;1) Navigate to your command line and run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm init -y&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is going to initialize a new project, and the &lt;code&gt;-y&lt;/code&gt; flag automatically answers “yes” to all the prompts. This generates a package.json file that has all the default settings without having to manually provide details.&lt;/p&gt;

&lt;p&gt;2) Next, you will run:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm -install @clack/prompts&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Once you do that, you’ll have a framework that lets you create things like spinners, multi-selection, and self-validated cancel logic all with just a few lines of code. &lt;/p&gt;

&lt;p&gt;3) Let’s leverage Clack’s built-in setup logic. To create an intro, I’m going to call intro here and pass in this string that let’s the player know they’re about to play in a trivia quiz that tests their CLI knowledge.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fqy92qyfkg21whbfc2bzz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fqy92qyfkg21whbfc2bzz.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;4) Since this is a trivia quiz, let’s ask some questions. First, I’ll create a class called Question that requires a question string, an array of potential answers, and the index of the correct answer. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F0yx8ws56n8lg0fswz3ha.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F0yx8ws56n8lg0fswz3ha.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;5) Then, I’m going to create 10 question objects using this class template.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fsv9c4rt7wgqgo6jt7dha.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fsv9c4rt7wgqgo6jt7dha.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;6) Now that we’ve got our questions, how do we display them on the command line? We’re going to use Clack’s &lt;code&gt;select&lt;/code&gt; component to do this. All I have to do is pass in the question, initial value, and options, and voila.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F7mqmbx3mxyi752xsc3mj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F7mqmbx3mxyi752xsc3mj.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;7) To make it feel a little more sophisticated, I’m going to add a spinner after the user answers just so they have time to process the UI and makes the program feel intelligent. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fiu203exkzkbrraetfv8l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fiu203exkzkbrraetfv8l.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;8) And then to tie up, I’m going to declare a global variable called totalCorrect which will increment when the player answers a question correctly. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fmf2cp4i2g4p3gr1e9k83.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fmf2cp4i2g4p3gr1e9k83.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;9) At the end, I’ll add some logic to display different outros based on how many questions the player got correct.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F79g3qthkqai3jdrt3oa6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F79g3qthkqai3jdrt3oa6.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And the best part - we’re going to publish this as an npm package so that every developer in the world can download on their machines if they choose to. It’s actually pretty easy.&lt;/p&gt;

&lt;p&gt;1) Go to package.json and add “bin”:”./index.js”&lt;/p&gt;

&lt;p&gt;2) Run &lt;code&gt;npm login&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;3) Run &lt;code&gt;npm publish&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;4) Then run &lt;code&gt;npx cli-trivia-game&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;3 Min Youtube version (if you prefer that) 👇&lt;br&gt;
&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/GupmEQFkDJM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Done! But before you start this project for yourself, take into consideration that Clack is really lacking in the documentation. Anything fairly complex, you won’t benefit from the pre-made components of clack/prompts. &lt;a href="https://github.com/shadawck/awesome-cli-frameworks" rel="noopener noreferrer"&gt;Here's&lt;/a&gt; a list of other CLI frameworks you can use instead.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;&lt;strong&gt;Check out the full code for the project here:&lt;/strong&gt;&lt;/u&gt;&lt;br&gt;
&lt;a href="https://github.com/theRubberDuckiee/cli-trivia-game" rel="noopener noreferrer"&gt;https://github.com/theRubberDuckiee/cli-trivia-game&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Just like that, you have made and published your first CLI application! Congrats :)&lt;/p&gt;

&lt;p&gt;And, if you'd like to know the answers to all 10 questions in my trivia game, here you go:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F7du7d90vfbvoszam72jk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F7du7d90vfbvoszam72jk.gif" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>npm</category>
      <category>javascript</category>
      <category>node</category>
      <category>commandline</category>
    </item>
    <item>
      <title>The Battle For Perfomance In The Terminal</title>
      <dc:creator>Jessica Wang</dc:creator>
      <pubDate>Tue, 09 May 2023 18:09:19 +0000</pubDate>
      <link>https://forem.com/therubberduckiee/the-battle-for-perfomance-in-the-terminal-40el</link>
      <guid>https://forem.com/therubberduckiee/the-battle-for-perfomance-in-the-terminal-40el</guid>
      <description>&lt;p&gt;Grids - two-dimensional arrays of characters - are the universal building blocks of terminals. The basic operations we expect from our terminals - entering a command, receiving output, scrolling through a file - are, at their core, operations on grids. This piece is an attempt at explaining the terminal from the bottom up, starting from the grid. The goal is to spell out some of the not-so-obvious performance calculus behind terminal grids: what are the operations being optimized and at what cost?&lt;/p&gt;

&lt;p&gt;If a terminal session is really some conversation history between the terminal and the commands it has executed or is currently executing, then it’s the grid that provides some agreed-upon format or ledger to converse. It’s not just that grids are a convenient choice for terminal builders; the grid is actually what’s required by the program the terminal is layered over, the shell. The shell moves a cursor and outputs characters within a defined two-dimensional space, and the terminal is responsible for storing and rendering the contents of that space to the end-user. This makes the concept of the grid a – if not the – fundamental engineering specification of terminals.&lt;/p&gt;

&lt;p&gt;(If you’re curious to learn more about the dependencies between terminals and shells, I highly recommend &lt;a href="https://www.warp.dev/blog/what-happens-when-you-open-a-terminal-and-enter-ls"&gt;this blog post&lt;/a&gt;.)&lt;/p&gt;

&lt;p&gt;In &lt;a href="//Warp.dev"&gt;Warp&lt;/a&gt;, the grid is especially important because it’s everywhere. Warp separates commands into blocks, and each block consists of three grids: one for the prompt, one for the command input, and one for the command output. Even opening full-screen programs like vim and emacs are themselves instances of this same grid, just with expanded dimensions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GVyzxQmv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bvd0g73odg1wyraf0x9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GVyzxQmv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bvd0g73odg1wyraf0x9x.png" alt="Image description" width="800" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, a command in Warp is the combination of three grids receiving and sending characters to the shell. (Technical note: Warp also offers a default prompt instead of a customized shell prompt).&lt;/p&gt;

&lt;p&gt;Because the grid is the means of storage and communication, it is at this layer that terminals make meaningful trade-offs around performance. For example, imagine you’re running a server that endlessly prints out log lines. When the log lines fill up the available grid space on the screen, how many new rows should Warp allocate for the grid? 1? 100? 1000? Allocating 1000 could buy us time before the command runs out of space again, saving us the performance penalty of repeated memory allocations, but of course the command might not use the full 1000. Should we risk a slightly greater memory footprint for an uncertain promise of faster execution?&lt;/p&gt;

&lt;p&gt;Even more interesting are the trade-offs the grid must make between the most basic terminal operations. Some examples of these operations are reading in data from the shell, scrolling through content, and resizing the screen or splitting panes. Warp’s implementation of the grid is itself an opinionated stance on which of these operations a terminal should be most efficient at, with the reality being that optimizing for one almost always means not optimizing for another. Should we be excellent at one thing if it means being just okay at something else? The grid is the layer at which this type of performance calculus is baked in and shipped to our users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting technical
&lt;/h2&gt;

&lt;p&gt;Warp’s grid is a circular buffer that stores rows sequentially in a vector. Say, for example, you create a grid storing a subset of Shakespeare plays that are sorted alphabetically. How this renders on screen and how this is stored internally might look something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IRQR_Of5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f5dik9n8xnmjg283grbe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IRQR_Of5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f5dik9n8xnmjg283grbe.png" alt="Image description" width="800" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On screen, the contents are rendered alphabetically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q6USPO3f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eqp3dgkhuyvv6298iqei.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q6USPO3f--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eqp3dgkhuyvv6298iqei.png" alt="Image description" width="800" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In storage (the underlying vector), the contents remain sequential but may be rotated. Notice the top row in the grid is stored at vector index 2, the second row is stored at vector index 3, and so on.&lt;/p&gt;

&lt;p&gt;The conversion between the &lt;em&gt;screen index&lt;/em&gt; and the &lt;em&gt;vector index&lt;/em&gt; is a &lt;em&gt;really, really important one&lt;/em&gt;. When the grid renders to the screen, we’ll want to locate the contents of screen index 0 and then 1 and then 2 and so on. To optimize for this, the grid stores two integers that allow us to efficiently translate between &lt;em&gt;screen space&lt;/em&gt; and &lt;em&gt;vector space&lt;/em&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;bottom_row: the vector index of the last row in the grid, aka the furthest the grid extends. In the example, bottom_row = 1 (Tempest is the last row in the grid).&lt;/li&gt;
&lt;li&gt;length: the total number of rows that we’ve received from the shell, which may be different from the actual capacity of the vector at any given moment. In the example, length = 5.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Using these indices, we can convert from &lt;em&gt;screen space&lt;/em&gt; to &lt;em&gt;vector space&lt;/em&gt; like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T-UPpRS9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ykq7d6m786v20t5mwq1x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T-UPpRS9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ykq7d6m786v20t5mwq1x.png" alt="Image description" width="800" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nevertheless, the whole process of index conversions presupposes that the grid needs to be rotated in the first place. Why is that the case? Why not skip the silly conversions and enforce equivalence between the screen indices and the vector indices, meaning that “As You Like It,” the first row on screen, is also stored as the first row in the vector? Not surprisingly, this is because the grid is heavily optimizing for something else entirely: scrolling!&lt;/p&gt;

&lt;h2&gt;
  
  
  Scrolling the grid
&lt;/h2&gt;

&lt;p&gt;In Warp, scrolling in a command like vim, emacs, git log, git diff, or many others is optimized to avoid the ultimate price in the grid world – having to move physical memory. Inspired from the previous section, imagine an example of scrolling the contents of the Shakespeare file upwards to reveal a new row at the bottom of the screen:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3FpA76WB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zanz649evee5e91lxhmi.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3FpA76WB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zanz649evee5e91lxhmi.gif" alt="Image description" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What would this look like at the grid layer? To start off, the underlying vector is unrotated. It looks like this, with bottom_row = 8 and the conceptual “top row” = 0.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hoyXKJm4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75kezwfpnvv4ojv9kmyb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hoyXKJm4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/75kezwfpnvv4ojv9kmyb.png" alt="Image description" width="796" height="828"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first step to scrolling up is to extend bottom_row by one while keeping length the same. When we wrap bottom_row, it becomes 0 and top row becomes 1.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mr44y85I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dgqytmlg0b0446js39he.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mr44y85I--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dgqytmlg0b0446js39he.png" alt="Image description" width="800" height="813"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From there, we overwrite the contents of the bottom row with whatever the shell feeds to Warp as the next line in the file – in this case, “Tempest”. After that, the scroll operation is finished and the resulting grid looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VgCc26AO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/okb1u8o8i90dp8l7m4he.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VgCc26AO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/okb1u8o8i90dp8l7m4he.png" alt="Image description" width="800" height="844"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What’s noteworthy is that Warp completes this scroll operation without moving any data in the vector, besides overwriting the &lt;em&gt;exiting&lt;/em&gt; row with the &lt;em&gt;entering&lt;/em&gt; row. The scroll operation succeeded solely by changing the metadata of the grid, specifically the index of the bottom_row. The result is an extremely efficient scroll operation at the cost of leaving the grid in a rotated state.&lt;/p&gt;

&lt;p&gt;One of the penalties of the now-rotated grid is the added complexity of index conversion that we discussed above. Broadly speaking, this penalty is relatively minor and is more than compensated for by the efficiency we gain during scrolling. Where this becomes a little bit more harrowing as a performance calculation is when we actually receive output from the shell and find ourselves needing an extra row. At that time, having a grid that’s rotated leaves us no choice but to move memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extending the grid
&lt;/h2&gt;

&lt;p&gt;A lot of terminal commands print content to the screen rapidly and require the size of the grid to grow dynamically.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mjVNikfg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4sa2eu229qyu5lj74q85.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mjVNikfg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4sa2eu229qyu5lj74q85.gif" alt="Image description" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, the terminal reads content from the shell (via the receiving end of a pipe known as the &lt;a href="https://www.warp.dev/blog/what-happens-when-you-open-a-terminal-and-enter-ls"&gt;PTY&lt;/a&gt;, short for pseudo-TTY) and writes it to a grid. This is a fundamental operation for the terminal: how efficiently can we read streamed input from an open file descriptor and write it character-by-character to the specified grid location? &lt;/p&gt;

&lt;p&gt;Specifically, the problem becomes interesting in the common case where the shell issues a newline – this is the shell warning the terminal that the command requires more than the current size of the grid, implying the vertical grid dimension needs to be expanded by one row. From the terminal’s perspective, let’s consider this request under the worst-case grid configuration: the underlying vector is already at capacity and the grid is rotated (again, index on screen != index in vector).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Capacity&lt;/strong&gt;: if the vector has no more free memory, then we cannot avoid a vector resize cost. Vectors are heap-allocated data types, and resizing requires (1) finding a heap chunk that’s big enough for the new capacity and (2) copying the old contents into the new memory space.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Rotation&lt;/strong&gt;: if the grid is currently rotated, then we must “re-zero” the vector before increasing its size. This implies costly memory management: we reverse the rotation until the conceptual “top row” is back to vector index 0. Only once the vector is unrotated can we tag new rows onto the end.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9iaA2WoZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/10leiye45jwij8lavl6o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9iaA2WoZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/10leiye45jwij8lavl6o.png" alt="Image description" width="800" height="267"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, we cannot extend the vector until the existing contents are unrotated. Otherwise, we’d conceptually be tagging on new rows to somewhere that’s not the end of the logical grid - that would be a mess! This “re-zero” is a costly copying operation.&lt;/p&gt;

&lt;p&gt;Because the penalty under the worst-case grid configuration is very high, Warp does its best to make this circumstance as infrequent as possible. As an example, we only allocate new rows in chunks of 1000, opting to make the resize slightly more heavyweight with the benefit of amortizing the cost to every 1000 newlines. &lt;/p&gt;

&lt;h2&gt;
  
  
  Resizing a grid
&lt;/h2&gt;

&lt;p&gt;A final, essential piece to this weaving narrative about grid optimizations and their externalities is the process of resizing a grid. Importantly, grids in Warp are always stored at the dimensions of the pane in which they render, which are not necessarily equivalent to the dimensions at which we received them from the shell. Said differently, we always rewrite the grid so any horizontal limit is imposed at the time of resizing and not at the time of rendering. This makes more sense with a visual:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cASBZzBD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t3xr3vw60bo6hozuu3gb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cASBZzBD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t3xr3vw60bo6hozuu3gb.gif" alt="Image description" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the shell originally outputs a grid that is 10 rows by 10 columns and the user proceeds to resize the screen (or split a pane) that makes the grid 5 columns, then internally the vector is re-written to account for this new horizontal constraint: the grid becomes 20 rows by 5 columns both on screen and in storage. Today, every resize in Warp requires re-writing every grid currently displayed on the screen from the top down, row by row. &lt;/p&gt;

&lt;p&gt;We permit this considerable performance penalty – for now – because, although resizing is an essential operation, it’s hard designing optimizations that won’t slow down everything else: doing index conversions, scrolling, adding a new line to the grid, etc. From a holistic standpoint about overall performance priorities in the app, it’s also not clear that resizing should take precedence over any of those things.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;All told, an intelligent, intentional design of the terminal grid should recognize that oftentimes the most important operations are in competition with one another. Every block in Warp is a careful calculus about where to excel and how to minimize the resulting cost: that the rotated vector streamlines scrolling but costs us the amortized “re-zero” every 1000 lines, that allocating new rows in chunks means we’re willing to risk some bloat for fewer memory allocations, that storing the grid in its renderable dimensions is optimal but leaves us vulnerable at resize, to name but a few. &lt;/p&gt;

&lt;p&gt;If you have opinions on what our performance priorities should be – or any insights about the trade-offs discussed here – we’d love to hear from you! Better yet, if you find this type of problem interesting, come work with us!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>How I Write Easy CLI Scripts to Automate My Life.</title>
      <dc:creator>Jessica Wang</dc:creator>
      <pubDate>Wed, 12 Apr 2023 07:00:52 +0000</pubDate>
      <link>https://forem.com/therubberduckiee/how-i-write-easy-cli-scripts-to-automate-my-life-427k</link>
      <guid>https://forem.com/therubberduckiee/how-i-write-easy-cli-scripts-to-automate-my-life-427k</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oVcfpHqX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/88e3xejmalfjz099nzln.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oVcfpHqX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/88e3xejmalfjz099nzln.jpeg" alt="Image description" width="600" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I take hundreds of screenshots a day, and I always forget to delete them. They end up taking up a lot of memory on my machine, and sometimes I can't just bulk highlight &amp;amp; delete in fear of deleting important pictures &amp;amp; videos that I actually need.&lt;/p&gt;

&lt;p&gt;Here's how I wrote &lt;a href="https://github.com/theRubberDuckiee/dev-environment-files/blob/main/delete_screenshots.sh"&gt;a script to automate this for me&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;If you don't like reading, here's a video tutorial you can watch:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/rPEkXll7kUc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  Before Getting Started
&lt;/h1&gt;

&lt;p&gt;I want to preface this by saying this tutorial heavily hinges on me using Warp AI, which is basically a ChatGPT integration built into the terminal that quickly generates answers/code snippets to any question. &lt;/p&gt;

&lt;p&gt;If you want to follow along, you'll need to be using the Warp terminal app which you can download for free &lt;a href="//warp.dev"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the next section, I'll be putting the prompt (what I asked Warp AI) and what Warp AI's output was. I basically just followed those instructions to write my script in under 5 minutes.&lt;/p&gt;

&lt;h1&gt;
  
  
  A Summary Of My Process
&lt;/h1&gt;

&lt;p&gt;So my process for writing this script was just:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Asking Warp AI how to do something.&lt;/li&gt;
&lt;li&gt;Copying the output it gave me (code blocks + commands to run).&lt;/li&gt;
&lt;li&gt;Checking to see if it worked.&lt;/li&gt;
&lt;li&gt;Asking Warp AI about any errors/getting it to tweak its output.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the section below, I'll include the entire transcript of my interaction with Warp AI as I was building my script. This probably took me less than 5 minutes. Here's a summary if you don't want to read the whole transcript.&lt;/p&gt;




&lt;p&gt;1) Ask Warp AI how to write a script that deletes all screenshots from my Documents directory.&lt;/p&gt;

&lt;p&gt;2) Copying the output, but realizing it looks for everything named "screenshots.png" whereas my screenshots were named "Screen Shot XYZ.png"&lt;/p&gt;

&lt;p&gt;3) Asking Warp AI to tweak its output to look for the phrase Screen Shot and also be case insensitive.&lt;/p&gt;

&lt;p&gt;4) Getting base functionality working, and then improving on it by getting the script to print out the names of files it was deleting.&lt;/p&gt;

&lt;p&gt;5) Asking Warp AI to explain some of the outputs so I can understand what my script is doing &amp;amp; learn in the process.&lt;/p&gt;




&lt;h1&gt;
  
  
  Warp AI Transcripts (Ok To Skip to Conclusion)
&lt;/h1&gt;

&lt;p&gt;&lt;u&gt;&lt;strong&gt;Prompt: Write me a script that deletes all screenshots from my Documents directory&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Warp AI: Sure! Here's a script that will delete all screenshots from your Documents directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

cd ~/Documents
rm -f *screenshot*.png

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

&lt;/div&gt;



&lt;p&gt;Save this script as a file with a &lt;code&gt;.sh&lt;/code&gt; extension (e.g. &lt;code&gt;delete_screenshots.sh&lt;/code&gt;) and make it executable with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chmod +x delete_screenshots.sh

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

&lt;/div&gt;



&lt;p&gt;Then, you can run the script in the terminal with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./delete_screenshots.sh

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

&lt;/div&gt;



&lt;p&gt;This will change to your Documents directory and delete all files with "screenshot" in the filename and a ".png" extension. Be careful when running this script, as it will permanently delete these files without prompting for confirmation.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;&lt;strong&gt;Prompt: do I have to run chmod +x command every time I edit the .sh file?&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Warp AI: No, you don't have to run the &lt;code&gt;chmod +x&lt;/code&gt; command every time you edit the &lt;code&gt;.sh&lt;/code&gt; file. The &lt;code&gt;chmod +x&lt;/code&gt; command only needs to be run once to make the script executable.&lt;/p&gt;

&lt;p&gt;However, if you make changes to the script and save the file, you will need to run the script again to see the effects of the changes. If you have already made the script executable with &lt;code&gt;chmod +x&lt;/code&gt;, you can simply run the script again with &lt;code&gt;./script_name.sh&lt;/code&gt; to execute the updated version of the script.&lt;/p&gt;

&lt;p&gt;If you make changes to the script and want to see the effects of the changes without having to run the &lt;code&gt;chmod +x&lt;/code&gt; command again, you can use the following command to reload the script in the current shell session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source script_name.sh

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

&lt;/div&gt;



&lt;p&gt;This will reload the script and apply any changes you have made to it, without requiring you to make the script executable again.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;&lt;strong&gt;Prompt: Can you edit the script above to delete anything with file name that includes "Screen Shot" and is case insensitive?&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Warp AI: Yes, here's an updated script that will delete all files with "Screen Shot" in the filename (case insensitive) from your Documents directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

cd ~/Documents
shopt -s nocaseglob
rm -f *Screen\\ Shot*

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;shopt -s nocaseglob&lt;/code&gt; command enables case-insensitive globbing, which means that the &lt;code&gt;*Screen\\ Shot*&lt;/code&gt; pattern will match filenames with "Screen Shot" in any combination of upper and lower case letters.&lt;/p&gt;

&lt;p&gt;Save this script as a file with a &lt;code&gt;.sh&lt;/code&gt; extension (e.g. &lt;code&gt;delete_screen_shots.sh&lt;/code&gt;) and make it executable with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chmod +x delete_screen_shots.sh

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

&lt;/div&gt;



&lt;p&gt;Then, you can run the script in the terminal with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./delete_screen_shots.sh

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

&lt;/div&gt;



&lt;p&gt;This will change to your Documents directory and delete all files with "Screen Shot" in the filename (case insensitive). Be careful when running this script, as it will permanently delete these files without prompting for confirmation.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;&lt;strong&gt;Prompt: can you edit this script above to output the names of all the screenshots it deleted, each on their own line?&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Warp AI: Yes, here's an updated script that will delete all files with "Screen Shot" in the filename (case insensitive) from your Documents directory and output the names of the deleted files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#!/bin/bash

cd ~/Documents
shopt -s nocaseglob
deleted_files=(*Screen\\ Shot*)
rm -f "${deleted_files[@]}"
printf "Deleted files:\\n%s\\n" "${deleted_files[@]}"

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;deleted_files=(*Screen\\ Shot*)&lt;/code&gt; line creates an array of all files with "Screen Shot" in the filename (case insensitive), and the &lt;code&gt;rm -f "${deleted_files[@]}"&lt;/code&gt; line deletes all of these files. Finally, the &lt;code&gt;printf&lt;/code&gt; command outputs the names of the deleted files.&lt;/p&gt;

&lt;p&gt;Save this script as a file with a &lt;code&gt;.sh&lt;/code&gt; extension (e.g. &lt;code&gt;delete_screen_shots.sh&lt;/code&gt;) and make it executable with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;chmod +x delete_screen_shots.sh

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

&lt;/div&gt;



&lt;p&gt;Then, you can run the script in the terminal with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;./delete_screen_shots.sh

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

&lt;/div&gt;



&lt;p&gt;This will change to your Documents directory, delete all files with "Screen Shot" in the filename (case insensitive), and output the names of the deleted files. Be careful when running this script, as it will permanently delete these files without prompting for confirmation.&lt;/p&gt;

&lt;p&gt;&lt;u&gt;&lt;strong&gt;Prompt: What does the \n%s\n do?&lt;/strong&gt;&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Warp AI: The &lt;code&gt;\\n&lt;/code&gt; characters in the &lt;code&gt;printf&lt;/code&gt; command are escape sequences that represent a newline character, which causes the output to be formatted with each deleted filename on a separate line.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;%s&lt;/code&gt; format specifier in the &lt;code&gt;printf&lt;/code&gt; command is a placeholder for a string value, which is replaced by the values of the &lt;code&gt;deleted_files&lt;/code&gt; array. The &lt;code&gt;@&lt;/code&gt; symbol after the array name tells &lt;code&gt;printf&lt;/code&gt; to expand the array into separate arguments, with each element of the array being treated as a separate string value.&lt;/p&gt;

&lt;p&gt;So, the &lt;code&gt;printf&lt;/code&gt; command with the &lt;code&gt;%s&lt;/code&gt; specifier and the &lt;code&gt;@&lt;/code&gt; symbol expands the &lt;code&gt;deleted_files&lt;/code&gt; array into separate string arguments, which are then printed on separate lines with a newline character before and after each string.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EUFPa7aC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h0962vqk41q11erj23rz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EUFPa7aC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h0962vqk41q11erj23rz.gif" alt="Image description" width="315" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And in the end, I now have a &lt;a href="https://github.com/theRubberDuckiee/dev-environment-files/blob/main/delete_screenshots.sh"&gt;working script&lt;/a&gt; that I run pretty easily from the command line.&lt;/p&gt;

&lt;p&gt;It's crazy that I can so easily write bash scripts without knowing anything about CLI scripting.&lt;/p&gt;

&lt;p&gt;Warp AI is OP.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Become a VIM PRO in 2023 without having to memorize ANYTHING (ChatGPT + Warp + MacOS)</title>
      <dc:creator>Jessica Wang</dc:creator>
      <pubDate>Sun, 02 Apr 2023 05:39:17 +0000</pubDate>
      <link>https://forem.com/therubberduckiee/become-a-vim-pro-in-2023-without-having-to-memorize-anything-chatgpt-warp-macos-2j0</link>
      <guid>https://forem.com/therubberduckiee/become-a-vim-pro-in-2023-without-having-to-memorize-anything-chatgpt-warp-macos-2j0</guid>
      <description>&lt;p&gt;I think we can all agree that Vim is pretty intimidating and not super intuitive - especially because you need to memorize so many keyboard shortcuts just to move around. &lt;/p&gt;

&lt;p&gt;In this blog, I’m going to show you how I became a Vim PRO without having to memorize anything. Let’s get started!&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ir4ulHPfEZg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  So why do people use vim?
&lt;/h1&gt;

&lt;p&gt;Here are a few reasons:&lt;/p&gt;

&lt;p&gt;1) Vim is the default editor on any operating system in the world.&lt;/p&gt;

&lt;p&gt;2) Vim is super fast and responsive.&lt;/p&gt;

&lt;p&gt;3) Lastly, I think there are some developers that just enjoy the challenge of mastering Vim.&lt;/p&gt;

&lt;h1&gt;
  
  
  How I mastered Vim without memorizing anything
&lt;/h1&gt;

&lt;p&gt;I’m using my Warp terminal on my MacOS. You’re going to need the Warp terminal to follow along. To install it, run &lt;/p&gt;

&lt;p&gt;&lt;code&gt;brew install warp&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;in your CLI or download &lt;a href="https://app.warp.dev/get_warp" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For the purposes of this demo, I’ve created this .txt file that you can think of as a little game loosely inspired by British TV-show Taskmaster. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fjup2ol6iey81nswmzzno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fjup2ol6iey81nswmzzno.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each line has a task that I need to follow, and we’re going to complete all these tasks using Vim keyboard shortcuts in the MOST EFFICIENT WAY POSSIBLE. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fjlgs7vkfxn92lqd4q3dp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fjlgs7vkfxn92lqd4q3dp.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To open up Warp AI, press&lt;/p&gt;

&lt;p&gt;&lt;code&gt;^ + Space&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and then as you try to complete these tasks, ask Warp AI the bullet point questions to learn the corresponding Vim keyboard shortcuts.&lt;/p&gt;

&lt;p&gt;1) Remove the period from the end of this line.&lt;br&gt;
&lt;em&gt;I could technically just press right to get to the end of the line, but that’s not efficient!&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“How do I navigate to the end of the line in vim”&lt;/li&gt;
&lt;li&gt;“How do I edit in vim”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2) Go to the last line and fill in the blank, then come back up to this line.&lt;br&gt;
&lt;em&gt;I could technically just press down to get to the end of the file, but that’s not efficient!&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“How do I navigate 3 lines down”&lt;/li&gt;
&lt;li&gt;“How do I navigate by word”&lt;/li&gt;
&lt;li&gt;“How do I delete a word”&lt;/li&gt;
&lt;li&gt;“How do I navigate 3 lines up”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3) Delete this entire line.&lt;br&gt;
&lt;em&gt;I could navigate to the end of the line and just press backspace, but that’s not efficient!&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“How do I delete an entire line”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;4) Find and replace all instances of the word “line” with “COOKIE”&lt;br&gt;
&lt;em&gt;I could do this manually, but that’s not efficient!&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;“How do I find and replace all instances of a word”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;5) Please like and _____ to this channel for more tech and terminal tips!&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;And that’s it! Hopefully this helped.&lt;/p&gt;

</description>
      <category>chatgpt</category>
      <category>vim</category>
      <category>macos</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
