<?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: Trevor Zabar</title>
    <description>The latest articles on DEV Community by Trevor Zabar (@zabartrevor).</description>
    <link>https://dev.to/zabartrevor</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%2F4071751%2F6a4c7790-3941-46d0-8102-a02efea6a7db.png</url>
      <title>DEV Community: Trevor Zabar</title>
      <link>https://dev.to/zabartrevor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zabartrevor"/>
    <language>en</language>
    <item>
      <title>Power BI Data Modelling, Relationships &amp; Joins: A Practical Technical Guide</title>
      <dc:creator>Trevor Zabar</dc:creator>
      <pubDate>Sun, 13 Sep 2026 11:50:30 +0000</pubDate>
      <link>https://dev.to/zabartrevor/power-bi-data-modelling-relationships-joins-a-practical-technical-guide-3i7g</link>
      <guid>https://dev.to/zabartrevor/power-bi-data-modelling-relationships-joins-a-practical-technical-guide-3i7g</guid>
      <description>&lt;p&gt;Building a robust Power BI solution starts long before the first visual is placed on a report canvas. The foundation of every performant, scalable, and maintainable Power BI report is its data model. This article explains the core concepts of data modelling in Power BI—schemas, fact and dimension tables, relationships, filter direction, and joins—and shows how to apply them when designing a real-world solution.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Data Modelling in Power BI
&lt;/h2&gt;

&lt;p&gt;Data modelling in Power BI is the process of organizing your data into tables, defining how those tables relate to one another, and structuring them so that reports, DAX calculations, and queries perform efficiently. A well-designed data model:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enables accurate and consistent reporting across multiple visuals and pages.&lt;/li&gt;
&lt;li&gt;Simplifies DAX by reducing the need for complex workarounds.&lt;/li&gt;
&lt;li&gt;Improves query performance and reduces memory usage.&lt;/li&gt;
&lt;li&gt;Scales as data volumes and business requirements grow.&lt;/li&gt;
&lt;li&gt;Makes the model easier to understand, maintain, and extend.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Power BI supports three common schema types: flat (fully denormalized), star, and snowflake.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1 Flat Table (Fully Denormalized)
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Definition:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A flat table schema stores all attributes—facts and dimensions—in a single, wide table. There are no relationships because there is only one table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structure:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Every row contains both transactional data (e.g., sales amount, quantity) and descriptive attributes (e.g., customer name, product category, region).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple to understand for beginners.&lt;/li&gt;
&lt;li&gt;No relationships to manage.&lt;/li&gt;
&lt;li&gt;Quick to build for very small, one-off analyses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High data redundancy (same customer, product, or date repeated many times).&lt;/li&gt;
&lt;li&gt;Larger model size and slower refresh.&lt;/li&gt;
&lt;li&gt;Harder to maintain when attributes change (e.g., customer address updates).&lt;/li&gt;
&lt;li&gt;DAX can become verbose when implementing time intelligence or complex filters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When appropriate:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Small datasets, quick prototypes, or when source data is already a single denormalized extract and performance is not a concern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implications for Power BI:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Flat models often lead to larger in-memory models and can degrade performance as data grows. They also make it harder to reuse dimension logic across multiple fact tables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Illustration (Flat Table):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---------------------------------------------------------------------------+
|                           FactSales_Flat                                  |
+---------------------------------------------------------------------------+
| SaleID | Date       | CustomerID | CustomerName | ProductID | ProductName |
|--------|------------|------------|--------------|-----------|-------------|
| 1001   | 2025-01-01 | C001       | Alice        | P100      | Laptop      |
| 1002   | 2025-01-01 | C002       | Bob          | P101      | Mouse       |
| 1003   | 2025-01-02 | C001       | Alice        | P102      | Keyboard    |
+---------------------------------------------------------------------------+
| Quantity | Amount | Region | Category | ... (many more columns)          |
|----------|--------|--------|----------|----------------------------------|
| 1        | 1200   | Nairobi| IT       | ...                              |
| 2        | 50     | Mombasa| IT       | ...                              |
| 1        | 80     | Nairobi| IT       | ...                              |
+---------------------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  1.2 Star Schema
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Definition:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A star schema consists of one central fact table surrounded by multiple dimension tables. Each dimension connects directly to the fact table via a one-to-many relationship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structure:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fact table:&lt;/strong&gt; Contains measurable business events (e.g., sales, orders).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dimension tables:&lt;/strong&gt; Contain descriptive attributes used for slicing and dicing (e.g., customer, product, date).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Optimized for Power BI's VertiPaq engine and DAX.&lt;/li&gt;
&lt;li&gt;Clear separation of facts and dimensions improves readability.&lt;/li&gt;
&lt;li&gt;Efficient filter propagation from dimensions to facts.&lt;/li&gt;
&lt;li&gt;Easier to extend with new dimensions or facts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requires more upfront design than a flat table.&lt;/li&gt;
&lt;li&gt;Multiple tables mean relationships must be managed correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When appropriate:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Most business intelligence scenarios, especially when you have clear business processes (sales, orders, transactions) and descriptive dimensions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implications for Power BI:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Star schemas are the recommended default in Power BI. They balance performance, simplicity, and scalability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Illustration (Star Schema):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                DimDate
                  |
                  | 1:*
                  v
    DimCustomer &amp;lt;--+--&amp;gt; FactSales &amp;lt;--+--&amp;gt; DimProduct
       1:*         |       1:*       |        1:*
                   |                 |
                   v                 v
               DimLocation       DimCategory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each arrow represents a one-to-many relationship from the dimension (1) to the fact (*).&lt;/p&gt;




&lt;h3&gt;
  
  
  1.3 Snowflake Schema
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Definition:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
A snowflake schema is a normalized version of a star schema where some dimension tables are further split into related sub-dimensions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structure:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Dimensions are normalized into multiple related tables. For example, &lt;code&gt;DimProduct&lt;/code&gt; might link to &lt;code&gt;DimCategory&lt;/code&gt;, which links to &lt;code&gt;DimSubCategory&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reduces data redundancy in dimensions.&lt;/li&gt;
&lt;li&gt;Useful when dimensions are large and highly normalized in the source system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More complex model with more relationships to manage.&lt;/li&gt;
&lt;li&gt;Can slightly degrade query performance due to extra joins.&lt;/li&gt;
&lt;li&gt;DAX can become more complex when navigating multiple dimension layers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When appropriate:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
When dimensions are very large, highly normalized, or shared across multiple fact tables in a way that justifies normalization.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Implications for Power BI:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Snowflake schemas can work well but often add unnecessary complexity in Power BI, where denormalized dimensions are usually more efficient.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Illustration (Snowflake Schema):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                DimDate
                  |
                  | 1:*
                  v
    DimCustomer &amp;lt;--+--&amp;gt; FactSales &amp;lt;--+--&amp;gt; DimProduct
       1:*         |       1:*       |        1:*
                   |                 |
                   v                 v
               DimLocation       DimCategory
                                   |
                                   | 1:*
                                   v
                               DimSubCategory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Fact Tables and Dimension Tables
&lt;/h2&gt;

&lt;p&gt;Understanding the difference between fact and dimension tables is central to designing a good star schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 Fact Tables
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What they store:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Fact tables store measurable business events or transactions. Typical columns include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Foreign keys to dimension tables (e.g., &lt;code&gt;CustomerID&lt;/code&gt;, &lt;code&gt;ProductID&lt;/code&gt;, &lt;code&gt;DateID&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Numeric measures (e.g., &lt;code&gt;Quantity&lt;/code&gt;, &lt;code&gt;SalesAmount&lt;/code&gt;, &lt;code&gt;Cost&lt;/code&gt;, &lt;code&gt;Profit&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Characteristics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usually the largest table in the model.&lt;/li&gt;
&lt;li&gt;One row represents a single occurrence of a business event at a specific grain.&lt;/li&gt;
&lt;li&gt;Grain (granularity) defines what each row represents (e.g., one line item on an invoice, one order, one daily snapshot).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;FactSales&lt;/code&gt;: One row per sales transaction line.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FactOrders&lt;/code&gt;: One row per order or order line.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FactTransactions&lt;/code&gt;: One row per financial transaction.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2.2 Dimension Tables
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What they store:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Dimension tables store descriptive attributes used to slice, filter, and group facts. Typical columns include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A unique key (e.g., &lt;code&gt;CustomerID&lt;/code&gt;, &lt;code&gt;ProductID&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Descriptive attributes (e.g., &lt;code&gt;CustomerName&lt;/code&gt;, &lt;code&gt;City&lt;/code&gt;, &lt;code&gt;ProductCategory&lt;/code&gt;, &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;Month&lt;/code&gt;, &lt;code&gt;Year&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Characteristics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Usually smaller than fact tables.&lt;/li&gt;
&lt;li&gt;Keys are unique within the dimension.&lt;/li&gt;
&lt;li&gt;Attributes are often textual or categorical.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DimCustomer&lt;/code&gt;: &lt;code&gt;CustomerID&lt;/code&gt;, &lt;code&gt;CustomerName&lt;/code&gt;, &lt;code&gt;Segment&lt;/code&gt;, &lt;code&gt;Region&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DimProduct&lt;/code&gt;: &lt;code&gt;ProductID&lt;/code&gt;, &lt;code&gt;ProductName&lt;/code&gt;, &lt;code&gt;Category&lt;/code&gt;, &lt;code&gt;Brand&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DimDate&lt;/code&gt;: &lt;code&gt;DateID&lt;/code&gt;, &lt;code&gt;Date&lt;/code&gt;, &lt;code&gt;Day&lt;/code&gt;, &lt;code&gt;Month&lt;/code&gt;, &lt;code&gt;Quarter&lt;/code&gt;, &lt;code&gt;Year&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DimLocation&lt;/code&gt;: &lt;code&gt;LocationID&lt;/code&gt;, &lt;code&gt;City&lt;/code&gt;, &lt;code&gt;Region&lt;/code&gt;, &lt;code&gt;Country&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  2.3 Practical Example: Star Schema for Sales
&lt;/h3&gt;

&lt;p&gt;Consider a retail business tracking sales. The central fact table is &lt;code&gt;FactSales&lt;/code&gt;, with dimensions for customer, product, date, and location.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;FactSales (grain: one row per sales line item):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FactSales
---------
SaleID (PK)
DateID (FK)
CustomerID (FK)
ProductID (FK)
LocationID (FK)
Quantity
SalesAmount
Cost
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Dimensions:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DimCustomer
-----------
CustomerID (PK)
CustomerName
Segment
Region

DimProduct
----------
ProductID (PK)
ProductName
Category
Brand

DimDate
-------
DateID (PK)
Date
Day
Month
Quarter
Year

DimLocation
-----------
LocationID (PK)
City
Region
Country
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Star schema diagram:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                DimDate
                  |
                  | 1:*
                  v
    DimCustomer &amp;lt;--+--&amp;gt; FactSales &amp;lt;--+--&amp;gt; DimProduct
       1:*         |       1:*       |        1:*
                   |                 |
                   v                 v
               DimLocation       (other dims as needed)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each dimension filters &lt;code&gt;FactSales&lt;/code&gt; through a one-to-many relationship, enabling intuitive slicing (e.g., sales by customer, by product, by month).&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Relationships in Power BI
&lt;/h2&gt;

&lt;p&gt;A relationship in Power BI defines how two tables are connected via key columns, enabling filter propagation and accurate aggregations across tables. Without relationships, Power BI cannot correctly combine data from multiple tables in visuals or DAX.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Relationship Cardinalities
&lt;/h3&gt;

&lt;p&gt;Power BI supports three main cardinalities: one-to-many (1:&lt;em&gt;), one-to-one (1:1), and many-to-many (&lt;/em&gt;:*).&lt;/p&gt;

&lt;h4&gt;
  
  
  3.1.1 One-to-Many (1:*)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
One row in the "one" table (usually a dimension) relates to many rows in the "many" table (usually a fact).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;DimCustomer[CustomerID]&lt;/code&gt; (unique) → &lt;code&gt;FactSales[CustomerID]&lt;/code&gt; (repeated).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This is the most common relationship in BI models, connecting dimensions to facts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When not to use:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Avoid using 1:* where many-to-many logic is actually required (e.g., students and courses via an enrollment bridge).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Illustration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DimCustomer                 FactSales
------------                ---------
CustomerID (PK)  ----1:*---&amp;gt; CustomerID (FK)
CustomerName                SaleID
Segment                     Quantity
                            SalesAmount
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3.1.2 One-to-One (1:1)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Each row in Table A matches at most one row in Table B, and vice versa.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;DimEmployee&lt;/code&gt; and &lt;code&gt;DimEmployeeDetails&lt;/code&gt;, where each employee has exactly one detail record.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Rarely needed in Power BI. Often indicates that two tables should be merged into one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When not to use:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
If the relationship is truly 1:1 and within the same source, it's usually better to merge the tables in Power Query to simplify the model.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Illustration:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DimEmployee          DimEmployeeDetails
-----------          ------------------
EmployeeID (PK)  1:1  EmployeeID (PK)
Name                 HireDate
Department           SalaryGrade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3.1.3 Many-to-Many (&lt;em&gt;:&lt;/em&gt;)
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Multiple rows in Table A can relate to multiple rows in Table B.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Students and Courses via an Enrollment bridge table, or products and promotions where a product can have many promotions and a promotion can apply to many products.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
When there is no direct 1:* path and a bridge table is required to correctly model the business logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When not to use:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Avoid many-to-many between fact tables. Instead, relate each fact to shared dimensions in a star schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Illustration (via bridge):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DimProduct 1:* BridgeProductPromo :* DimPromotion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Power BI guidance recommends implementing many-to-many via a bridge table with two 1:* relationships, rather than a direct &lt;em&gt;:&lt;/em&gt; relationship where possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 Keys, Uniqueness, Cardinality, and Referential Integrity
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Primary Key (PK):&lt;/strong&gt; A column (or set of columns) that uniquely identifies each row in a table (e.g., &lt;code&gt;CustomerID&lt;/code&gt; in &lt;code&gt;DimCustomer&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Foreign Key (FK):&lt;/strong&gt; A column in one table that references a primary key in another (e.g., &lt;code&gt;CustomerID&lt;/code&gt; in &lt;code&gt;FactSales&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unique values:&lt;/strong&gt; Dimension keys should be unique; fact table keys can repeat.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cardinality:&lt;/strong&gt; Describes the numeric relationship between rows in related tables (1:1, 1:&lt;em&gt;, *:&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Referential integrity:&lt;/strong&gt; Ensures that every foreign key value in the fact table has a matching primary key in the dimension. Violations (orphaned keys) can cause incorrect results or BLANKs in visuals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Active vs inactive relationships:&lt;/strong&gt; Only one active path can exist between two tables for filter propagation. Additional relationships can be defined as inactive and used in DAX via &lt;code&gt;USERELATIONSHIP&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;DimCustomer[CustomerID]&lt;/code&gt; is unique (each customer appears once). In &lt;code&gt;FactSales&lt;/code&gt;, &lt;code&gt;CustomerID&lt;/code&gt; appears many times because a customer can make many purchases. This is a classic 1:* relationship.&lt;/p&gt;


&lt;h2&gt;
  
  
  4. Filter Direction
&lt;/h2&gt;

&lt;p&gt;Filters in Power BI propagate along relationship paths. Understanding filter direction is critical for predictable behavior.&lt;/p&gt;
&lt;h3&gt;
  
  
  4.1 Single-Direction Filtering
&lt;/h3&gt;

&lt;p&gt;By default, relationships filter from the "one" side to the "many" side.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Selecting "Laptop" in &lt;code&gt;DimProduct[ProductName]&lt;/code&gt; filters &lt;code&gt;FactSales&lt;/code&gt; to only rows where &lt;code&gt;ProductID&lt;/code&gt; matches "Laptop".&lt;/p&gt;

&lt;p&gt;Flow:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DimProduct (1) --filters--&amp;gt; FactSales (*)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the recommended default because it is simple and avoids ambiguity.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Bidirectional (Both) Filtering
&lt;/h3&gt;

&lt;p&gt;Bidirectional filtering allows filters to flow in both directions across a relationship.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
With bidirectional filtering between &lt;code&gt;DimProduct&lt;/code&gt; and &lt;code&gt;FactSales&lt;/code&gt;, selecting a product filters sales, and selecting a sales record (e.g., via a visual filter) can also filter the product dimension.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use carefully:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Bidirectional filtering can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create ambiguous filter paths when multiple routes exist between tables.&lt;/li&gt;
&lt;li&gt;Increase model complexity and make behavior harder to predict.&lt;/li&gt;
&lt;li&gt;Degrade performance in large models.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best practice:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Use single-direction filtering by default. Enable bidirectional filtering only when there is a clear need and after testing for ambiguity and performance.&lt;/p&gt;


&lt;h2&gt;
  
  
  5. Joins in Power Query
&lt;/h2&gt;

&lt;p&gt;In Power Query, joins are performed using &lt;strong&gt;Merge Queries&lt;/strong&gt;. A merge combines columns from two queries based on matching key columns. This happens during the data transformation stage, before the data is loaded into the model.&lt;/p&gt;

&lt;p&gt;Consider two tables:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Customers:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Customers
---------
CustomerID | CustomerName | Region
C001       | Alice        | Nairobi
C002       | Bob          | Mombasa
C003       | Carol        | Kisumu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Orders:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Orders
------
OrderID | CustomerID | OrderDate  | Amount
O100    | C001       | 2025-01-01 | 500
O101    | C002       | 2025-01-02 | 300
O102    | C004       | 2025-01-03 | 700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: &lt;code&gt;C003&lt;/code&gt; has no orders; &lt;code&gt;C004&lt;/code&gt; has orders but is not in &lt;code&gt;Customers&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 Left Outer Join
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Definition:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Returns all rows from the left table (first table) and matching rows from the right table. Unmatched rows from the right table appear as nulls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retained records:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
All customers; orders only where &lt;code&gt;CustomerID&lt;/code&gt; matches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example (Customers LEFT OUTER JOIN Orders on CustomerID):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CustomerID | CustomerName | Region  | OrderID | OrderDate  | Amount
-----------|--------------|---------|---------|------------|-------
C001       | Alice        | Nairobi | O100    | 2025-01-01 | 500
C002       | Bob          | Mombasa | O101    | 2025-01-02 | 300
C003       | Carol        | Kisumu  | null    | null       | null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
List all customers and their orders, including customers with no orders.&lt;/p&gt;


&lt;h3&gt;
  
  
  5.2 Right Outer Join
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Definition:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Returns all rows from the right table and matching rows from the left table. Unmatched rows from the left table appear as nulls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retained records:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
All orders; customers only where &lt;code&gt;CustomerID&lt;/code&gt; matches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example (Customers RIGHT OUTER JOIN Orders on CustomerID):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CustomerID | CustomerName | Region  | OrderID | OrderDate  | Amount
-----------|--------------|---------|---------|------------|-------
C001       | Alice        | Nairobi | O100    | 2025-01-01 | 500
C002       | Bob          | Mombasa | O101    | 2025-01-02 | 300
C004       | null         | null    | O102    | 2025-01-03 | 700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
List all orders and their customers, including orders with missing customer records.&lt;/p&gt;


&lt;h3&gt;
  
  
  5.3 Full Outer Join
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Definition:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Returns all rows from both tables, matching where possible. Unmatched rows from either side appear with nulls for the other table's columns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retained records:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
All customers and all orders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example (Customers FULL OUTER JOIN Orders on CustomerID):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CustomerID | CustomerName | Region  | OrderID | OrderDate  | Amount
-----------|--------------|---------|---------|------------|-------
C001       | Alice        | Nairobi | O100    | 2025-01-01 | 500
C002       | Bob          | Mombasa | O101    | 2025-01-02 | 300
C003       | Carol        | Kisumu  | null    | null       | null
C004       | null         | null    | O102    | 2025-01-03 | 700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Audit data to find customers without orders and orders without customers.&lt;/p&gt;


&lt;h3&gt;
  
  
  5.4 Inner Join
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Definition:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Returns only rows where there is a match in both tables.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retained records:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Only customers with orders and only orders with customers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example (Customers INNER JOIN Orders on CustomerID):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CustomerID | CustomerName | Region  | OrderID | OrderDate  | Amount
-----------|--------------|---------|---------|------------|-------
C001       | Alice        | Nairobi | O100    | 2025-01-01 | 500
C002       | Bob          | Mombasa | O101    | 2025-01-02 | 300
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Analyze only completed customer-order pairs.&lt;/p&gt;


&lt;h3&gt;
  
  
  5.5 Left Anti Join
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Definition:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Returns rows from the left table that have no match in the right table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retained records:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Customers with no orders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example (Customers LEFT ANTI JOIN Orders on CustomerID):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CustomerID | CustomerName | Region
-----------|--------------|--------
C003       | Carol        | Kisumu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Identify customers who have never placed an order.&lt;/p&gt;


&lt;h3&gt;
  
  
  5.6 Right Anti Join
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Definition:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Returns rows from the right table that have no match in the left table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retained records:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Orders with no matching customer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example (Customers RIGHT ANTI JOIN Orders on CustomerID):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CustomerID | CustomerName | Region  | OrderID | OrderDate  | Amount
-----------|--------------|---------|---------|------------|-------
C004       | null         | null    | O102    | 2025-01-03 | 700
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use case:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Find orphaned orders (e.g., data quality issues).&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Power Query Joins vs Power BI Relationships
&lt;/h2&gt;

&lt;p&gt;It's important to distinguish between merging tables in Power Query and creating relationships in the data model.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 What Each Operation Does
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Power Query Merge (Join):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Physically combines columns from two queries into one query.&lt;/li&gt;
&lt;li&gt;Happens during the &lt;strong&gt;data transformation&lt;/strong&gt; stage, before loading.&lt;/li&gt;
&lt;li&gt;Results in a single table in the model (if loaded).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Power BI Relationship:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does not merge or combine data physically.&lt;/li&gt;
&lt;li&gt;Defines a logical link between two separate tables in the &lt;strong&gt;data model&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Enables filter propagation and coordinated aggregations across tables.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6.2 When to Use Each
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Use a Power Query merge when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need a single denormalized table (e.g., for a flat export or specific calculation).&lt;/li&gt;
&lt;li&gt;You are consolidating 1:1 related tables to simplify the model.&lt;/li&gt;
&lt;li&gt;You are preparing a curated dataset for a specific report where relationships are not needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use relationships when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are building a BI model with facts and dimensions.&lt;/li&gt;
&lt;li&gt;You want to keep tables normalized and leverage Power BI's engine for aggregations.&lt;/li&gt;
&lt;li&gt;You need flexible slicing by multiple dimensions without duplicating data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6.3 Impact on Model Structure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Excessive merging creates wide, denormalized tables, increasing model size and reducing flexibility.&lt;/li&gt;
&lt;li&gt;Keeping fact and dimension tables separate (star schema) improves:

&lt;ul&gt;
&lt;li&gt;Query performance (better compression, fewer columns scanned).&lt;/li&gt;
&lt;li&gt;DAX simplicity (clear filter paths, standard time intelligence).&lt;/li&gt;
&lt;li&gt;Maintainability (changes in dimensions don't require rebuilding large fact tables).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Merging &lt;code&gt;FactSales&lt;/code&gt; with &lt;code&gt;DimCustomer&lt;/code&gt;, &lt;code&gt;DimProduct&lt;/code&gt;, and &lt;code&gt;DimDate&lt;/code&gt; into one huge table might seem convenient, but it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Increases model size due to repeated dimension values.&lt;/li&gt;
&lt;li&gt;Makes it harder to reuse dimensions across other facts (e.g., &lt;code&gt;FactTargets&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Complicates DAX for time intelligence and advanced calculations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using relationships preserves a clean star schema and lets Power BI handle the joins efficiently at query time.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Recommended Power BI Model for a Typical BI Project
&lt;/h2&gt;

&lt;p&gt;For most business intelligence projects, the recommended approach is a &lt;strong&gt;star schema&lt;/strong&gt; with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One or more fact tables (e.g., &lt;code&gt;FactSales&lt;/code&gt;, &lt;code&gt;FactOrders&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Multiple dimension tables (e.g., &lt;code&gt;DimCustomer&lt;/code&gt;, &lt;code&gt;DimProduct&lt;/code&gt;, &lt;code&gt;DimDate&lt;/code&gt;, &lt;code&gt;DimLocation&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;One-to-many relationships from dimensions to facts, with &lt;strong&gt;single-direction filtering&lt;/strong&gt; by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Justification
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Query and report performance:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Star schemas are optimized for Power BI's VertiPaq engine, yielding faster aggregations and lower memory usage compared to flat or heavily snowflaked models.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;DAX simplicity:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear 1:* relationships and single-direction filters make DAX measures more straightforward, especially for time intelligence and context manipulation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Model readability:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separating facts and dimensions makes the model intuitive: facts represent "what happened," dimensions represent "how to slice it."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scalability:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adding new facts or dimensions is easier without restructuring a massive flat table.&lt;/li&gt;
&lt;li&gt;Dimensions can be reused across multiple fact tables (e.g., &lt;code&gt;DimDate&lt;/code&gt; for sales and targets).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Data redundancy:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Star schemas reduce redundancy compared to flat tables by storing dimension attributes once.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Maintainability:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Changes to dimension attributes (e.g., new region, corrected product category) are made in one place.&lt;/li&gt;
&lt;li&gt;Easier to document and onboard new developers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Ease of creating reports:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Report builders can drag dimensions onto visuals and automatically filter facts, without complex DAX or query logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Filter propagation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Single-direction 1:* relationships provide predictable filter flow from dimensions to facts, reducing ambiguity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Model complexity:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Star schemas strike a balance: more structured than flat tables, but simpler than deeply snowflaked models.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Recommended Relationship Design
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema:&lt;/strong&gt; Star schema as the default. Use snowflaking only when there is a strong, justified need (e.g., very large, shared, normalized dimensions).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relationships:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Predominantly one-to-many from dimensions to facts.
&lt;/li&gt;
&lt;li&gt;Avoid many-to-many between facts; use bridge tables if truly needed.
&lt;/li&gt;
&lt;li&gt;Consolidate 1:1 related tables via Power Query merges where appropriate.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filter direction:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Single-direction by default.
&lt;/li&gt;
&lt;li&gt;Enable bidirectional filtering sparingly and only after testing for ambiguity and performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This design provides a robust foundation for scalable, high-performance Power BI solutions that are easy to understand, extend, and maintain.&lt;/p&gt;

</description>
      <category>powerfuldevs</category>
      <category>beginners</category>
      <category>dataengineering</category>
      <category>data</category>
    </item>
    <item>
      <title>Understanding the Git Workflow: Working Directory, Staging, Commit, and Push</title>
      <dc:creator>Trevor Zabar</dc:creator>
      <pubDate>Sun, 23 Aug 2026 07:02:32 +0000</pubDate>
      <link>https://dev.to/zabartrevor/understanding-the-git-workflow-working-directory-staging-commit-and-push-485h</link>
      <guid>https://dev.to/zabartrevor/understanding-the-git-workflow-working-directory-staging-commit-and-push-485h</guid>
      <description>&lt;p&gt;When I first started learning Git, commands such as &lt;code&gt;git add&lt;/code&gt;, &lt;code&gt;git commit&lt;/code&gt;, and &lt;code&gt;git push&lt;/code&gt; seemed like separate steps that were easy to confuse. After practicing the Git workflow, I understood that each command moves changes to a different stage.&lt;br&gt;
The complete workflow is:&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 → Local Repository → Remote Repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this article, I will explain each stage using a simple website project.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Is Git?
&lt;/h2&gt;

&lt;p&gt;Git is a version control system that tracks changes in a project. It allows developers to save different versions of their work and review or restore earlier versions when necessary.&lt;/p&gt;

&lt;p&gt;GitHub is an online platform where Git repositories can be stored and shared. Git works on your computer, while GitHub stores a copy of your repository online.&lt;/p&gt;

&lt;p&gt;The important difference is that saving a commit locally does not automatically upload it to GitHub. You must push the commit separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Working Directory
&lt;/h2&gt;

&lt;p&gt;The working directory is the project folder on your computer. It contains the files you are currently creating or editing.&lt;/p&gt;

&lt;p&gt;For example, imagine a website project with the following files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-website/
├── index.html
├── style.css
└── script.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I open &lt;code&gt;index.html&lt;/code&gt; and change the heading, the change first exists in the working directory.&lt;/p&gt;

&lt;p&gt;At this point, Git knows that the file has changed, but the change has not been prepared for saving yet. I can check the condition of the project with:&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;Git may display something similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Changes not staged for commit:
  modified:   index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that &lt;code&gt;index.html&lt;/code&gt; has been changed, but it is not yet in the staging area.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Staging Area
&lt;/h2&gt;

&lt;p&gt;The staging area is where I select the changes that I want to include in my next commit.&lt;br&gt;
To stage the modified file, I run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then I check the status again:&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;The output may now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Changes to be committed:
  modified:   index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The file is now ready to be committed, but it has not been permanently saved in the project history yet.&lt;/p&gt;

&lt;p&gt;I can stage more than one file by listing the filenames:&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 index.html style.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can also stage all changed files with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Although &lt;code&gt;git add .&lt;/code&gt; is convenient, I should check the files before committing. It may stage files that I did not intend to include.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Staging Area Matters
&lt;/h2&gt;

&lt;p&gt;The staging area gives me control over what goes into a commit.&lt;/p&gt;

&lt;p&gt;For example, suppose I make changes to two files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I add a completed navigation bar to &lt;code&gt;index.html&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;I experiment with unfinished styles in &lt;code&gt;style.css&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I want to commit only the navigation bar, I can run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add index.html
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add navigation bar"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The changes in &lt;code&gt;style.css&lt;/code&gt; will remain in the working directory and will not be included in the commit.&lt;/p&gt;

&lt;p&gt;This makes it possible to create focused commits. Each commit can describe one meaningful change instead of containing a mixture of unrelated work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Commit
&lt;/h2&gt;

&lt;p&gt;A commit is a saved record of staged changes. It represents a particular version of the project.&lt;/p&gt;

&lt;p&gt;After staging &lt;code&gt;index.html&lt;/code&gt;, I can create a commit with:&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;"Update homepage heading"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The message after &lt;code&gt;-m&lt;/code&gt; explains what the commit contains.&lt;/p&gt;

&lt;p&gt;Good commit messages include:&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 contact form"&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Fix mobile navigation"&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;Messages such as these are less useful:&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;"Changes"&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Update"&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Work"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A good commit message should be short, clear, and specific.&lt;/p&gt;

&lt;p&gt;When I commit changes, they are saved in my local Git repository. They are not yet available on GitHub.&lt;/p&gt;

&lt;p&gt;To view the project history, I can run:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;For a shorter version of the history, I can use:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;A short log may look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a31f9c2 Add contact form
e72b410 Update homepage heading
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each commit has a unique identifier that Git uses to distinguish it from other commits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pushing to GitHub
&lt;/h2&gt;

&lt;p&gt;A remote repository is the online version of a Git repository. For many projects, the remote repository is hosted on GitHub.&lt;/p&gt;

&lt;p&gt;After creating a commit locally, I can upload it with:&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;In this command:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;origin&lt;/code&gt; is the name of the remote repository.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;main&lt;/code&gt; is the branch I am pushing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The difference between committing and pushing is important:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git commit = Save changes in the local repository
git push   = Upload local commits to the remote repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running &lt;code&gt;git commit&lt;/code&gt; does not automatically update GitHub. I must run &lt;code&gt;git push&lt;/code&gt; to send the commit to the remote repository.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Complete Example
&lt;/h2&gt;

&lt;p&gt;Suppose I want to add a welcome message to my website.&lt;/p&gt;

&lt;p&gt;First, I check the current 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;Next, I edit &lt;code&gt;index.html&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Welcome to My Website&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can review the changes before staging them:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;git diff&lt;/code&gt; command shows changes that have not been staged.&lt;/p&gt;

&lt;p&gt;Next, I stage the file:&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 index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I check the status again:&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;Now I create a 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 commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add welcome message"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I push the commit to GitHub:&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;The complete workflow is:&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
git diff
git add index.html
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add welcome message"&lt;/span&gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each command has a separate purpose:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;git status&lt;/code&gt; shows the current state of the project.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git diff&lt;/code&gt; shows the changes made to files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git add&lt;/code&gt; selects changes for the next commit.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git commit&lt;/code&gt; saves the staged changes locally.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git push&lt;/code&gt; uploads the commit to GitHub.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Starting Git in a Project
&lt;/h2&gt;

&lt;p&gt;If I am working on a new project, I first move into the project folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;my-website
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I initialize Git:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;This creates a hidden &lt;code&gt;.git&lt;/code&gt; folder that stores the project's Git information and history.&lt;br&gt;
I can check the project status with:&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;If the project contains files that Git has not tracked before, they will appear as untracked files.&lt;/p&gt;

&lt;p&gt;I can add all the files:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then I create the first 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 commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Initial project setup"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I have already created an empty repository on GitHub, I can connect the local repository to it:&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 origin https://github.com/your-username/my-website.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can make sure the branch is called &lt;code&gt;main&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Then I push the project to GitHub:&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;The &lt;code&gt;-u&lt;/code&gt; option links the local branch to the remote branch. After this first push, I can usually use:&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reviewing Staged Changes
&lt;/h2&gt;

&lt;p&gt;It is useful to review changes before committing them.&lt;/p&gt;

&lt;p&gt;The following command shows changes that have not been staged:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;After using &lt;code&gt;git add&lt;/code&gt;, I can review the changes that are ready to be committed:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;The difference is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git diff          = Shows unstaged changes
git diff --staged = Shows staged changes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This extra check can help me catch mistakes before they become part of the project history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Removing a File from the Staging Area
&lt;/h2&gt;

&lt;p&gt;Sometimes I may stage a file by mistake. I can remove it from the staging area without deleting it from my computer:&lt;br&gt;
&lt;/p&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; style.css
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The changes in &lt;code&gt;style.css&lt;/code&gt; will remain in the working directory, but they will not be included in the next commit.&lt;/p&gt;

&lt;p&gt;This command only unstages the file. It does not delete the file or remove the changes I made.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discarding Changes
&lt;/h2&gt;

&lt;p&gt;If I decide that I do not want to keep changes in a file, I can restore it to the version from the latest 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 restore index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This removes the unstaged changes in &lt;code&gt;index.html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I should use this command carefully because the discarded changes may not be easy to recover. Before running it, I can check what will be removed with:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  Forgetting to Stage Changes
&lt;/h3&gt;

&lt;p&gt;If I run &lt;code&gt;git commit&lt;/code&gt; before staging a file, Git may tell me that there is nothing to commit.&lt;/p&gt;

&lt;p&gt;The correct sequence is:&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 index.html
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Describe the change"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Thinking a Commit Is the Same as a Push
&lt;/h3&gt;

&lt;p&gt;A commit saves changes locally. A push uploads those local commits to GitHub.&lt;/p&gt;

&lt;p&gt;The commands are separate:&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;"Save my changes"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using Unclear Commit Messages
&lt;/h3&gt;

&lt;p&gt;A message such as &lt;code&gt;"Update"&lt;/code&gt; does not provide enough information about the change.&lt;/p&gt;

&lt;p&gt;A clearer message would be:&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;"Fix incorrect signup link"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Committing Without Checking
&lt;/h3&gt;

&lt;p&gt;Before committing, I should inspect the staged files:&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
git diff &lt;span class="nt"&gt;--staged&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps prevent accidental commits containing unfinished work, temporary files, or sensitive information.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Simple Way to Remember the Workflow
&lt;/h2&gt;

&lt;p&gt;I think of the Git workflow as preparing and sending a parcel.&lt;/p&gt;

&lt;p&gt;The working directory is my desk, where I work on the contents. The staging area is where I choose what should go into the parcel. The commit is like sealing and labeling the parcel, while pushing is like sending it to the online destination.&lt;/p&gt;

&lt;p&gt;The commands follow the same idea:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git add       Choose what to include
git commit    Save the selected changes locally
git push      Share the saved changes online
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Workflow
&lt;/h2&gt;

&lt;p&gt;For everyday Git work, I can use:&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
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;"Describe what changed"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I am unsure about the state of my project, I start with:&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;The most important lesson is that changes move through separate stages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Edit files
   ↓
Working Directory
   ↓ git add
Staging Area
   ↓ git commit
Local Repository
   ↓ git push
Remote Repository
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once I understand this process, Git commands become easier to remember. I am not just running commands randomly; I am deliberately moving my work from one stage to the next.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>githubactions</category>
    </item>
  </channel>
</rss>
