<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Benjamin Tetteh</title>
    <description>The latest articles on DEV Community by Benjamin Tetteh (@benjamin_tetteh).</description>
    <link>https://dev.to/benjamin_tetteh</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2923117%2Ff3d1ecaa-89e9-4456-afb0-d3217c2bb874.png</url>
      <title>DEV Community: Benjamin Tetteh</title>
      <link>https://dev.to/benjamin_tetteh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/benjamin_tetteh"/>
    <language>en</language>
    <item>
      <title>Real-World Git Branching Strategies: How Teams Manage Code in Practical Dev Environments</title>
      <dc:creator>Benjamin Tetteh</dc:creator>
      <pubDate>Wed, 08 Jul 2026 02:48:21 +0000</pubDate>
      <link>https://dev.to/benjamin_tetteh/real-world-git-branching-strategies-how-teams-manage-code-in-practical-dev-environments-2gd9</link>
      <guid>https://dev.to/benjamin_tetteh/real-world-git-branching-strategies-how-teams-manage-code-in-practical-dev-environments-2gd9</guid>
      <description>&lt;p&gt;&lt;em&gt;You've learned Git commands. Now let's see how real development teams actually use Git day to day — from branching strategies to deployments, code reviews, and fixing production bugs at speed.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Git commands are one thing. Using Git properly in a real development team is another.&lt;/p&gt;

&lt;p&gt;You can know &lt;code&gt;git commit&lt;/code&gt;, &lt;code&gt;git push&lt;/code&gt;, and &lt;code&gt;git merge&lt;/code&gt; perfectly and still feel lost the first time a senior developer says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Create a feature branch off &lt;code&gt;develop&lt;/code&gt;, open a PR when it's ready, and don't push directly to &lt;code&gt;main&lt;/code&gt;."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This article bridges that gap.&lt;/p&gt;

&lt;p&gt;Whether you are a beginner trying to understand how teams work, or an intermediate developer looking to sharpen your workflow, this guide covers the branching strategies, environments, and team practices that show up in real projects — from small startups to large engineering organisations.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Note on Real-World Workflows
&lt;/h2&gt;

&lt;p&gt;Every engineering team has its own workflow.&lt;/p&gt;

&lt;p&gt;A startup with five engineers may work very differently from a bank with hundreds of developers. The strategies in this article are the most common patterns you will encounter, but do not be surprised if your future team combines ideas from several of them — or has invented their own hybrid entirely.&lt;/p&gt;

&lt;p&gt;The goal is not to memorise a strategy. The goal is to understand the reasoning behind each one so you can adapt to any team confidently.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Why Branching Matters in Real Projects&lt;/li&gt;
&lt;li&gt;The Common Environments&lt;/li&gt;
&lt;li&gt;How Code Moves Between Environments&lt;/li&gt;
&lt;li&gt;Common Branch Types&lt;/li&gt;
&lt;li&gt;GitHub Flow&lt;/li&gt;
&lt;li&gt;Git Flow&lt;/li&gt;
&lt;li&gt;Trunk-Based Development&lt;/li&gt;
&lt;li&gt;Environment-Based Branching&lt;/li&gt;
&lt;li&gt;Feature Flags&lt;/li&gt;
&lt;li&gt;Pull Requests and Code Reviews&lt;/li&gt;
&lt;li&gt;What Happens After a Merge?&lt;/li&gt;
&lt;li&gt;How This Looks on GitHub&lt;/li&gt;
&lt;li&gt;CI/CD and Branch Protection Rules&lt;/li&gt;
&lt;li&gt;Where Does DevOps Fit?&lt;/li&gt;
&lt;li&gt;A Security Perspective&lt;/li&gt;
&lt;li&gt;Hotfix Scenario: Fixing Production Quickly&lt;/li&gt;
&lt;li&gt;Which Strategy Do Real Companies Use?&lt;/li&gt;
&lt;li&gt;Which Branching Strategy Should Beginners Learn First?&lt;/li&gt;
&lt;li&gt;Common Mistakes Teams Make&lt;/li&gt;
&lt;li&gt;Final Thoughts&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Why Branching Matters in Real Projects
&lt;/h2&gt;

&lt;p&gt;In a solo project, branching is a good habit.&lt;/p&gt;

&lt;p&gt;In a team project, branching is essential.&lt;/p&gt;

&lt;p&gt;Without a clear branching strategy, teams run into problems like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two developers overwriting each other's work&lt;/li&gt;
&lt;li&gt;Untested code being deployed directly to users&lt;/li&gt;
&lt;li&gt;A bug fix accidentally breaking an unrelated feature&lt;/li&gt;
&lt;li&gt;No clear way to roll back a bad deployment&lt;/li&gt;
&lt;li&gt;Confusion over which branch is the "source of truth"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A branching strategy is simply an agreed set of rules for how your team creates, names, and merges branches. It answers questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Where does new feature work start?&lt;/li&gt;
&lt;li&gt;How does code get reviewed before merging?&lt;/li&gt;
&lt;li&gt;How do we push code to production safely?&lt;/li&gt;
&lt;li&gt;What do we do when there is a bug in production right now?&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why &lt;code&gt;main&lt;/code&gt; exists
&lt;/h3&gt;

&lt;p&gt;Most tutorials say: "&lt;code&gt;main&lt;/code&gt; is production." But why does it exist as a special branch?&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;main&lt;/code&gt; branch represents the &lt;strong&gt;source of truth&lt;/strong&gt; for your project. In most organisations, every commit on &lt;code&gt;main&lt;/code&gt; should be deployable at any moment. That means if someone triggers a deployment of &lt;code&gt;main&lt;/code&gt; right now, the application should work correctly for users.&lt;/p&gt;

&lt;p&gt;This concept — a &lt;em&gt;deployable branch&lt;/em&gt; — is fundamental to understanding why teams protect &lt;code&gt;main&lt;/code&gt; so carefully.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡 Many organisations configure GitHub so that nobody — not even senior engineers — can push directly to &lt;code&gt;main&lt;/code&gt;. All changes must go through a pull request. Do not assume you will always be able to push directly; on most professional teams, you will not.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  2. The Common Environments
&lt;/h2&gt;

&lt;p&gt;Before understanding branching strategies, it helps to understand the environments that code typically passes through on its way to users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Laptop → Sandbox → Dev → QA/Test → Staging → Production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Local Development (Your Laptop)
&lt;/h3&gt;

&lt;p&gt;This is your machine. You write code here, run it, test it, and break things freely. Nothing here affects anyone else.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sandbox Environment
&lt;/h3&gt;

&lt;p&gt;A personal or isolated environment where a developer can test their work independently before sharing it with the team. Sandboxes are often spun up on demand and torn down afterwards. They are useful for experimenting with infrastructure changes, testing integrations, or working on something that is not ready for the shared dev environment yet.&lt;/p&gt;

&lt;h3&gt;
  
  
  Development Environment (Dev)
&lt;/h3&gt;

&lt;p&gt;A shared server where developers see each other's work integrated together. Often automatically updated when code is merged into a &lt;code&gt;develop&lt;/code&gt; branch. It is not stable and changes frequently throughout the day.&lt;/p&gt;

&lt;h3&gt;
  
  
  QA / Test Environment
&lt;/h3&gt;

&lt;p&gt;Used by QA engineers or testers to verify that features work correctly and that nothing is broken. More controlled than the dev environment, but still not user-facing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Staging Environment
&lt;/h3&gt;

&lt;p&gt;A near-identical copy of production. Staging is used for final checks before a release goes live. Stakeholders often review features here. It should behave exactly like production — same data structure, same configuration, same infrastructure where possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Production Environment
&lt;/h3&gt;

&lt;p&gt;The live environment that real users interact with. Code here must be stable, tested, and approved. Changes to production are treated with the highest level of care.&lt;/p&gt;

&lt;p&gt;Each environment has a higher bar for stability than the last. Code earns its way to production by passing through every gate before it.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. How Code Moves Between Environments
&lt;/h2&gt;

&lt;p&gt;In most teams, code does not jump directly from a developer's laptop to production. It moves through a deliberate pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your laptop
     ↓
Feature branch (your work in progress)
     ↓
Pull Request (code review + automated tests)
     ↓
develop / main (integration)
     ↓
Sandbox / Dev environment (integration testing)
     ↓
QA / Test environment (quality assurance)
     ↓
Staging environment (final review)
     ↓
Production (live for users)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each step acts as a quality gate. The further along the pipeline the code moves, the more confidence the team has that it is safe to ship.&lt;/p&gt;

&lt;p&gt;In many teams, this movement is automated. When you merge a PR into &lt;code&gt;develop&lt;/code&gt;, a CI/CD pipeline may automatically deploy that code to the dev environment. When code reaches &lt;code&gt;main&lt;/code&gt;, it may automatically deploy to staging or production.&lt;/p&gt;

&lt;p&gt;Understanding this pipeline is just as important as understanding Git commands.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Common Branch Types
&lt;/h2&gt;

&lt;p&gt;Most teams use a consistent naming convention for branches. Here are the types you will encounter in real projects:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Branch&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Production-ready code. Always stable and deployable.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;develop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Integration branch where completed features are merged before release.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;feature/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;New feature work. Created from &lt;code&gt;develop&lt;/code&gt; or &lt;code&gt;main&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;bugfix/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fixing a non-urgent bug found in development or testing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hotfix/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Urgent fix for a bug in production. Bypasses the normal flow.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;release/*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Preparing a release. Used for final tweaks before merging to &lt;code&gt;main&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Naming conventions in practice
&lt;/h3&gt;

&lt;p&gt;Teams often include a ticket or issue number in the branch name for traceability:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;feature/AUTH-42-add-oauth-login
bugfix/UI-17-fix-mobile-nav-overlap
hotfix/PROD-99-fix-payment-timeout
release/v2.4.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes it immediately clear what the branch is for, which ticket it relates to, and what kind of change it contains — even months later when someone is reading through the history.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. GitHub Flow
&lt;/h2&gt;

&lt;p&gt;GitHub Flow is a lightweight, simple branching strategy suited for teams that deploy frequently — sometimes many times a day.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main ──────────────────────────────────────► (always deployable)
  │                                  ▲
  └──► feature/login                 │
           │                         │
           │  commits                │
           ▼                         │
        Pull Request                 │
           │                         │
        Code Review                  │
           │                         │
        CI Checks                    │
           │                         │
        Approved ────────────────────┘
                        Merge
                           │
                       Production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The rules
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt; is always deployable&lt;/li&gt;
&lt;li&gt;All new work happens on a feature branch created from &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Open a pull request early, even before the work is finished&lt;/li&gt;
&lt;li&gt;Get the branch reviewed and tested via CI&lt;/li&gt;
&lt;li&gt;Merge into &lt;code&gt;main&lt;/code&gt; and deploy&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  In practice
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch main
git pull
git switch &lt;span class="nt"&gt;-c&lt;/span&gt; feature/add-dark-mode

&lt;span class="c"&gt;# do your work&lt;/span&gt;

git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add dark mode toggle to settings page"&lt;/span&gt;
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin feature/add-dark-mode

&lt;span class="c"&gt;# open a pull request on GitHub&lt;/span&gt;
&lt;span class="c"&gt;# get it reviewed and approved&lt;/span&gt;
&lt;span class="c"&gt;# merge and deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Who should use it
&lt;/h3&gt;

&lt;p&gt;GitHub Flow works well for small to medium teams, web applications with continuous deployment, open source projects, and teams that want simplicity over ceremony. It is the ideal starting point for beginners joining a real team.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Git Flow
&lt;/h2&gt;

&lt;p&gt;Git Flow is a more structured strategy designed for teams with scheduled releases. It was introduced by Vincent Driessen in 2010 and became very influential in enterprise and product teams.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main      ──────────────────────────────────► (tagged releases)
               ▲                    ▲
               │                    │
hotfix/fix ────┘           release/v2.1 ──► develop
                                               │
                              ┌────────────────┤
                              │                │
                         feature/login   feature/dashboard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The key branches
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;main&lt;/code&gt;&lt;/strong&gt; — production-ready code only. Tagged with version numbers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;develop&lt;/code&gt;&lt;/strong&gt; — the integration branch. All features merge here first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;feature/*&lt;/code&gt;&lt;/strong&gt; — individual feature branches, created from &lt;code&gt;develop&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;release/*&lt;/code&gt;&lt;/strong&gt; — created from &lt;code&gt;develop&lt;/code&gt; when preparing a release. Only bug fixes and release prep happen here, no new features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;hotfix/*&lt;/code&gt;&lt;/strong&gt; — created from &lt;code&gt;main&lt;/code&gt; for urgent production fixes.&lt;/p&gt;

&lt;h3&gt;
  
  
  A typical Git Flow lifecycle
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start a new feature&lt;/span&gt;
git switch develop
git switch &lt;span class="nt"&gt;-c&lt;/span&gt; feature/user-profile
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add user profile page"&lt;/span&gt;

&lt;span class="c"&gt;# Merge back into develop via pull request&lt;/span&gt;
git switch develop
git merge feature/user-profile
git branch &lt;span class="nt"&gt;-d&lt;/span&gt; feature/user-profile

&lt;span class="c"&gt;# When ready to release&lt;/span&gt;
git switch &lt;span class="nt"&gt;-c&lt;/span&gt; release/v2.1.0
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Bump version to 2.1.0"&lt;/span&gt;

&lt;span class="c"&gt;# Merge into main and tag&lt;/span&gt;
git switch main
git merge release/v2.1.0
git tag &lt;span class="nt"&gt;-a&lt;/span&gt; v2.1.0 &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Release version 2.1.0"&lt;/span&gt;

&lt;span class="c"&gt;# Merge back into develop&lt;/span&gt;
git switch develop
git merge release/v2.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Who should use it
&lt;/h3&gt;

&lt;p&gt;Git Flow works well for teams with scheduled versioned releases, mobile or desktop apps going through an approval process, and projects that maintain multiple versions simultaneously. It can feel heavy for small teams or fast-moving web projects, but it provides clear structure for complex release cycles.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Trunk-Based Development
&lt;/h2&gt;

&lt;p&gt;Trunk-based development (TBD) is a strategy where all developers commit to a single branch — the trunk, usually &lt;code&gt;main&lt;/code&gt; — as frequently as possible, often multiple times a day.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main (trunk)
  ▲    ▲    ▲    ▲    ▲
  │    │    │    │    │
 Dev1 Dev2 Dev1 Dev3 Dev2
(short-lived branches merged within hours, not days)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The core principles
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Branches are short-lived — hours or a day at most&lt;/li&gt;
&lt;li&gt;Commits to &lt;code&gt;main&lt;/code&gt; are small and frequent&lt;/li&gt;
&lt;li&gt;Broken builds are fixed immediately — they are the team's top priority&lt;/li&gt;
&lt;li&gt;Feature flags control what users see (more on this below)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why teams use it
&lt;/h3&gt;

&lt;p&gt;Trunk-based development avoids the painful merge conflicts that come from long-lived branches. When everyone integrates constantly, there is never a large divergence to reconcile at the end of a sprint.&lt;/p&gt;

&lt;p&gt;It is the approach favoured by high-performing engineering organisations and is closely tied to mature CI/CD pipelines.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who should use it
&lt;/h3&gt;

&lt;p&gt;Teams with strong automated test coverage, mature CI/CD tooling, and experience with feature flags. It can be challenging for beginners without these foundations in place.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Environment-Based Branching
&lt;/h2&gt;

&lt;p&gt;Some teams structure their branches to directly mirror their deployment environments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feature/* ──► develop ──► staging ──► main
                │             │          │
              Dev env    Staging env   Production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each branch maps directly to an environment:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Branch&lt;/th&gt;
&lt;th&gt;Environment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;develop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Development server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;staging&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Staging server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Production&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Merging a PR into &lt;code&gt;develop&lt;/code&gt; automatically deploys to the dev server. Merging into &lt;code&gt;staging&lt;/code&gt; deploys to staging. Merging &lt;code&gt;staging&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt; goes to production.&lt;/p&gt;

&lt;p&gt;This gives every environment a clear, always-current state. Developers, QA, and stakeholders know exactly what is deployed where at all times. The trade-off is that code must move through each environment sequentially, which can slow fast-moving teams down.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Feature Flags
&lt;/h2&gt;

&lt;p&gt;Feature flags (also called feature toggles) let you deploy code to production without making it visible or active for users.&lt;/p&gt;

&lt;p&gt;The code is live in production. The feature is switched off. When the team is ready, they flip the flag — no new deployment needed.&lt;/p&gt;

&lt;h3&gt;
  
  
  A simple example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;featureFlags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newDashboard&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;renderNewDashboard&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;renderOldDashboard&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;h3&gt;
  
  
  Why teams use feature flags
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Deploy incomplete features safely — they are hidden from users&lt;/li&gt;
&lt;li&gt;Roll out features to a percentage of users first (gradual rollout)&lt;/li&gt;
&lt;li&gt;Instantly disable a feature if something goes wrong, without a full rollback deployment&lt;/li&gt;
&lt;li&gt;Enable trunk-based development — unfinished work can be merged to &lt;code&gt;main&lt;/code&gt; without affecting users&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The trade-off
&lt;/h3&gt;

&lt;p&gt;Feature flags add complexity. Old flags that are never cleaned up become permanent dead code. Teams often track flag debt the same way they track technical debt, and schedule regular cleanup cycles.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Pull Requests and Code Reviews
&lt;/h2&gt;

&lt;p&gt;A pull request (PR) — called a merge request (MR) on GitLab — is a formal request to merge one branch into another.&lt;/p&gt;

&lt;p&gt;In real teams, almost no code goes directly into &lt;code&gt;main&lt;/code&gt; or &lt;code&gt;develop&lt;/code&gt; without a pull request. PRs are how teams maintain quality, catch mistakes, and share knowledge across the codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  What a good pull request looks like
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Small&lt;/strong&gt; — focuses on one change. Easier to review and easier to revert.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Well described&lt;/strong&gt; — explains what changed, why it changed, and how to test it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linked to a ticket&lt;/strong&gt; — connects the change to a task in the project tracker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tested&lt;/strong&gt; — includes or references relevant tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A poor PR is large, vague, touches many unrelated files, and is hard to review meaningfully.&lt;/p&gt;

&lt;h3&gt;
  
  
  The code review process
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Developer opens a PR and assigns reviewers&lt;/li&gt;
&lt;li&gt;Reviewers read the code and leave comments&lt;/li&gt;
&lt;li&gt;Developer addresses feedback and pushes updates&lt;/li&gt;
&lt;li&gt;Reviewers approve when satisfied&lt;/li&gt;
&lt;li&gt;PR is merged&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  PR etiquette for beginners
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Do not take review comments personally — the goal is better code, not criticism of you&lt;/li&gt;
&lt;li&gt;Respond to every comment, even if just to confirm you have addressed it&lt;/li&gt;
&lt;li&gt;Keep PRs small. A 50-line PR gets reviewed properly. A 2,000-line PR gets rubber-stamped.&lt;/li&gt;
&lt;li&gt;Write a clear description — reviewers should not have to guess what you changed or why&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  11. What Happens After a Merge?
&lt;/h2&gt;

&lt;p&gt;Many beginners ask: &lt;em&gt;"Okay, I merged. Now what?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is what typically happens after a PR is merged:&lt;/p&gt;

&lt;p&gt;✅ Your feature branch is deleted (either automatically or manually)&lt;/p&gt;

&lt;p&gt;✅ The CI/CD pipeline triggers automatically&lt;/p&gt;

&lt;p&gt;✅ Automated tests run against the merged code&lt;/p&gt;

&lt;p&gt;✅ If tests pass, the application is deployed to the appropriate environment (Dev, Staging, or Production — depending on which branch was merged into)&lt;/p&gt;

&lt;p&gt;✅ Your teammates pull the latest changes before starting new work&lt;/p&gt;

&lt;p&gt;✅ The ticket is marked as done in the project tracker&lt;/p&gt;

&lt;p&gt;✅ Development continues on the next task&lt;/p&gt;

&lt;p&gt;The merge is not the end of the story — it is the handoff point between development and delivery.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. How This Looks on GitHub
&lt;/h2&gt;

&lt;p&gt;Many beginners are comfortable with Git in the terminal but are not sure what to do on the GitHub interface. Here is a quick orientation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Branches&lt;/strong&gt; — visible under the repository's branch dropdown or the Branches tab. Every branch you push appears here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pull Requests&lt;/strong&gt; — opened from the Pull Requests tab. Once you push a branch, GitHub often shows a prompt to open a PR directly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Review comments&lt;/strong&gt; — reviewers leave inline comments on specific lines of code inside the PR. You can reply, resolve, and push new commits to address them — all visible in the PR thread.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI/CD status&lt;/strong&gt; — automated test results appear directly in the PR as green checkmarks or red crosses. A PR with failing checks should not be merged until the issues are resolved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Merge button&lt;/strong&gt; — once reviews are approved and checks pass, the merge button becomes available. On protected branches, it may be restricted to certain team members.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Branch deletion&lt;/strong&gt; — after merging, GitHub offers a one-click option to delete the feature branch. Use it. Stale branches are clutter.&lt;/p&gt;

&lt;p&gt;Understanding the GitHub interface alongside your terminal workflow is what makes the full Git loop feel complete.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. CI/CD and Branch Protection Rules
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Continuous Integration and Continuous Delivery (CI/CD)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Continuous Integration (CI)&lt;/strong&gt; — every push triggers an automated pipeline that runs tests, checks code style, and builds the project. If something fails, the team knows immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Continuous Delivery/Deployment (CD)&lt;/strong&gt; — after tests pass, the code is automatically deployed to an environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  How CI/CD connects to branching
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Pipeline action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Push to any branch&lt;/td&gt;
&lt;td&gt;Run tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PR opened&lt;/td&gt;
&lt;td&gt;Run tests + linting + security checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merge to &lt;code&gt;develop&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Deploy to dev/sandbox environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merge to &lt;code&gt;staging&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Deploy to staging environment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Merge to &lt;code&gt;main&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Deploy to production&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Branch Protection Rules
&lt;/h3&gt;

&lt;p&gt;On GitHub, these are configured under Settings → Branches. Common rules for &lt;code&gt;main&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Require pull request before merging — no direct pushes allowed&lt;/li&gt;
&lt;li&gt;Require approvals — at least one reviewer must approve&lt;/li&gt;
&lt;li&gt;Require status checks to pass — CI tests must pass before merging&lt;/li&gt;
&lt;li&gt;Require branches to be up to date — branch must be current with &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Restrict who can merge — only certain roles can merge to &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a beginner joining a team, you may find that you cannot push to &lt;code&gt;main&lt;/code&gt; directly. This is standard practice that protects everyone, including senior developers.&lt;/p&gt;




&lt;h2&gt;
  
  
  14. Where Does DevOps Fit?
&lt;/h2&gt;

&lt;p&gt;Git is the starting point of the entire modern software delivery pipeline. Here is how a single &lt;code&gt;git push&lt;/code&gt; can trigger a full deployment flow in a DevOps environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer writes code
        │
    git push
        │
      GitHub
        │
   CI Pipeline triggers
        │
   ┌────┴─────────────┐
   │                  │
 Tests run       Linting / SAST
   │                  │
   └────────┬─────────┘
            │
     Security scan
            │
     Build Docker image
            │
     Push to container registry
            │
     Deploy to Dev / Sandbox
            │
     Deploy to Staging
            │
     Manual approval (optional)
            │
     Deploy to Production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every step in this pipeline is triggered by a Git event — a push, a merge, a tag. This is why branching strategy and DevOps pipeline design are inseparable. The branches you define determine what gets built, tested, and deployed, and when.&lt;/p&gt;

&lt;p&gt;Understanding this flow is what takes a developer from "I know Git commands" to "I understand how software ships."&lt;/p&gt;




&lt;h2&gt;
  
  
  15. A Security Perspective
&lt;/h2&gt;

&lt;p&gt;Git workflows are not just about code quality — they are part of your security posture. These are the practices that security-conscious teams implement:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Protect &lt;code&gt;main&lt;/code&gt;&lt;/strong&gt; — enforce branch protection rules so that no single person, however senior, can push directly to production.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Require pull request reviews&lt;/strong&gt; — every change to a critical branch should have at least one other set of eyes on it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Require signed commits&lt;/strong&gt; — Git allows commits to be cryptographically signed, proving they came from a verified author. This matters in regulated industries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Enable secret scanning&lt;/strong&gt; — GitHub's secret scanning automatically detects accidentally committed credentials and alerts you before they are exposed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Restrict force pushes&lt;/strong&gt; — &lt;code&gt;git push --force&lt;/code&gt; can rewrite history and erase evidence of changes. Disable it on protected branches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use least privilege for repository access&lt;/strong&gt; — not everyone needs write access to every repository. Give team members the minimum permissions they need to do their work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Audit logs&lt;/strong&gt; — platforms like GitHub Enterprise provide audit logs of who did what and when. In regulated environments, this is a compliance requirement.&lt;/p&gt;

&lt;p&gt;These practices apply whether you are a developer, a DevOps engineer, or a security professional. Git is infrastructure, and infrastructure needs to be secured.&lt;/p&gt;




&lt;h2&gt;
  
  
  16. Hotfix Scenario: Fixing Production Quickly
&lt;/h2&gt;

&lt;p&gt;A hotfix is an urgent bug fix applied directly to the production codebase, bypassing the normal development flow.&lt;/p&gt;

&lt;h3&gt;
  
  
  When do you need a hotfix?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A critical bug is causing errors for live users&lt;/li&gt;
&lt;li&gt;A security vulnerability has been discovered&lt;/li&gt;
&lt;li&gt;A payment or data process is failing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You cannot wait for the next release cycle. You need to fix it now.&lt;/p&gt;

&lt;h3&gt;
  
  
  The hotfix workflow (Git Flow)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start from main (production code)&lt;/span&gt;
git switch main
git pull
git switch &lt;span class="nt"&gt;-c&lt;/span&gt; hotfix/fix-payment-timeout

&lt;span class="c"&gt;# Make the minimal fix&lt;/span&gt;
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Fix payment gateway timeout handling"&lt;/span&gt;

&lt;span class="c"&gt;# Merge into main and tag&lt;/span&gt;
git switch main
git merge hotfix/fix-payment-timeout
git tag &lt;span class="nt"&gt;-a&lt;/span&gt; v2.1.1 &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Hotfix: payment timeout"&lt;/span&gt;
git push origin main &lt;span class="nt"&gt;--tags&lt;/span&gt;

&lt;span class="c"&gt;# Merge back into develop so the fix is not lost&lt;/span&gt;
git switch develop
git merge hotfix/fix-payment-timeout
git push origin develop

&lt;span class="c"&gt;# Clean up&lt;/span&gt;
git branch &lt;span class="nt"&gt;-d&lt;/span&gt; hotfix/fix-payment-timeout
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The hotfix workflow (GitHub Flow)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch main
git pull
git switch &lt;span class="nt"&gt;-c&lt;/span&gt; hotfix/fix-payment-timeout

&lt;span class="c"&gt;# make the fix&lt;/span&gt;

git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin hotfix/fix-payment-timeout
&lt;span class="c"&gt;# open a PR directly into main&lt;/span&gt;
&lt;span class="c"&gt;# get fast approval from a senior developer&lt;/span&gt;
&lt;span class="c"&gt;# merge and deploy&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Key principles for hotfixes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Keep the fix minimal — only change what is strictly necessary&lt;/li&gt;
&lt;li&gt;Get a second pair of eyes even under pressure — a rushed fix can introduce a new problem&lt;/li&gt;
&lt;li&gt;Merge the hotfix back into &lt;code&gt;develop&lt;/code&gt; immediately, or it will be lost in the next release&lt;/li&gt;
&lt;li&gt;Document what happened — production incidents should be reviewed and learned from&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  17. Which Strategy Do Real Companies Use?
&lt;/h2&gt;

&lt;p&gt;Theory is useful, but it helps to see how these strategies map to real environments.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Organisation type&lt;/th&gt;
&lt;th&gt;Common strategy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Early-stage startup&lt;/td&gt;
&lt;td&gt;GitHub Flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SaaS / web product&lt;/td&gt;
&lt;td&gt;GitHub Flow or Trunk-Based Development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Open source project&lt;/td&gt;
&lt;td&gt;GitHub Flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bank or financial institution&lt;/td&gt;
&lt;td&gt;Git Flow or environment-based branching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Enterprise software&lt;/td&gt;
&lt;td&gt;Git Flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Large tech companies (FAANG)&lt;/td&gt;
&lt;td&gt;Trunk-Based Development with feature flags&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regulated industry (healthcare, fintech)&lt;/td&gt;
&lt;td&gt;Environment-based branching with strict approval gates&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Most mature organisations have evolved their own hybrid. They take the core ideas from these strategies and adapt them to their team size, release cadence, compliance requirements, and tooling.&lt;/p&gt;




&lt;h2&gt;
  
  
  18. Which Branching Strategy Should Beginners Learn First?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Start with GitHub Flow
&lt;/h3&gt;

&lt;p&gt;GitHub Flow is the most beginner-friendly strategy. It has minimal complexity, maps directly to how GitHub works, and teaches the core loop that underpins all other strategies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;branch → commit → pull request → review → merge → deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that loop feels natural, everything else is a variation on it.&lt;/p&gt;

&lt;h3&gt;
  
  
  A simple decision guide
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Your situation&lt;/th&gt;
&lt;th&gt;Recommended strategy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Solo project or learning&lt;/td&gt;
&lt;td&gt;Simple branching or GitHub Flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Small team, deploying often&lt;/td&gt;
&lt;td&gt;GitHub Flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team with scheduled releases&lt;/td&gt;
&lt;td&gt;Git Flow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mature team, strong CI/CD&lt;/td&gt;
&lt;td&gt;Trunk-Based Development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Team with distinct environments&lt;/td&gt;
&lt;td&gt;Environment-based branching&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regulated or compliance-heavy environment&lt;/td&gt;
&lt;td&gt;Git Flow + environment-based branching&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  19. Common Mistakes Teams Make
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Long-lived feature branches&lt;/strong&gt; — a branch that lives for two weeks accumulates painful merge conflicts. Keep branches short-lived. Break large features into smaller, mergeable pieces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pushing directly to &lt;code&gt;main&lt;/code&gt;&lt;/strong&gt; — without branch protection rules, this can and does happen. Even a small accidental push to &lt;code&gt;main&lt;/code&gt; can break production for real users.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skipping code review under pressure&lt;/strong&gt; — "We're in a hurry, just merge it." Rushed merges are where most production incidents are born. A 10-minute review often prevents an hour of debugging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not merging hotfixes back to &lt;code&gt;develop&lt;/code&gt;&lt;/strong&gt; — the fix goes to production but is overwritten in the next release. Always merge hotfixes back to your integration branch immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inconsistent branch naming&lt;/strong&gt; — without conventions, branches become impossible to track. Establish naming standards early and enforce them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Letting feature flags pile up&lt;/strong&gt; — flags that are never cleaned up become permanent dead code. Schedule regular flag cleanup as part of your development cycle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Treating CI failures as optional&lt;/strong&gt; — a red pipeline that nobody fixes is a pipeline nobody trusts. A failing build should be the team's top priority, not a notification to dismiss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ignoring the security basics&lt;/strong&gt; — no branch protection, no secret scanning, no access controls. These are not optional extras — they are part of a professional Git workflow.&lt;/p&gt;




&lt;h2&gt;
  
  
  20. Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Branching strategies are not about following rules for their own sake. They exist to answer one practical question: &lt;em&gt;how do we ship good code, safely, as a team?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The specifics will vary. But the underlying principles are consistent across every team and every strategy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Protect your production environment&lt;/li&gt;
&lt;li&gt;Review code before it merges&lt;/li&gt;
&lt;li&gt;Keep changes small and frequent&lt;/li&gt;
&lt;li&gt;Automate testing and deployment&lt;/li&gt;
&lt;li&gt;Have a clear plan for when things go wrong&lt;/li&gt;
&lt;li&gt;Treat security as part of the workflow, not an afterthought&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Git is not just version control. It is the foundation of modern software delivery. Every pull request, code review, automated test, deployment pipeline, infrastructure change, and production release begins with a Git commit. Understanding Git workflows means understanding how modern software teams build, ship, and maintain reliable systems.&lt;/p&gt;

&lt;p&gt;When you join a team and they say "we use trunk-based development with feature flags and deploy to production ten times a day," you will not just nod along. You will understand exactly what they mean, why it works, and how you fit into it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;In the next article, we'll build a complete CI/CD pipeline around these branching strategies using GitHub Actions — so you'll see exactly what happens after you push your code.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Missed the first article? Read &lt;a href="https://dev.to/benjamin_tetteh/git-for-beginners-a-complete-practical-guide-to-version-control-59i1"&gt;Git for Beginners: A Complete Practical Guide&lt;/a&gt; to start from the beginning.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Found this helpful? Drop a ❤️ or leave a comment below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>devops</category>
      <category>beginners</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Git for Beginners: A Complete Practical Guide to Version Control</title>
      <dc:creator>Benjamin Tetteh</dc:creator>
      <pubDate>Wed, 08 Jul 2026 01:33:36 +0000</pubDate>
      <link>https://dev.to/benjamin_tetteh/git-for-beginners-a-complete-practical-guide-to-version-control-59i1</link>
      <guid>https://dev.to/benjamin_tetteh/git-for-beginners-a-complete-practical-guide-to-version-control-59i1</guid>
      <description>&lt;p&gt;&lt;em&gt;Whether you're writing your first line of code, learning DevOps, cybersecurity, cloud, data, or simply revising your notes — this guide will help you understand Git from the ground up.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you are new to tech, one tool you will meet again and again is &lt;strong&gt;Git&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;At first, Git can feel confusing because it introduces terms like commits, branches, staging, pushing, pulling, merging, and remote repositories.&lt;/p&gt;

&lt;p&gt;But once you understand the basics, Git becomes one of the most useful tools in your workflow. It helps you track changes, collaborate with others, recover from mistakes, and manage your projects professionally.&lt;/p&gt;

&lt;p&gt;This article is written for beginners and for anyone who wants a clear revision guide.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is Git?&lt;/li&gt;
&lt;li&gt;Why Git Matters&lt;/li&gt;
&lt;li&gt;Git vs GitHub&lt;/li&gt;
&lt;li&gt;Key Git Concepts&lt;/li&gt;
&lt;li&gt;Installing Git&lt;/li&gt;
&lt;li&gt;Configuring Git&lt;/li&gt;
&lt;li&gt;Creating Your First Repository&lt;/li&gt;
&lt;li&gt;The Basic Git Workflow&lt;/li&gt;
&lt;li&gt;Working with Branches&lt;/li&gt;
&lt;li&gt;Merging and Rebasing&lt;/li&gt;
&lt;li&gt;Working with Remote Repositories&lt;/li&gt;
&lt;li&gt;Understanding &lt;code&gt;git push origin main&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;.gitignore&lt;/code&gt; File&lt;/li&gt;
&lt;li&gt;Git Security: Do Not Commit Secrets&lt;/li&gt;
&lt;li&gt;Undoing Mistakes&lt;/li&gt;
&lt;li&gt;A Simple Daily Git Workflow&lt;/li&gt;
&lt;li&gt;What Is a Pull Request?&lt;/li&gt;
&lt;li&gt;Useful Git Commands Cheat Sheet&lt;/li&gt;
&lt;li&gt;Common Beginner Mistakes&lt;/li&gt;
&lt;li&gt;Next Steps&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. What is Git?
&lt;/h2&gt;

&lt;p&gt;Git is a &lt;strong&gt;distributed version control system&lt;/strong&gt; — a tool that tracks changes to your files over time so you can save different versions of your project and return to them when needed.&lt;/p&gt;

&lt;p&gt;Think of Git like a detailed save system for your project. Without it, you may end up with files like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;project_final.js
project_final_v2.js
project_final_real_final.js
project_ACTUALLY_final.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git helps you avoid that mess by keeping a clean history of every change.&lt;/p&gt;

&lt;p&gt;Git was created by &lt;strong&gt;Linus Torvalds&lt;/strong&gt; in 2005 — the same person who created the Linux kernel. Today, it is one of the most widely used tools in software development and modern technology work.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Why Git Matters
&lt;/h2&gt;

&lt;p&gt;Git is important because it gives you:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;History&lt;/strong&gt; — See what changed, when it changed, and who made the change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collaboration&lt;/strong&gt; — Multiple people can work on the same project without overwriting each other's work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backup&lt;/strong&gt; — Push your work to platforms like GitHub or GitLab so it is not only stored on your computer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Experimentation&lt;/strong&gt; — Create a separate branch to test a new idea without breaking your main project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accountability&lt;/strong&gt; — Understand why a change was made, not just what changed.&lt;/p&gt;

&lt;p&gt;Whether you are a solo developer, DevOps engineer, cloud engineer, cybersecurity analyst, or part of a large engineering team, Git is an essential skill.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Git vs GitHub
&lt;/h2&gt;

&lt;p&gt;Many beginners confuse &lt;strong&gt;Git&lt;/strong&gt; and &lt;strong&gt;GitHub&lt;/strong&gt;. They are related, but they are not the same thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Git&lt;/strong&gt; is the tool installed on your computer. It tracks changes in your project locally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt; is an online platform where you can store Git repositories, collaborate with others, and share your code publicly or privately.&lt;/p&gt;

&lt;p&gt;A helpful way to think about it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Git is the tool that tracks your project history. GitHub is the online home where you can upload and collaborate on that project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Other platforms similar to GitHub include GitLab, Bitbucket, and Azure DevOps.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Key Git Concepts
&lt;/h2&gt;

&lt;p&gt;Before using Git commands, it helps to understand the vocabulary.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repository&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A project folder tracked by Git&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Commit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A saved snapshot of your changes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Branch&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A separate line of development&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Merge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Combining changes from one branch into another&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Remote&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A version of your repository hosted online&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Clone&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Downloading a remote repository to your computer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Push&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uploading your local commits to a remote repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Pull&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Downloading remote changes into your local repository&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Staging Area&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A holding area where you prepare changes before committing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;HEAD&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A pointer to your current commit or branch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  5. Installing Git
&lt;/h2&gt;

&lt;p&gt;To check if Git is already installed, open your terminal and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="c"&gt;# Example output: git version 2.43.0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  macOS
&lt;/h3&gt;

&lt;p&gt;Git is often pre-installed on macOS. You can also install it via &lt;a href="https://brew.sh" rel="noopener noreferrer"&gt;Homebrew&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Windows
&lt;/h3&gt;

&lt;p&gt;Download the installer from &lt;a href="https://git-scm.com/download/win" rel="noopener noreferrer"&gt;git-scm.com&lt;/a&gt;. The installer also includes &lt;strong&gt;Git Bash&lt;/strong&gt;, a terminal you can use to run Git commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linux (Ubuntu/Debian)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. Configuring Git
&lt;/h2&gt;

&lt;p&gt;Before making your first commit, tell Git who you are. This information is attached to every commit you make.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.name &lt;span class="s2"&gt;"Your Name"&lt;/span&gt;
git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.email &lt;span class="s2"&gt;"you@example.com"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check your configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; &lt;span class="nt"&gt;--list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also set your preferred text editor. For VS Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; core.editor &lt;span class="s2"&gt;"code --wait"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Creating Your First Repository
&lt;/h2&gt;

&lt;p&gt;There are two common ways to start working with Git.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Start a New Project
&lt;/h3&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;my-first-git-project
&lt;span class="nb"&gt;cd &lt;/span&gt;my-first-git-project
git init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;git init&lt;/code&gt; creates a hidden &lt;code&gt;.git&lt;/code&gt; folder inside your project. That folder stores all of Git's tracking information — do not delete it unless you intentionally want to remove Git tracking from the project.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: Clone an Existing Repository
&lt;/h3&gt;

&lt;p&gt;If a project already exists on GitHub or another platform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/username/repository-name.git
&lt;span class="nb"&gt;cd &lt;/span&gt;repository-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. The Basic Git Workflow
&lt;/h2&gt;

&lt;p&gt;The core Git workflow has three stages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Working Directory  →  Staging Area  →  Repository
   (edit files)        (git add)        (git commit)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 1: Make changes
&lt;/h3&gt;

&lt;p&gt;Create, edit, or delete files in your project as normal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Check the status
&lt;/h3&gt;



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

&lt;/div&gt;



&lt;p&gt;This shows which files have changed, which are untracked, and which are staged and ready to commit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Stage your changes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stage a specific file&lt;/span&gt;
git add filename.txt

&lt;span class="c"&gt;# Stage all changes at once&lt;/span&gt;
git add &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Staging lets you choose exactly which changes to include in your next commit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Commit your changes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add README file"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A commit is a saved checkpoint. Write clear, specific messages so your history is useful later.&lt;/p&gt;

&lt;p&gt;Good commit messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add login page"&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Fix broken navigation link"&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Update project documentation"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Avoid vague messages like &lt;code&gt;"stuff"&lt;/code&gt;, &lt;code&gt;"changes"&lt;/code&gt;, or &lt;code&gt;"update"&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: View your history
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log

&lt;span class="c"&gt;# For a cleaner, one-line view&lt;/span&gt;
git log &lt;span class="nt"&gt;--oneline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  9. Working with Branches
&lt;/h2&gt;

&lt;p&gt;Branches allow you to work on features, bug fixes, or experiments without affecting your main project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;main:     A --- B --- C
                      \
feature:               D --- E
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;main&lt;/code&gt; branch holds the stable version of your project. The &lt;code&gt;feature&lt;/code&gt; branch holds work in progress, completely isolated.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create and switch to a branch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a branch&lt;/span&gt;
git branch feature/add-login

&lt;span class="c"&gt;# Switch to it&lt;/span&gt;
git checkout feature/add-login

&lt;span class="c"&gt;# Or create and switch in one step&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feature/add-login

&lt;span class="c"&gt;# With newer Git versions (2.23+)&lt;/span&gt;
git switch &lt;span class="nt"&gt;-c&lt;/span&gt; feature/add-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  List branches
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch        &lt;span class="c"&gt;# local branches only&lt;/span&gt;
git branch &lt;span class="nt"&gt;-a&lt;/span&gt;     &lt;span class="c"&gt;# local and remote branches&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Delete a branch after merging
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git branch &lt;span class="nt"&gt;-d&lt;/span&gt; feature/add-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  10. Merging and Rebasing
&lt;/h2&gt;

&lt;p&gt;Once your branch is ready, you need to bring those changes back into &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Merging
&lt;/h3&gt;

&lt;p&gt;Switch to the branch you want to merge into, then merge:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch main
git merge feature/add-login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling Merge Conflicts
&lt;/h3&gt;

&lt;p&gt;A merge conflict happens when Git cannot automatically combine changes — for example, when two people edit the same line in the same file. Git will mark the conflicting section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD
&lt;/span&gt;&lt;span class="p"&gt;This is the version from main
&lt;/span&gt;&lt;span class="gh"&gt;=======
&lt;/span&gt;&lt;span class="p"&gt;This is the version from your feature branch
&lt;/span&gt;&lt;span class="gi"&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; feature/add-login
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To resolve it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the file and choose the correct final version&lt;/li&gt;
&lt;li&gt;Remove the conflict markers (&lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/code&gt;, &lt;code&gt;=======&lt;/code&gt;, &lt;code&gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Stage the resolved file&lt;/li&gt;
&lt;li&gt;Commit the result
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add filename.txt
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Resolve merge conflict"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Merge conflicts are normal. They are not a sign that you have done something wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rebasing
&lt;/h3&gt;

&lt;p&gt;Rebasing is an alternative to merging that replays your branch's commits on top of another branch, producing a cleaner, linear history.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch feature/add-login
git rebase main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a beginner, do not worry about mastering rebase immediately. Also avoid rebasing branches that other people are already working on, since it rewrites commit history and can cause problems for collaborators.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Working with Remote Repositories
&lt;/h2&gt;

&lt;p&gt;A remote repository is an online version of your project, hosted on GitHub, GitLab, Bitbucket, Azure DevOps, or similar platforms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connect your local project to a remote
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add origin https://github.com/yourusername/your-repo.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check your remotes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Push your code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# First push&lt;/span&gt;
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main

&lt;span class="c"&gt;# Subsequent pushes&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pull and fetch
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Download and apply remote changes to your current branch&lt;/span&gt;
git pull

&lt;span class="c"&gt;# Download remote updates without applying them&lt;/span&gt;
git fetch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of it this way: &lt;code&gt;git fetch&lt;/code&gt; checks what is new online. &lt;code&gt;git pull&lt;/code&gt; checks what is new and brings it into your current branch.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Understanding &lt;code&gt;git push origin main&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Many beginners see this command and wonder what each part means.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the breakdown:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;git push&lt;/code&gt;&lt;/strong&gt; — upload your local commits to a remote repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;origin&lt;/code&gt;&lt;/strong&gt; — the name of the remote. By default, Git uses &lt;code&gt;origin&lt;/code&gt; when you clone a repo or add a remote, but it is just a name, not something special. You could name it anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add github https://github.com/yourusername/your-repo.git
git push github main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;main&lt;/code&gt;&lt;/strong&gt; — the branch you are pushing. Most modern repos use &lt;code&gt;main&lt;/code&gt; as the default, but some use &lt;code&gt;master&lt;/code&gt;, &lt;code&gt;dev&lt;/code&gt;, or &lt;code&gt;staging&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;git push origin main&lt;/code&gt; simply means: &lt;em&gt;"Push my local &lt;code&gt;main&lt;/code&gt; branch to the remote called &lt;code&gt;origin&lt;/code&gt;."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;-u&lt;/code&gt; flag sets an upstream link:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this, Git remembers the connection between your local &lt;code&gt;main&lt;/code&gt; and the remote &lt;code&gt;main&lt;/code&gt;, so future pushes and pulls can be run without specifying the remote and branch each time.&lt;/p&gt;

&lt;p&gt;To check your current branch:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;To check your remote names:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  13. The &lt;code&gt;.gitignore&lt;/code&gt; File
&lt;/h2&gt;

&lt;p&gt;Not everything in your project should be tracked by Git. Common files to exclude include dependency folders, environment files, logs, build outputs, and OS-generated files.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;.gitignore&lt;/code&gt; file in your project root:&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;touch&lt;/span&gt; .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A practical example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Dependencies&lt;/span&gt;
node_modules/

&lt;span class="c"&gt;# Environment variables&lt;/span&gt;
.env
.env.local

&lt;span class="c"&gt;# Logs&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;.log

&lt;span class="c"&gt;# macOS files&lt;/span&gt;
.DS_Store

&lt;span class="c"&gt;# Windows files&lt;/span&gt;
Thumbs.db

&lt;span class="c"&gt;# Build output&lt;/span&gt;
dist/
build/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git will ignore everything listed here. You can find ready-made templates for different languages and frameworks at &lt;a href="https://www.toptal.com/developers/gitignore" rel="noopener noreferrer"&gt;gitignore.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Set up your &lt;code&gt;.gitignore&lt;/code&gt; before your first commit — not after.&lt;/p&gt;




&lt;h2&gt;
  
  
  14. Git Security: Do Not Commit Secrets
&lt;/h2&gt;

&lt;p&gt;This is one of the most important habits to build early.&lt;/p&gt;

&lt;p&gt;Never commit sensitive information such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Passwords&lt;/li&gt;
&lt;li&gt;API keys&lt;/li&gt;
&lt;li&gt;Access tokens&lt;/li&gt;
&lt;li&gt;Private keys&lt;/li&gt;
&lt;li&gt;Database credentials&lt;/li&gt;
&lt;li&gt;Cloud credentials&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.env&lt;/code&gt; files&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example of what &lt;strong&gt;not&lt;/strong&gt; to commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DATABASE_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysecretpassword
&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;examplekey
&lt;span class="nv"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;exampletoken
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you accidentally commit a secret, simply deleting the file later is &lt;strong&gt;not enough&lt;/strong&gt;. Git keeps the full history, so the secret remains visible in past commits. You should immediately rotate or revoke the exposed credential.&lt;/p&gt;

&lt;p&gt;Add these to your &lt;code&gt;.gitignore&lt;/code&gt; as standard practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;.env
&lt;span class="k"&gt;*&lt;/span&gt;.pem
&lt;span class="k"&gt;*&lt;/span&gt;.key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is especially relevant if you are learning cloud, DevOps, or cybersecurity — exposed credentials are one of the most common causes of security incidents.&lt;/p&gt;




&lt;h2&gt;
  
  
  15. Undoing Mistakes
&lt;/h2&gt;

&lt;p&gt;Everyone makes mistakes. Git gives you several ways to recover.&lt;/p&gt;

&lt;h3&gt;
  
  
  Unstage a file
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git restore &lt;span class="nt"&gt;--staged&lt;/span&gt; filename.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Discard changes in your working directory
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Careful: this permanently removes uncommitted changes&lt;/span&gt;
git restore filename.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Fix the last commit message
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;--amend&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Corrected commit message"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only use this if you have not already pushed the commit.&lt;/p&gt;

&lt;h3&gt;
  
  
  Undo the last commit but keep the changes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reset &lt;span class="nt"&gt;--soft&lt;/span&gt; HEAD~1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Undo the last commit and discard the changes
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# ⚠️ Destructive — your work will be lost&lt;/span&gt;
git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; HEAD~1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Safely undo a pushed commit
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git revert commit-id
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;git revert&lt;/code&gt; creates a new commit that reverses the target commit. This is the safe option for shared repositories because it does not rewrite history.&lt;/p&gt;




&lt;h2&gt;
  
  
  16. A Simple Daily Git Workflow
&lt;/h2&gt;

&lt;p&gt;Here is a practical workflow you can use on real projects right now.&lt;/p&gt;

&lt;p&gt;Get the latest changes:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Create a new branch for your work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git switch &lt;span class="nt"&gt;-c&lt;/span&gt; feature/update-readme
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make your changes, then check status:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Stage and commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Update README documentation"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Push your branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin feature/update-readme
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open a pull request on GitHub or your preferred platform for review before merging.&lt;/p&gt;




&lt;h2&gt;
  
  
  17. What Is a Pull Request?
&lt;/h2&gt;

&lt;p&gt;A pull request (PR) is a request to merge your branch into another branch, usually &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Pull requests help teams:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Review code before it is merged&lt;/li&gt;
&lt;li&gt;Discuss proposed changes&lt;/li&gt;
&lt;li&gt;Run automated tests&lt;/li&gt;
&lt;li&gt;Catch mistakes early&lt;/li&gt;
&lt;li&gt;Keep the main branch stable and deployable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if you work alone, pull requests are a good habit. They give you a chance to review your own work before it becomes part of the project history.&lt;/p&gt;




&lt;h2&gt;
  
  
  18. Useful Git Commands Cheat Sheet
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Setup&lt;/span&gt;
git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.name &lt;span class="s2"&gt;"Your Name"&lt;/span&gt;
git config &lt;span class="nt"&gt;--global&lt;/span&gt; user.email &lt;span class="s2"&gt;"you@example.com"&lt;/span&gt;

&lt;span class="c"&gt;# Starting out&lt;/span&gt;
git init
git clone &amp;lt;url&amp;gt;

&lt;span class="c"&gt;# Core workflow&lt;/span&gt;
git status
git add &amp;lt;file&amp;gt;
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"message"&lt;/span&gt;
git log &lt;span class="nt"&gt;--oneline&lt;/span&gt;

&lt;span class="c"&gt;# Branching&lt;/span&gt;
git branch
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; &amp;lt;branch-name&amp;gt;
git switch &lt;span class="nt"&gt;-c&lt;/span&gt; &amp;lt;branch-name&amp;gt;
git switch main
git merge &amp;lt;branch-name&amp;gt;
git branch &lt;span class="nt"&gt;-d&lt;/span&gt; &amp;lt;branch-name&amp;gt;

&lt;span class="c"&gt;# Remote repositories&lt;/span&gt;
git remote &lt;span class="nt"&gt;-v&lt;/span&gt;
git remote add origin &amp;lt;url&amp;gt;
git push &lt;span class="nt"&gt;-u&lt;/span&gt; origin main
git push
git pull
git fetch

&lt;span class="c"&gt;# Inspecting changes&lt;/span&gt;
git diff
git diff &lt;span class="nt"&gt;--staged&lt;/span&gt;
git show &amp;lt;commit-id&amp;gt;

&lt;span class="c"&gt;# Undoing changes&lt;/span&gt;
git restore &lt;span class="nt"&gt;--staged&lt;/span&gt; &amp;lt;file&amp;gt;
git restore &amp;lt;file&amp;gt;
git reset &lt;span class="nt"&gt;--soft&lt;/span&gt; HEAD~1
git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; HEAD~1
git revert &amp;lt;commit-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  19. Common Beginner Mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Committing directly to &lt;code&gt;main&lt;/code&gt;&lt;/strong&gt; — In team projects, always work on a separate branch and merge through a pull request. Keep &lt;code&gt;main&lt;/code&gt; stable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Writing vague commit messages&lt;/strong&gt; — &lt;code&gt;"stuff"&lt;/code&gt; and &lt;code&gt;"changes"&lt;/code&gt; tell future-you nothing. Write messages like &lt;code&gt;"Fix login form validation"&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Forgetting &lt;code&gt;.gitignore&lt;/code&gt;&lt;/strong&gt; — Set it up before your first commit to avoid accidentally tracking &lt;code&gt;node_modules/&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt; files, or build folders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;git reset --hard&lt;/code&gt; carelessly&lt;/strong&gt; — This can permanently remove your work. When in doubt, use &lt;code&gt;--soft&lt;/code&gt; or &lt;code&gt;git stash&lt;/code&gt; to park your changes safely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Force-pushing to shared branches&lt;/strong&gt; — &lt;code&gt;git push --force&lt;/code&gt; can overwrite your teammates' commits. Only use it on private branches when you fully understand the risk.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not pulling before starting work&lt;/strong&gt; — Always run &lt;code&gt;git pull&lt;/code&gt; before beginning new work on a shared project to reduce merge conflicts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Committing secrets&lt;/strong&gt; — As covered above, never commit passwords, API keys, or credentials. Set up &lt;code&gt;.gitignore&lt;/code&gt; and get into this habit from day one.&lt;/p&gt;




&lt;h2&gt;
  
  
  20. Next Steps
&lt;/h2&gt;

&lt;p&gt;You now have a solid foundation in Git. Here is where to go from here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Practice&lt;/strong&gt; — Use Git on a small personal project. Even solo work benefits from Git habits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub&lt;/strong&gt; — Create an account at &lt;a href="https://github.com" rel="noopener noreferrer"&gt;github.com&lt;/a&gt; and push your first repository.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive learning&lt;/strong&gt; — Try &lt;a href="https://learngitbranching.js.org/" rel="noopener noreferrer"&gt;Learn Git Branching&lt;/a&gt;, a free visual tool that makes branching and rebasing click.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Advanced commands&lt;/strong&gt; — Explore &lt;code&gt;git stash&lt;/code&gt;, &lt;code&gt;git cherry-pick&lt;/code&gt;, &lt;code&gt;git bisect&lt;/code&gt;, and Git hooks when you are ready.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conventional commits&lt;/strong&gt; — A standard for writing consistent, meaningful commit messages: &lt;a href="https://www.conventionalcommits.org" rel="noopener noreferrer"&gt;conventionalcommits.org&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to memorise every Git command. The goal is to understand the workflow:&lt;/p&gt;

&lt;p&gt;Make changes. Review changes. Stage changes. Commit changes. Push changes. Collaborate safely.&lt;/p&gt;

&lt;p&gt;Once that clicks, Git stops being intimidating and starts being one of your most reliable tools.&lt;/p&gt;




&lt;p&gt;In the next article, we'll explore how &lt;a href="https://dev.to/benjamin_tetteh/real-world-git-branching-strategies-how-teams-manage-code-in-practical-dev-environments-2gd9"&gt;teams manage code in practical dev environments&lt;/a&gt; using real-world git branching strategies.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Found this helpful? Drop a ❤️ or leave a comment below with any questions. Your feedback is always welcome.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>beginners</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Your AI Writes Code Fast. Here’s How to Check It Before Shipping</title>
      <dc:creator>Benjamin Tetteh</dc:creator>
      <pubDate>Sat, 23 May 2026 14:55:54 +0000</pubDate>
      <link>https://dev.to/benjamin_tetteh/your-ai-writes-code-fast-heres-how-to-check-it-before-shipping-24cj</link>
      <guid>https://dev.to/benjamin_tetteh/your-ai-writes-code-fast-heres-how-to-check-it-before-shipping-24cj</guid>
      <description>&lt;h2&gt;
  
  
  A beginner-friendly guide to building an automated security pipeline with GitHub Actions — from zero, in under an hour.
&lt;/h2&gt;

&lt;p&gt;Here is a scenario that plays out constantly in the world of AI-assisted development.&lt;/p&gt;

&lt;p&gt;You build something. It works. You are excited. It is late. You run through a mental checklist — or most of it — and you push to production. Three items got checked. Two did not. You tell yourself you will fix it tomorrow.&lt;/p&gt;

&lt;p&gt;Tomorrow, you are already building the next thing.&lt;/p&gt;

&lt;p&gt;This is not a discipline problem. This is a systems problem. Checklists depend on human memory and human energy, and both of those fail under shipping pressure. &lt;strong&gt;Automation does not forget. It does not get tired.&lt;/strong&gt; It does not skip the last two items because it is midnight and the feature finally works.&lt;/p&gt;

&lt;p&gt;That is the entire argument for automated security gates. Not that you are careless — but that no human is perfectly consistent, and consistency is exactly what security requires.&lt;/p&gt;

&lt;p&gt;If you read the first article in this series about vibe coding and security, you saw the Moltbook breach — a weekend-built app that exposed 1.5 million API tokens and 35,000 emails because a few security defaults were never configured. The checklist at the end of that article is a good start. This article is about making that checklist run automatically, every single time you push code, without you having to think about it.&lt;/p&gt;

&lt;p&gt;One of the tools that makes this possible is GitHub Actions.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is GitHub Actions, Really?
&lt;/h2&gt;

&lt;p&gt;If you have a GitHub repository — a place where your code lives online — GitHub Actions is a system that lets you run automatic tasks whenever something happens in that repository.&lt;/p&gt;

&lt;p&gt;Something happens could mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You push new code&lt;/li&gt;
&lt;li&gt;You open a pull request&lt;/li&gt;
&lt;li&gt;You merge into your main branch&lt;/li&gt;
&lt;li&gt;You create a new release&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When that trigger fires, GitHub spins up a temporary computer in the cloud, runs whatever instructions you give it, and reports back whether everything passed or failed. You do not manage that computer. You do not pay for it beyond GitHub's free tier limits. It just runs.&lt;/p&gt;

&lt;p&gt;Those instructions live in a file called a &lt;strong&gt;workflow&lt;/strong&gt; — a plain text file written in a format called YAML that lives inside your repository at &lt;code&gt;.github/workflows/&lt;/code&gt;. You can have as many workflow files as you want, each doing different things.&lt;/p&gt;

&lt;p&gt;Here is the simplest possible workflow to make this concrete:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/hello.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;My First Workflow&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;              &lt;span class="c1"&gt;# Run this whenever code is pushed&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;say-hello&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;    &lt;span class="c1"&gt;# Use a fresh Linux computer&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Print a message&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "Code was pushed. The pipeline is running."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is it. A name, a trigger, a machine to run on, and steps to execute. Everything we build in this article follows exactly that same structure — we are just swapping &lt;code&gt;echo "hello"&lt;/code&gt; for real security tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Matters More for Vibe Coders
&lt;/h2&gt;

&lt;p&gt;Traditional developers have something vibe coders are still building: years of conditioned habits. The reflex to check for exposed secrets before pushing. The instinct to verify that a new endpoint requires authentication. The muscle memory of running a linter before committing.&lt;/p&gt;

&lt;p&gt;Those habits took years to form. AI-assisted development is compressing timelines so fast that many people are shipping production apps before those habits exist.&lt;/p&gt;

&lt;p&gt;Automated security gates close that gap. They are not a replacement for understanding security — but they are a safety net that catches common mistakes before they reach production. Think of it less like a replacement for a trained developer and more like spell-check. Spell-check does not make you a better writer, but it catches the embarrassing errors while you focus on the ideas.&lt;/p&gt;

&lt;p&gt;The goal of this article is to give you a working security pipeline by the end. Not a theoretical one. An actual &lt;code&gt;.yml&lt;/code&gt; file you can drop into any project today.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Set It Up: From Zero
&lt;/h2&gt;

&lt;p&gt;You need two things before anything else:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A GitHub repository.&lt;/strong&gt; If your project is not on GitHub yet, create a free account at github.com and push your code there. GitHub has a beginner guide for this if you have never done it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A &lt;code&gt;.github/workflows/&lt;/code&gt; folder&lt;/strong&gt; in your repository. You can create this directly on GitHub by clicking "Add file" inside your repository, or in your code editor locally. GitHub Actions picks up any &lt;code&gt;.yml&lt;/code&gt; file inside that folder automatically.&lt;/p&gt;

&lt;p&gt;That is genuinely all the setup required. No servers, no accounts, no credit cards.&lt;/p&gt;




&lt;h2&gt;
  
  
  Building the Pipeline: Layer by Layer
&lt;/h2&gt;

&lt;p&gt;We are going to build one workflow file that runs three security checks in sequence. Each check catches a different category of problem. You can start with just one and add the others when you are ready — the pipeline is modular by design.&lt;/p&gt;

&lt;p&gt;Here is the full picture of what we are building:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;On every push to main:
│
├── Step 1: Gitleaks — scan for exposed secrets and API keys
├── Step 2: Semgrep — scan for insecure code patterns  
└── Step 3: Snyk or CodeQL — scan for vulnerable dependencies
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each scan catches a different category of problem — secrets, insecure code patterns, and vulnerable dependencies. If any one fails, GitHub stops the pipeline before the code reaches production. You get an email, a red badge on your repository, and a detailed report showing exactly what was found and where.&lt;/p&gt;

&lt;p&gt;The diagram below maps how the pieces connect. Think of it as your security assembly line — every push goes through it automatically, without you having to remember to run anything manually.&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%2Fa2q3cb8p9ugvuky5m1oe.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%2Fa2q3cb8p9ugvuky5m1oe.png" alt="GitHub actions workflow" width="799" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's build each layer.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 1: Catching Exposed Secrets with Gitleaks
&lt;/h3&gt;

&lt;p&gt;Gitleaks scans your codebase for anything that looks like a secret — API keys, database passwords, tokens, private keys. It knows the patterns for hundreds of services: AWS, OpenAI, Stripe, Supabase, GitHub itself.&lt;/p&gt;

&lt;p&gt;This is the Moltbook failure in automated form. If Gitleaks had been running on that repository, the exposed Supabase key would have been flagged before the first commit ever reached production.&lt;/p&gt;

&lt;p&gt;Create a file at &lt;code&gt;.github/workflows/security.yml&lt;/code&gt; and add this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Security Pipeline&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;secret-scan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scan for Exposed Secrets&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Step 1: Download your code onto the pipeline machine&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;    &lt;span class="c1"&gt;# Scan the full git history, not just the latest commit&lt;/span&gt;

      &lt;span class="c1"&gt;# Step 2: Run Gitleaks&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Gitleaks&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gitleaks/gitleaks-action@v2&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;  &lt;span class="c1"&gt;# GitHub provides this automatically&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this does in plain English:&lt;/strong&gt; Every time you push to main or open a pull request, GitHub downloads your code onto a fresh machine and runs Gitleaks across every file and every commit in your history. If it finds anything that looks like a secret, the pipeline fails and you get a detailed report showing exactly which file and which line contains the problem.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fetch-depth: 0&lt;/code&gt; line is important — without it, Gitleaks only scans your most recent commit. With it, it scans your entire history, which catches secrets that were committed weeks ago and never removed.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 2: Catching Insecure Code Patterns with Semgrep
&lt;/h3&gt;

&lt;p&gt;Gitleaks catches secrets. Semgrep catches insecure patterns — things like SQL queries that trust user input directly, authentication checks that can be bypassed, or configuration values that should never be public.&lt;/p&gt;

&lt;p&gt;Semgrep has a free tier and a library of pre-written rules maintained by security researchers. You do not need to write the rules yourself. You just point it at a ruleset and it does the work.&lt;/p&gt;

&lt;p&gt;Add this job to the same &lt;code&gt;security.yml&lt;/code&gt; file, underneath the &lt;code&gt;secret-scan&lt;/code&gt; job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;code-scan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scan for Insecure Code Patterns&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Semgrep&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;returntocorp/semgrep-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
            &lt;span class="s"&gt;p/security-audit&lt;/span&gt;
            &lt;span class="s"&gt;p/secrets&lt;/span&gt;
            &lt;span class="s"&gt;p/javascript&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;SEMGREP_APP_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SEMGREP_APP_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What this does in plain English:&lt;/strong&gt; Semgrep reads through your code looking for patterns that security researchers have identified as dangerous. The &lt;code&gt;p/security-audit&lt;/code&gt; ruleset covers common vulnerabilities. The &lt;code&gt;p/javascript&lt;/code&gt; ruleset adds JavaScript and TypeScript-specific checks. If your project uses Python, swap &lt;code&gt;p/javascript&lt;/code&gt; for &lt;code&gt;p/python&lt;/code&gt;. If it uses both, list both.&lt;/p&gt;

&lt;p&gt;To get your &lt;code&gt;SEMGREP_APP_TOKEN&lt;/code&gt;, create a free account at semgrep.dev, go to Settings, and copy your token. Then in your GitHub repository, go to Settings → Secrets and Variables → Actions → New Repository Secret, and paste it there as &lt;code&gt;SEMGREP_APP_TOKEN&lt;/code&gt;. GitHub stores it encrypted and injects it into the pipeline automatically — your token never appears in your code.&lt;/p&gt;




&lt;h3&gt;
  
  
  Step 3: Catching Vulnerable Dependencies
&lt;/h3&gt;

&lt;p&gt;Your app almost certainly uses packages written by other people — React, Express, Axios, whichever libraries your AI reached for. Those packages sometimes contain known security vulnerabilities. You did not write the vulnerability, but if it is in your app, it is your problem.&lt;/p&gt;

&lt;p&gt;You have two solid options here depending on your comfort level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A: Snyk (beginner-friendly, generous free tier)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Snyk is built for accessibility. It has a VS Code extension, a web dashboard, and a GitHub integration that makes setup straightforward. Add this job to your workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;dependency-scan-snyk&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scan Dependencies with Snyk&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Snyk&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;snyk/actions/node@master&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;SNYK_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SNYK_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--severity-threshold=high&lt;/span&gt;  &lt;span class="c1"&gt;# Only fail on high and critical issues&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Get your &lt;code&gt;SNYK_TOKEN&lt;/code&gt; by creating a free account at snyk.io, going to Account Settings, and copying your Auth Token. Add it to GitHub Secrets the same way you added the Semgrep token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B: CodeQL (built into GitHub, no extra account needed)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;CodeQL is GitHub's own static analysis engine. It is more powerful than Snyk for code-level analysis, slightly more complex to configure, but requires no external account. Replace the Snyk job with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;dependency-scan-codeql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scan with CodeQL&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;security-events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Initialize CodeQL&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github/codeql-action/init@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;languages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;javascript&lt;/span&gt;  &lt;span class="c1"&gt;# Change to python, ruby, etc. if needed&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Perform Analysis&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github/codeql-action/analyze@v3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Results from CodeQL appear directly in your GitHub repository under the Security tab — no external dashboard needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which should you choose?&lt;/strong&gt; Start with Snyk if you want immediate, readable results in plain English. Move to CodeQL when you want deeper analysis integrated directly into GitHub. There is no wrong answer — running either is infinitely better than running neither.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Complete Pipeline
&lt;/h2&gt;

&lt;p&gt;Here is the full &lt;code&gt;security.yml&lt;/code&gt; file with all three jobs combined. Drop this into &lt;code&gt;.github/workflows/security.yml&lt;/code&gt; in any project and your automated security gate is live:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Security Pipeline&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;secret-scan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scan for Exposed Secrets&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gitleaks/gitleaks-action@v2&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;

  &lt;span class="na"&gt;code-scan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scan for Insecure Code Patterns&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;returntocorp/semgrep-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
            &lt;span class="s"&gt;p/security-audit&lt;/span&gt;
            &lt;span class="s"&gt;p/secrets&lt;/span&gt;
            &lt;span class="s"&gt;p/javascript&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;SEMGREP_APP_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SEMGREP_APP_TOKEN }}&lt;/span&gt;

  &lt;span class="na"&gt;dependency-scan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Scan Dependencies&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;snyk/actions/node@master&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;SNYK_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SNYK_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;--severity-threshold=high&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three jobs. Three categories of risk. All running automatically on every push.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reading the Results
&lt;/h2&gt;

&lt;p&gt;When a pipeline run completes, GitHub shows you one of two things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Green checkmarks&lt;/strong&gt; — all scans passed. You are clear to merge or deploy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A red X&lt;/strong&gt; — something was found. Click into the failed job, read the output, and it will tell you exactly what was flagged, in which file, and often why it is a problem.&lt;/p&gt;

&lt;p&gt;Do not treat a red X as a failure. Treat it as the system working. The pipeline caught something before it reached production — which is precisely what it is there to do.&lt;/p&gt;




&lt;h2&gt;
  
  
  One Thing to Do Right Now
&lt;/h2&gt;

&lt;p&gt;If setting up all three jobs at once feels like too much, start with just Gitleaks. Create the &lt;code&gt;.github/workflows/security.yml&lt;/code&gt; file with only the &lt;code&gt;secret-scan&lt;/code&gt; job. Push it to your repository. Watch it run.&lt;/p&gt;

&lt;p&gt;Once you have seen a pipeline run — green or red — the rest becomes much less intimidating. The structure is always the same: trigger, machine, steps. You are just swapping in different tools.&lt;/p&gt;

&lt;p&gt;Security automation is not a one-time setup. It is a habit that compounds. Every project you start from now on gets the pipeline from day one. Every collaborator who opens a pull request gets their code scanned automatically. Every push gets checked before it ships.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That is what consistency looks like when you remove the human from the loop.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Where This Fits in Your Stack
&lt;/h2&gt;

&lt;p&gt;This pipeline covers the code layer — what is in your repository. Combined with the platform-level defaults from the previous article — RLS enabled, rate limiting on, secrets in the right environment variables — you now have security running at two levels simultaneously:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before the code ships&lt;/strong&gt; → GitHub Actions catches it in the pipeline&lt;br&gt;
&lt;strong&gt;After the code ships&lt;/strong&gt; → Platform defaults limit the damage if something slips through&lt;/p&gt;

&lt;p&gt;Neither layer is perfect on its own. Together they cover the vast majority of the failures that appear in real-world breaches — including the ones that made Moltbook a case study.&lt;/p&gt;

&lt;p&gt;You did not need a Computer Science degree to set this up. You needed a &lt;code&gt;.github/workflows/&lt;/code&gt; folder and about an hour. &lt;/p&gt;




&lt;p&gt;&lt;em&gt;The next article in this series covers something slightly different: what to do when the AI itself is part of your pipeline — and the new attack surface that creates.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Found this useful? Share it with someone who just shipped their first repo. The best time to add a security pipeline is before the first breach. The second best time is right now.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>security</category>
      <category>beginners</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>The Hidden Layer Every AI Developer Must Learn</title>
      <dc:creator>Benjamin Tetteh</dc:creator>
      <pubDate>Fri, 22 May 2026 12:22:52 +0000</pubDate>
      <link>https://dev.to/benjamin_tetteh/the-hidden-layer-every-ai-developer-must-learn-be0</link>
      <guid>https://dev.to/benjamin_tetteh/the-hidden-layer-every-ai-developer-must-learn-be0</guid>
      <description>&lt;p&gt;Late January 2026. A developer ships a social network over a weekend. No traditional code written — just prompts, a vision, and an AI that turned ideas into a working product in days. The platform goes viral. Andrej Karpathy, OpenAI co-founder, calls it "the most incredible sci-fi takeoff-adjacent thing I have seen recently."&lt;/p&gt;

&lt;p&gt;Then a security researcher opens the browser's developer tools.&lt;br&gt;
Within minutes, they find an API key sitting in plain JavaScript — visible to anyone who knows how to press F12. They use it to query the production database. No login required. No special tools. Just a simple command and a coffee.&lt;/p&gt;

&lt;p&gt;What comes back: &lt;strong&gt;1.5 million API authentication tokens. 35,000 email addresses. Thousands of private messages.&lt;/strong&gt; The entire platform — every agent, every credential, every conversation — sitting wide open. &lt;/p&gt;

&lt;p&gt;The platform was called Moltbook. The fix, when it came, took two SQL statements.&lt;/p&gt;

&lt;p&gt;This is not a story about a bad developer. It is a story about a gap that AI does not fill automatically — and what you can do about it before you ship.&lt;/p&gt;
&lt;h2&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%2Ff6tvprhmdlon5xqi7vg0.jpg" alt="developer working behind desk" width="799" height="436"&gt;
&lt;/h2&gt;
&lt;h2&gt;
  
  
  The Illusion That Catches Everyone
&lt;/h2&gt;

&lt;p&gt;A few years ago, building software required crossing a painful barrier. You had to learn syntax, frameworks, databases, APIs, Git, deployment — and break things repeatedly along the way. For most people outside tech, software engineering felt like a locked room with a very small door.&lt;/p&gt;

&lt;p&gt;Then AI arrived.&lt;/p&gt;

&lt;p&gt;Now someone with little traditional programming experience can build a SaaS app over a weekend, connect a database to a frontend, integrate payments, and deploy an API — all from prompts. That shift is extraordinary, and I think it is genuinely amazing. I am a vibe coder too. I understand the excitement of watching an idea move from imagination to a working product faster than ever before.&lt;/p&gt;

&lt;p&gt;But there is a dangerous illusion forming around AI-generated software: &lt;strong&gt;If the app works, people assume it's safe. Those are not the same thing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A beautifully designed application can still expose private user data, leak API keys, allow unauthorized access, and accidentally disable database protections — all while the UI looks polished and the login flow works perfectly. Security problems live underneath visible functionality. That is exactly what happened with Moltbook.&lt;/p&gt;


&lt;h2&gt;
  
  
  What AI Gets Right, and What It Quietly Skips
&lt;/h2&gt;

&lt;p&gt;AI is very good at helping you build the happy path — the feature works, the button responds, the API returns data, the page renders. That part is genuinely impressive.&lt;br&gt;
Security lives in the unhappy paths:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What if someone queries your database without logging in?&lt;/li&gt;
&lt;li&gt;What if an attacker manipulates a request from the browser?&lt;/li&gt;
&lt;li&gt;What if your API keys are visible in the page source?&lt;/li&gt;
&lt;li&gt;What if someone calls your registration endpoint ten thousand times in a loop?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Experienced developers ask these questions automatically. Not because they are smarter — but because they built the habit slowly, over years of debugging painful issues, reading post-mortems, and making mistakes in lower-stakes environments.&lt;/p&gt;

&lt;p&gt;AI compresses the implementation timeline dramatically. It does not compress the experience required to ask the right security questions. That gap is where breaches happen.&lt;/p&gt;

&lt;p&gt;And to be clear: this is not only a beginner problem. Even experienced developers can become overconfident when AI accelerates output speed. Because AI-generated code often looks extremely convincing. The explanations sound confident. The architecture appears plausible. The feature functions correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But plausible code is not the same as safe code.&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  The Moltbook Breakdown: What Actually Went Wrong
&lt;/h2&gt;

&lt;p&gt;Moltbook was built on Supabase — a popular, well-documented backend service that is excellent for fast development. Supabase is designed to work with a public API key exposed on the client side. That is intentional and not, by itself, a security failure.&lt;/p&gt;

&lt;p&gt;The security failure is what you configure that key to be able to do.&lt;/p&gt;

&lt;p&gt;Supabase ships with a feature called Row-Level Security — a database setting that ensures users can only access their own data. It is not enabled by default. You have to turn it on. And if you are vibe coding and the AI generates a working backend without you asking about security, there is a good chance that step never comes up.&lt;/p&gt;

&lt;p&gt;At Moltbook, it didn't.&lt;/p&gt;

&lt;p&gt;The result: anyone with basic technical knowledge could query the entire production database — every user's credentials, private messages, and authentication tokens — using the key sitting in plain sight on the website. Write access was also open, meaning an attacker could have edited any post on the platform without logging in.&lt;/p&gt;

&lt;p&gt;The founder's public statement captured the situation honestly: &lt;em&gt;"I didn't write a single line of code for Moltbook. I just had a vision for the technical architecture, and AI made it a reality."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The AI made a working platform. It did not make a secure one.&lt;/p&gt;


&lt;h2&gt;
  
  
  It Is Not Just Moltbook
&lt;/h2&gt;

&lt;p&gt;Six months before Moltbook, security researchers at Wiz found a critical vulnerability in Base44 — a vibe coding platform used by actual enterprises to build internal HR systems, customer databases, and knowledge bases containing sensitive employee data.&lt;/p&gt;

&lt;p&gt;The flaw was shockingly simple: two API endpoints for registering and verifying users required no authentication whatsoever. Using only a value visible in the app's public URL, a researcher could create a verified account inside any private enterprise application on the platform — bypassing SSO and every other access control entirely.&lt;/p&gt;

&lt;p&gt;Here is the detail that changes the conversation: &lt;strong&gt;Base44 builders did not write that vulnerable endpoint. The platform did.&lt;/strong&gt; Individual developers had no visibility into the flaw. This is the second dimension of vibe coding security risk that almost nobody talks about. The first is what AI generates when you prompt it. The second is what the platform you are building on introduces independently of your code.&lt;/p&gt;

&lt;p&gt;Both matter. Both can be addressed.&lt;/p&gt;


&lt;h2&gt;
  
  
  Security in Plain English
&lt;/h2&gt;

&lt;p&gt;One reason security feels intimidating is because the terminology sounds abstract and technical. Most of it isn't. Here is a plain-English translation of the concepts that come up most often:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;What It Actually Means&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;Verifying who someone is&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Authorization&lt;/td&gt;
&lt;td&gt;Deciding what they are allowed to access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Key&lt;/td&gt;
&lt;td&gt;A secret password your app uses to talk to another service&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Row-Level Security&lt;/td&gt;
&lt;td&gt;Preventing users from reading other users' data in your database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate Limiting&lt;/td&gt;
&lt;td&gt;Stopping someone from making thousands of requests in a loop&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secret Scanning&lt;/td&gt;
&lt;td&gt;Automatically detecting exposed passwords or keys in your code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Least Privilege&lt;/td&gt;
&lt;td&gt;Only giving a system the minimum access it actually needs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input Validation&lt;/td&gt;
&lt;td&gt;Making sure users can't send dangerous or unexpected data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Security is not magic. Most of the time it is about reducing obvious mistakes, limiting damage when mistakes happen, and protecting user trust. That's it.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Five Things AI Consistently Misses
&lt;/h2&gt;

&lt;p&gt;These are the patterns that show up repeatedly in AI-generated code — not as exotic edge cases, but as defaults. Each one comes with a practical fix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. API Keys Exposed in Client-Side Code&lt;/strong&gt;&lt;br&gt;
When you ask AI to connect your app to an external service, it will often put the credentials directly in the frontend code. Frontend code is public. Anyone can open the browser's developer tools and find it. Automated scanners crawl the web looking for exactly this.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Run &lt;a href="https://gitleaks.io/" rel="noopener noreferrer"&gt;Gitleaks&lt;/a&gt; before you push code. It scans for secrets and blocks the commit if it finds any. GitHub's built-in secret scanning does the same thing automatically on public repositories — no setup required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Row-Level Security Not Enabled&lt;/strong&gt;&lt;br&gt;
This is the Moltbook failure. AI can generate a complete Supabase schema without ever enabling RLS, because RLS requires understanding your data access model — who should see what — and AI often skips that reasoning unless you ask for it explicitly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; In your Supabase dashboard, check that RLS is enabled on every table containing user data. Then write policies that define exactly who can access each row. The Supabase documentation has a beginner-friendly walkthrough that takes about twenty minutes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Enable RLS on a table&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;agents&lt;/span&gt; &lt;span class="n"&gt;ENABLE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Users can only read their own records&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="nv"&gt;"owner_only"&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;agents&lt;/span&gt; &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt;
&lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;owner_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;3. API Endpoints With No Authentication&lt;/strong&gt;&lt;br&gt;
AI generates routes that respond to requests. Whether it adds authentication to those routes depends on whether you asked — and whether the AI remembered your earlier requirements by the time it got to that file.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Go through every API route and ask: what happens if someone calls this without logging in? Tools like &lt;a href="https://www.bearer.com/" rel="noopener noreferrer"&gt;Bearer&lt;/a&gt; scan your codebase and flag unprotected routes for free. Better still, apply authentication at the middleware layer so every route is protected by default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Secrets on the Wrong Side of the App&lt;/strong&gt;&lt;br&gt;
Many AI tools know not to hardcode secrets and will suggest environment variables. What they sometimes miss is the difference between variables available to the client (public) and variables available only to the server (private).&lt;/p&gt;

&lt;p&gt;In Next.js, any variable prefixed with &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; is bundled into the client JavaScript and visible to everyone. Your OpenAI API key, Stripe secret key, and database credentials should never have that prefix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Search your project for &lt;code&gt;NEXT_PUBLIC_&lt;/code&gt; and verify that nothing sensitive uses it. Server-only secrets get no prefix. Public configuration values — like your Supabase URL — can use the prefix safely, but only if RLS is properly configured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. No Rate Limiting&lt;/strong&gt;&lt;br&gt;
AI generates endpoints that respond to requests. It does not add rate limiting unless you ask. This means your registration endpoint, login endpoint, and data endpoints will accept unlimited requests from anyone.&lt;/p&gt;

&lt;p&gt;At Moltbook, this allowed a single bot to create 500,000 fake accounts. The same pattern can be used to exhaust your API quota overnight and run up a bill that ends your project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; If you are on Vercel, enable rate limiting in your project's security settings — it takes five minutes and requires no code. For Supabase, the same option exists under Project Settings. Do this before you go public.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Mistake Almost Everyone Makes: "I'll Secure It Later"
&lt;/h2&gt;

&lt;p&gt;This mindset is understandable. When you are learning, shipping feels difficult enough already. Security can feel like an advanced topic for a future version of yourself.&lt;/p&gt;

&lt;p&gt;The problem is that insecure architecture hardens very quickly. Once real users arrive, technical debt compounds, insecure patterns spread through the codebase, and leaked secrets cannot be unexposed. The Moltbook breach did not happen because the founder planned to fix security later. It happened because the platform went viral before that moment came.&lt;/p&gt;

&lt;p&gt;Security is not a decorative layer added at the end. It is part of the design. That does not mean you need perfection before you launch. It means security awareness needs to begin at the same time as everything else.&lt;/p&gt;




&lt;h2&gt;
  
  
  Your Security Stack, Kept Simple
&lt;/h2&gt;

&lt;p&gt;You do not need to become a security engineer. You need a short checklist and the discipline to run it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start here — these take under an hour total:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Secret Scanning&lt;/strong&gt; — enabled by default on public repos, catches exposed keys automatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gitleaks&lt;/strong&gt; — run it locally before pushing; blocks commits containing secrets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Supabase RLS&lt;/strong&gt; — enable it on every table in your dashboard; follow the docs walkthrough&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vercel Rate Limiting&lt;/strong&gt; — enable it in your project settings before going public&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snyk free tier&lt;/strong&gt; — scans your dependencies for known vulnerabilities; integrates with VS Code&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When your project gets more serious:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Semgrep&lt;/strong&gt; — static analysis that catches insecure code patterns in CI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CodeQL&lt;/strong&gt; — deeper analysis integrated into your GitHub pull request workflow&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-commit hooks&lt;/strong&gt; — stop dangerous commits before they leave your machine&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of these tools as an automated second opinion. If AI is your fast-moving junior developer, security tooling is your automated reviewer. That combination is far safer than relying on intuition alone — especially when moving quickly.&lt;/p&gt;




&lt;h2&gt;
  
  
  One More Thing: Ask Your AI
&lt;/h2&gt;

&lt;p&gt;One of the most underused techniques in vibe coding security is simply prompting for it. Before you ship any feature that touches user data, try this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Review this code for security issues. Check whether any credentials are exposed in client-side code, whether database tables have Row-Level Security enabled, whether API endpoints require authentication, and whether there is rate limiting on registration and data endpoints."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI is quite capable of security review when asked explicitly. The problem is that it does not apply that review automatically. Make it a habit to ask. It takes thirty seconds and it will catch things.&lt;/p&gt;




&lt;h2&gt;
  
  
  A Fair Hearing for the Critics
&lt;/h2&gt;

&lt;p&gt;The people arguing against vibe coding are not wrong. They are pointing at a real pattern: AI generates working code that skips assumptions a trained developer would never skip. Moltbook and Base44 are valid evidence of that.&lt;/p&gt;

&lt;p&gt;But the conclusion — that people without traditional coding backgrounds should not build things — does not follow. What the evidence actually shows is that vibe coding without security awareness is dangerous. That is a different claim, and it has a different solution.&lt;/p&gt;

&lt;p&gt;The Wiz Research team — the same team that found both breaches — put it clearly: the opportunity is not to slow down vibe coding but to elevate it. AI tools that generate Supabase backends can enable RLS by default. Deployment platforms can scan for exposed credentials automatically. The infrastructure for secure-by-default vibe coding exists. It just is not the default yet.&lt;/p&gt;

&lt;p&gt;Until it is, the gap has to be filled by builders who know what to check — and who check it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before You Ship
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] No API keys or secrets in frontend code
[ ] Gitleaks run before pushing to the repository
[ ] Row-Level Security enabled on every database table with user data
[ ] Every API endpoint requires authentication, or is explicitly marked public
[ ] Rate limiting enabled on registration and data endpoints
[ ] NEXT_PUBLIC_ prefix checked — nothing sensitive uses it
[ ] Snyk or equivalent scanned your dependencies
[ ] Asked AI to review your code specifically for security issues
[ ] Opened the browser dev tools and checked what a stranger can see
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;AI is making software creation more accessible than ever. More people building means more innovation, more creativity, more diverse voices entering technology. That is a genuinely good thing.&lt;/p&gt;

&lt;p&gt;But software engineering has always been more than generating working code. There is a layer underneath every application — trust, security, permissions, responsibility — that still belongs to the person who shipped it.&lt;/p&gt;

&lt;p&gt;You do not need to learn everything before you build. You just need to know the layer exists, and make a habit of checking it.&lt;/p&gt;

&lt;p&gt;The checklist is above. The tools are free. The rest is discipline. The future of software may be AI-assisted. But responsibility is still human.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this useful? Share it with someone who just shipped their first AI-built app. The person who needs it most is usually the one who doesn't know they need it yet.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Building My First AWS VPC and EC2 Environment with Terraform: A Beginner-Friendly Guide for Career Changers</title>
      <dc:creator>Benjamin Tetteh</dc:creator>
      <pubDate>Fri, 08 May 2026 20:47:37 +0000</pubDate>
      <link>https://dev.to/benjamin_tetteh/building-my-first-aws-vpc-with-terraform-a-beginner-friendly-guide-for-career-changers-1elm</link>
      <guid>https://dev.to/benjamin_tetteh/building-my-first-aws-vpc-with-terraform-a-beginner-friendly-guide-for-career-changers-1elm</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F534ixd1wrw7l27rkmb7j.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%2F534ixd1wrw7l27rkmb7j.jpeg" alt="Terraform VPC setup" width="800" height="600"&gt;&lt;/a&gt;Not long ago, terms like “VPC,” “subnet,” and “Terraform” would have made my eyes glaze over. While my background wasn’t originally rooted in cloud engineering, I’ve always been fascinated by how modern infrastructure is designed, automated, and scaled behind the scenes.&lt;/p&gt;

&lt;p&gt;Now, partway through my DevOps journey, I’ve gone from simply reading about cloud infrastructure to actually building it, using Terraform to provision a fully functional AWS network entirely through code.&lt;/p&gt;

&lt;p&gt;If you're reading this while sitting at a career crossroads — maybe you're a teacher, an accountant, a customer service rep, or anyone wondering &lt;em&gt;"can I really break into tech?"&lt;/em&gt; I want this post to be your proof that yes, you absolutely can.&lt;/p&gt;




&lt;h2&gt;
  
  
  Before we touch any code...
&lt;/h2&gt;

&lt;p&gt;Let's understand why everything exists. Tools make a lot more sense that way. Think of it like building a neighbourhood. Imagine you're a city planner given a plot of land. Your job is to:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Draw the boundary of the land&lt;/strong&gt; — this is your VPC (Virtual Private Cloud). It defines your space in AWS. Nothing gets in or out unless you say so.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Divide the land into zones&lt;/strong&gt; — some areas are public (like a shopping street anyone can visit) and some are private (like a gated estate — no outsiders allowed).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build a gate to the outside world&lt;/strong&gt; — this is the Internet Gateway (IGW). The single controlled entrance between your network and the internet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set the traffic rules&lt;/strong&gt; — Route Tables tell your network traffic exactly where to go, like road signs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Construct buildings inside the neighbourhood&lt;/strong&gt; — these are your EC2 instances, the virtual servers that actually run applications and services.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hire security guards for the buildings&lt;/strong&gt; — these are your Security Groups, which act like firewalls controlling who can access your servers and through which doors (ports).&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;💡 &lt;strong&gt;Why Terraform instead of clicking around in AWS?&lt;/strong&gt;&lt;br&gt;
Clicking in the AWS console is slow, hard to repeat, and easy to mess up. Terraform lets you write your infrastructure as code — describe what you want, run one command, and it builds everything consistently every time. This is called &lt;strong&gt;Infrastructure as Code (IaC)&lt;/strong&gt; and employers actively look for this skill.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The project structure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;terraform-vpc/
├── main.tf       # The blueprint — everything to build
├── variables.tf  # The settings — values we can change
└── outputs.tf    # The receipt — shows what got created
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think of &lt;code&gt;main.tf&lt;/code&gt; as the architect's drawing, &lt;code&gt;variables.tf&lt;/code&gt; as the customizable options, and &lt;code&gt;outputs.tf&lt;/code&gt; as the summary report after construction is done.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 1: Setting up Terraform — the provider block&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 5.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first block tells Terraform: &lt;em&gt;"We're working with AWS, and we want version 5 of the AWS plugin."&lt;/em&gt; That plugin is called a &lt;strong&gt;provider&lt;/strong&gt; — think of it as an adapter that lets Terraform talk to AWS.&lt;/p&gt;

&lt;p&gt;The second block specifies which AWS region to build in. A &lt;strong&gt;region&lt;/strong&gt; is a physical location with Amazon data centres — &lt;code&gt;us-east-1&lt;/code&gt; is Northern Virginia, USA.&lt;/p&gt;

&lt;p&gt;Notice &lt;code&gt;var.region&lt;/code&gt;? That means: &lt;em&gt;"look up the value of region in my variables file."&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 2: Creating the VPC — your cloud neighbourhood&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_vpc"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr_block&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"main-vpc"&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;The &lt;code&gt;cidr_block&lt;/code&gt; defines the total size of your network. &lt;code&gt;10.0.0.0/16&lt;/code&gt; gives us room for up to 65,536 addresses — a big plot of land!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tags&lt;/code&gt; are just labels to help you find your resources in the AWS console. Always tag. Your future self will thank you.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 3: Internet Gateway — the front gate&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_internet_gateway"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"main-igw"&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;Without this, your VPC is sealed off — like a neighbourhood with no road to the outside world. Notice &lt;code&gt;vpc_id = aws_vpc.main.id&lt;/code&gt;? Terraform links resources like this. It figures out the build order automatically.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 4: Subnets — carving the zones&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Public subnet (the shopping street)&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"public"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_subnet_cidr_block&lt;/span&gt;
  &lt;span class="nx"&gt;map_public_ip_on_launch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;# Automatically assign public IPs to instances launched in this subnet&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"main-public-subnet"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Private subnet (the gated estate)&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_subnet"&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnet_cidr_block&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"main-private-subnet"&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;We're carving our big VPC into zones. The &lt;strong&gt;public subnet&lt;/strong&gt; (&lt;code&gt;10.0.1.0/24&lt;/code&gt;) is for resources that need internet access, like web servers. The &lt;strong&gt;private subnet&lt;/strong&gt; (&lt;code&gt;10.0.2.0/24&lt;/code&gt;) is for sensitive things like databases — no direct internet access. The &lt;code&gt;/24&lt;/code&gt; gives each subnet 256 addresses.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;map_public_ip_on_launch = true&lt;/code&gt; For resources inside a subnet to actually receive public internet access, instances launched there need public IP addresses. Without a public IP, an EC2 instance cannot be reached from the internet, even if everything else is configured correctly.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 5: Route tables — the traffic signs&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Public route table + association + internet route&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table"&lt;/span&gt; &lt;span class="s2"&gt;"public"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"main-public-rt"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table_association"&lt;/span&gt; &lt;span class="s2"&gt;"public"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;route_table_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route"&lt;/span&gt; &lt;span class="s2"&gt;"public_igw"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;route_table_id&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;destination_cidr_block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;
  &lt;span class="nx"&gt;gateway_id&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_internet_gateway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Private route table + association (no internet route)&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table"&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"main-private-rt"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_route_table_association"&lt;/span&gt; &lt;span class="s2"&gt;"private"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;route_table_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_route_table&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key line is &lt;code&gt;destination_cidr_block = "0.0.0.0/0"&lt;/code&gt; pointing to the IGW. &lt;code&gt;0.0.0.0/0&lt;/code&gt; means &lt;em&gt;"any address on the internet"&lt;/em&gt; — this is what makes the public subnet actually public.&lt;/p&gt;

&lt;p&gt;The private subnet gets its own route table &lt;strong&gt;but no internet route&lt;/strong&gt; — isolated by design. The associations are the connectors that glue each table to its subnet.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 6: Security groups - for the public subnet&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_security_group"&lt;/span&gt; &lt;span class="s2"&gt;"public_sg"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"public-sg"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow SSH and HTTP access"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

  &lt;span class="c1"&gt;# Allow SSH access from anywhere&lt;/span&gt;
  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;22&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# Allow HTTP access from anywhere&lt;/span&gt;
  &lt;span class="nx"&gt;ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"tcp"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;# Allow all outbound traffic&lt;/span&gt;
  &lt;span class="nx"&gt;egress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;from_port&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nx"&gt;to_port&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nx"&gt;protocol&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"-1"&lt;/span&gt;
    &lt;span class="nx"&gt;cidr_blocks&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"0.0.0.0/0"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"main-public-sg"&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;A Security Group is basically a firewall attached to your EC2 instance. It decides who can enter, which ports are open, what type of traffic is allowed.&lt;/p&gt;

&lt;p&gt;This Security Group allows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SSH access (port 22)&lt;/li&gt;
&lt;li&gt;HTTP web traffic (port 80)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;cidr_blocks = ["0.0.0.0/0"]&lt;/code&gt; means &lt;em&gt;“Allow traffic from anywhere on the internet.”&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 7: Create EC2 instance in the public subnet&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_instance"&lt;/span&gt; &lt;span class="s2"&gt;"public_ec2"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ami_id&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_type&lt;/span&gt;
  &lt;span class="nx"&gt;subnet_id&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="c1"&gt;# This places the EC2 inside the public subnet&lt;/span&gt;
  &lt;span class="nx"&gt;security_groups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-public-ec2"&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;&lt;code&gt;ami = var.ami_id&lt;/code&gt; An AMI (Amazon Machine Image) is essentially a template for the operating system. Think of it like installing Windows or Linux onto a new computer. I used an Amazon Linux AMI for this project.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;instance_type = var.instance_type&lt;/code&gt; This defines the size and power of the virtual server. I used &lt;code&gt;t3.micro&lt;/code&gt; (referenced in the &lt;code&gt;variables.tf&lt;/code&gt; file) which is lightweight and great for learning purposes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;subnet_id = aws_subnet.public.id&lt;/code&gt; This places the EC2 instance inside the public subnet we created earlier.&lt;/p&gt;

&lt;p&gt;Then &lt;code&gt;security_groups = [aws_security_group.public_sg.id]&lt;/code&gt; attaches the firewall rules to the server.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 8: Variables — the settings file&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS region to deploy resources in"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"vpc_cidr_block"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CIDR block for the VPC"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"public_subnet_cidr_block"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CIDR block for the public subnet"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.1.0/24"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"private_subnet_cidr_block"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CIDR block for the private subnet"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.2.0/24"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"ami_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AMI ID for the EC2 instance"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ami-0c94855ba95c71c99"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"instance_type"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Instance type for the EC2 instance"&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t3.micro"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of hardcoding values everywhere, I used variables for &lt;code&gt;region&lt;/code&gt;, &lt;code&gt;subnet CIDRs&lt;/code&gt;, &lt;code&gt;AMI ID&lt;/code&gt;, &lt;code&gt;instance type&lt;/code&gt;. This makes the infrastructure easier to modify and reuse later.&lt;/p&gt;

&lt;p&gt;For instance, instead of repeating &lt;code&gt;"us-east-1"&lt;/code&gt; everywhere, we define it once in variables. Want to deploy to a different region? Change it in one place, not everywhere. Clean, reusable, professional. &lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Step 9: Outputs — the receipt&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="c1"&gt;# output the VPC id&lt;/span&gt;
&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# output the public subnet id&lt;/span&gt;
&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"public_subnet_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# output the private subnet id&lt;/span&gt;
&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"private_subnet_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# EC2 Public IP&lt;/span&gt;
&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"ec2_public_ip"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ip&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# EC2 Instance ID&lt;/span&gt;
&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"ec2_instance_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_instance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_ec2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After Terraform finishes, outputs are printed in your terminal — like a receipt. Instead of logging into AWS to find your VPC ID, Public IP, EC2 Instance ID, Terraform just hands it to you. That means you can immediately locate the server, connect to it and verify the deployment worked, without manually digging through the AWS console.&lt;/p&gt;




&lt;h2&gt;
  
  
  Deploying the infrastructure
&lt;/h2&gt;

&lt;p&gt;Once everything was configured, I used the standard Terraform workflow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="nx"&gt;init&lt;/span&gt;     &lt;span class="c1"&gt;# Download the AWS provider and initialize Terraform&lt;/span&gt;
&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="nx"&gt;fmt&lt;/span&gt;      &lt;span class="c1"&gt;# Format the code neatly&lt;/span&gt;
&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="nx"&gt;validate&lt;/span&gt; &lt;span class="c1"&gt;# Check for configuration errors&lt;/span&gt;
&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt;     &lt;span class="c1"&gt;# Preview what Terraform will create&lt;/span&gt;
&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="nx"&gt;apply&lt;/span&gt;    &lt;span class="c1"&gt;# Provision the infrastructure&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when you're done experimenting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight terraform"&gt;&lt;code&gt;&lt;span class="k"&gt;terraform&lt;/span&gt; &lt;span class="nx"&gt;destroy&lt;/span&gt;  &lt;span class="c1"&gt;# Tear everything down to avoid unnecessary AWS charges&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The full architecture
&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%2Fg8kbvi57yhvxb4hvatrm.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%2Fg8kbvi57yhvxb4hvatrm.png" alt=" " width="800" height="867"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Challenges I faced
&lt;/h2&gt;

&lt;p&gt;Like every beginner, I made mistakes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using quotes incorrectly around variables inside &lt;code&gt;main.tf&lt;/code&gt; which broke my configuration&lt;/li&gt;
&lt;li&gt;Confusion around route tables and associations&lt;/li&gt;
&lt;li&gt;Understanding how IGW actually connects to subnets&lt;/li&gt;
&lt;li&gt;Debugging Terraform errors for the first time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But each error helped me understand Terraform and AWS networking better.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;I plan to explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Connecting to the EC2 instance via SSH&lt;/li&gt;
&lt;li&gt;Installing NGINX and hosting a simple webpage&lt;/li&gt;
&lt;li&gt;Terraform modules&lt;/li&gt;
&lt;li&gt;Remote state management with S3&lt;/li&gt;
&lt;li&gt;CI/CD pipelines with GitHub Actions&lt;/li&gt;
&lt;li&gt;Docker and container deployment
I'm still learning and still building. Follow along!&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devops</category>
      <category>terraform</category>
      <category>aws</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Enhancing Cybersecurity in Healthcare: A NIST Cybersecurity Framework Assessment</title>
      <dc:creator>Benjamin Tetteh</dc:creator>
      <pubDate>Sat, 08 Mar 2025 19:10:57 +0000</pubDate>
      <link>https://dev.to/benjamin_tetteh/enhancing-cybersecurity-in-healthcare-a-nist-cybersecurity-framework-assessment-23kd</link>
      <guid>https://dev.to/benjamin_tetteh/enhancing-cybersecurity-in-healthcare-a-nist-cybersecurity-framework-assessment-23kd</guid>
      <description>&lt;p&gt;Cybersecurity threats are an ever-growing concern, especially in industries handling sensitive data like healthcare. To address these risks, I conducted a NIST Cybersecurity Framework (CSF) Assessment for a fictional mid-sized healthcare provider, MediHealth Solutions Inc., as part of my cybersecurity portfolio.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project Overview&lt;/strong&gt;&lt;br&gt;
The goal of this assessment was to evaluate MediHealth’s security posture, identify vulnerabilities, and recommend remediation strategies in alignment with NIST CSF and HIPAA requirements. The assessment covered key cybersecurity domains, including identifying assets, implementing protective measures, detecting threats, responding to incidents, and ensuring recovery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Findings&lt;/strong&gt;&lt;br&gt;
One of the major findings was the presence of legacy system risks. The organization relied on an outdated Electronic Health Records (EHR) system, increasing its exposure to unpatched vulnerabilities. To mitigate this risk, I recommended system upgrades and the deployment of automated patch management. Another critical issue was human factors in cybersecurity. A phishing simulation revealed that 30% of employees fell for phishing attempts, highlighting the need for increased awareness. I proposed a cybersecurity training program using platforms like KnowBe4 and GoPhish to educate employees on recognizing and avoiding phishing attacks.&lt;/p&gt;

&lt;p&gt;Additionally, I identified the absence of an Incident Response Plan (IRP) to handle ransomware and data breaches. Without a structured IRP, the organization risked delayed responses to security incidents. To address this, I developed a comprehensive IRP based on NIST SP 800-61 Rev. 2, outlining clear response procedures and implementing quarterly tabletop exercises to ensure readiness. Weak access controls were another major concern, as critical systems lacked Multi-Factor Authentication (MFA) and Role-Based Access Controls (RBAC). Enforcing MFA for all high-risk accounts and restricting access based on user roles significantly improved the security posture. Furthermore, the lack of centralized monitoring meant that the organization had no Security Information and Event Management (SIEM) system to detect and analyze threats in real-time. To remedy this, I recommended deploying SIEM tools such as Splunk or ELK Stack, along with Intrusion Detection Systems (IDS/IPS) to enhance threat detection and mitigation capabilities.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Relevance to My Cybersecurity Journey&lt;/strong&gt;&lt;br&gt;
As a self-motivated cybersecurity enthusiast, this project was instrumental in refining my skills in risk assessment, incident response, compliance, and security control implementation. Conducting this assessment independently showcased my ability to analyze real-world cybersecurity threats, design security solutions, and align them with industry standards. This hands-on experience reinforced my understanding of security governance, risk management, and compliance (GRC), which are crucial skills for cybersecurity professionals. It also highlights my capability to work autonomously, proactively learn, and apply best practices in enterprise security.&lt;/p&gt;

&lt;p&gt;This project provided invaluable experience in conducting enterprise-wide cybersecurity assessments, aligning security controls with compliance frameworks, and implementing actionable security improvements. It reinforced the importance of a structured approach to risk management, proactive threat detection, and continuous cybersecurity awareness training. Cybersecurity is a constantly evolving field that requires a mix of technical expertise and risk-based decision-making. This NIST assessment has been a valuable addition to my cybersecurity portfolio, demonstrating my ability to analyze security gaps and implement industry-standard security measures.&lt;/p&gt;

&lt;p&gt;📌 Check out the full assessment &lt;a href="https://github.com/BenjaminTetteh/Cybersecurity-Portfolio/blob/main/Enterprise-wide%20NIST%20cybersecurity%20framework%20assessment.pdf" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;💬 Let’s discuss! Have you worked with the NIST Cybersecurity Framework before? How do you approach security risk assessments in your projects?&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>governance</category>
      <category>infosec</category>
      <category>compliance</category>
    </item>
    <item>
      <title>From Data Breach to Insight: Exploring the Intersection of Cybersecurity and Communication</title>
      <dc:creator>Benjamin Tetteh</dc:creator>
      <pubDate>Sat, 08 Mar 2025 18:22:00 +0000</pubDate>
      <link>https://dev.to/benjamin_tetteh/from-data-breach-to-insight-exploring-the-intersection-of-cybersecurity-and-communication-1f3b</link>
      <guid>https://dev.to/benjamin_tetteh/from-data-breach-to-insight-exploring-the-intersection-of-cybersecurity-and-communication-1f3b</guid>
      <description>&lt;p&gt;I recently received an email notifying me of a data breach at a major public service provider in London that I rely on. Before this, I never truly considered that I could be directly impacted by a breach, even though they’re frequently reported in the media. With incidents like these becoming increasingly common, it was unsettling to think that my personal data may have been compromised. However, the service provider has been proactive, sending follow-up emails in the weeks following the initial notification, outlining the incident and their remediation efforts. As a communications professional, I found their response reassuring.&lt;/p&gt;

&lt;p&gt;Data breaches are becoming so frequent that they’re starting to feel like notifications from my telecom provider—constant, annoying, and impossible to ignore.&lt;/p&gt;

&lt;p&gt;Coincidentally, I’d been enrolled in the Google Cybersecurity Certificate course. My initial foray into cybersecurity was driven by the assumption that I would be diving into a highly technical world—one filled with firewalls, encryption, and endless lines of code, likely while wearing a hoodie in a dark room. While I certainly encountered that (minus the hoodie), what piqued my interest was discovering how crucial communication is within the cybersecurity landscape.&lt;/p&gt;

&lt;p&gt;In today's hyper-connected world, cybersecurity has become one of the most critical aspects of every organization's operational strategy. It’s not just about protecting sensitive data or ensuring business continuity; it’s also about building and maintaining trust with customers. I’ve come to realize that cybersecurity is more than just a technical responsibility; it’s also a communications challenge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Intersection&lt;/strong&gt;&lt;br&gt;
Why? Because no matter how sophisticated an organization's defenses are, human error remains one of the most significant risks. This is where effective communication plays a pivotal role. While studying incident response plans, I realized that when a data breach occurs, it’s not just the IT team scrambling behind the scenes to secure systems and data. The communications team is equally essential in ensuring that stakeholders—whether customers, employees, or partners—are informed and reassured. Organizations are legally obligated to disclose breaches, and the quality, clarity, and timeliness of that communication often determine whether trust is preserved or lost. Cybersecurity professionals may patch vulnerabilities and mitigate future risks, but without clear, strategic communication, even the best technical response can leave people in the dark and cause unnecessary panic.&lt;/p&gt;

&lt;p&gt;One of the most critical elements of cybersecurity is awareness. Many threats—from phishing to social engineering—target the weakest link: people. Ensuring that employees and stakeholders understand the risks and how to avoid them requires more than a one-time memo or a check-the-box training module. It requires consistent, clear, and engaging communication. By translating complex technical concepts into easily understandable information, communicators can help create a culture of security. This extends to everything from regular awareness campaigns to engaging content that demystifies topics like password security, device protection, and data privacy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gaining Technical Expertise: The Next Frontier&lt;/strong&gt;&lt;br&gt;
On the flip side, my experience with the technical aspects of cybersecurity has also been eye-opening. Through the Google Cybersecurity Certificate, I’ve gained hands-on experience with tools like Python, Linux, and SQL, and I’ve worked with Security Information and Event Management (SIEM) tools to identify risks and mitigate threats. Understanding these technologies has allowed me to better appreciate the technical side of cybersecurity.&lt;/p&gt;

&lt;p&gt;My observations and learnings from the past weeks have made me appreciate the relationship between cybersecurity and communications. Both disciplines require a keen understanding of risk, an ability to anticipate and mitigate problems, and a focus on protecting people—whether through securing data or ensuring that information is clear and accessible. As I continue to explore both fields, I’m excited by the possibilities that lie at this intersection.&lt;/p&gt;

&lt;p&gt;On to the next.&lt;/p&gt;

&lt;p&gt;PS: I first published this article on my LinkedIn profile on 16/9/24.&lt;/p&gt;

</description>
      <category>security</category>
      <category>awareness</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
