<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[ENTR-451]]></title><description><![CDATA[ENTR-451: Introduction to Software Development, Kellogg School of Management]]></description><link>https://entr451.com/</link><image><url>https://entr451.com/favicon.png</url><title>ENTR-451</title><link>https://entr451.com/</link></image><generator>Ghost 4.20</generator><lastBuildDate>Wed, 30 Sep 2026 13:41:15 GMT</lastBuildDate><atom:link href="https://entr451.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Domain Modeling: The Data Model]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>In class, you asked a baseball database which team has lost the most games in history, and it couldn&apos;t really answer. It could give you the worst single season (the 1899 Cleveland Spiders, 20-134) or the most losses added up by team name (the Philadelphia Phillies, 10,426)</p>]]></description><link>https://entr451.com/domain-modeling-the-data-model/</link><guid isPermaLink="false">6abc113ad9019e19441c333f</guid><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Tue, 29 Sep 2026 19:28:25 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>In class, you asked a baseball database which team has lost the most games in history, and it couldn&apos;t really answer. It could give you the worst single season (the 1899 Cleveland Spiders, 20-134) or the most losses added up by team name (the Philadelphia Phillies, 10,426). But a fan asking that question means the <em>franchise</em>, and there is no franchise anywhere in the database. Cleveland alone shows up under seven different names.</p>
<p>No amount of clever SQL fixes that. The problem isn&apos;t the query; it&apos;s the design of the tables. Deciding what tables a system should have, and how they relate to each other, is called <strong>domain modeling</strong>, and it&apos;s the subject of this post.</p>
<h3 id="models">Models</h3>
<p>A domain is the slice of the real world your software is about: a school, an airline, a store, a baseball league. A domain model represents that world in software, using the words the people in it actually use. An airline and a retailer end up with different models because they talk about different things.</p>
<p>We&apos;ll use a school as our example. We call each real-world thing in the domain a <strong>model</strong>, and all of the models, together with the relationships between them, make up the <strong>domain model</strong>.</p>
<p>What are the real, tangible things in a school? Two obvious ones:</p>
<ul>
<li>Student</li>
<li>Teacher</li>
</ul>
<p>Students and teachers aren&apos;t theoretical. They&apos;re people who exist outside the software. Students attend the school; teachers teach its courses.</p>
<p>You might ask: why not a single model for Person? Students and teachers are both people. It&apos;s a fair question, and the answer is that models are distinguished by their <strong>attributes</strong>, the data we need to keep about them. In our school, teachers have a bio and students don&apos;t. Students have contact information (email and phone number) and teachers don&apos;t. So they&apos;re different models.</p>
<p>Where do those attributes come from? From what the product needs to do. If the product shows a teacher&apos;s bio on the course page, a teacher needs a bio. Next week, we&apos;ll write that down properly as user stories, and the attributes will fall out of them. For now:</p>
<ul>
<li>Student: first name, last name, email, phone number</li>
<li>Teacher: first name, last name, bio</li>
</ul>
<h3 id="models-become-tables">Models become tables</h3>
<p>If you&apos;ve been following along with SQL, you&apos;ve probably already guessed where this is going. A model and its attributes map neatly onto a database table:</p>
<ul>
<li>Each <strong>column</strong> is an attribute</li>
<li>Each <strong>row</strong> is one instance of the model: one student, one teacher</li>
</ul>
<p>Each model gets its own table, because each model has different attributes:</p>
<p><strong>students</strong></p>
<table>
<thead>
<tr>
<th>id</th>
<th>first_name</th>
<th>last_name</th>
<th>email</th>
<th>phone_number</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Jane</td>
<td>Doe</td>
<td><a href="mailto:jane@example.com">jane@example.com</a></td>
<td>555-1212</td>
</tr>
<tr>
<td>2</td>
<td>Jenny</td>
<td>Smith</td>
<td><a href="mailto:jenny@gmail.com">jenny@gmail.com</a></td>
<td>867-5309</td>
</tr>
<tr>
<td>3</td>
<td>John</td>
<td>Johnson</td>
<td><a href="mailto:john@acme.com">john@acme.com</a></td>
<td>456-7890</td>
</tr>
</tbody>
</table>
<p><strong>teachers</strong></p>
<table>
<thead>
<tr>
<th>id</th>
<th>first_name</th>
<th>last_name</th>
<th>bio</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Ben</td>
<td>Block</td>
<td>Often talks to a rubber ducky.</td>
</tr>
<tr>
<td>2</td>
<td>Brian</td>
<td>Eng</td>
<td>Loves tacos.</td>
</tr>
</tbody>
</table>
<p>A good start. Next, a Course. A course isn&apos;t a physical thing you can hold, the way a student or teacher is, but it&apos;s real and it has data we need to keep: a name and a description.</p>
<p><strong>courses</strong></p>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>description</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Intro to Software Development</td>
<td>This course is focused on software development...</td>
</tr>
<tr>
<td>2</td>
<td>Taco-Making 101</td>
<td>In this course, you&apos;ll learn how to build a proper taco...</td>
</tr>
</tbody>
</table>
<h3 id="an-attribute-or-a-new-model">An attribute, or a new model?</h3>
<p>You might be wondering whether that&apos;s really all the data a course needs. What about the times it meets, and who teaches it?</p>
<p>Knowing what belongs as an attribute on a model and what should be its own model is one of the keys to being good at domain modeling. So let&apos;s try putting the times and teachers on the course and see what happens:</p>
<p><strong>courses</strong></p>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>description</th>
<th>time_1</th>
<th>teacher_id_1</th>
<th>time_2</th>
<th>teacher_id_2</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Intro to Software Development</td>
<td>This course is...</td>
<td>Wednesday 6:30-9:30pm</td>
<td>1</td>
<td>Friday 1:30-4:30pm</td>
<td>2</td>
</tr>
<tr>
<td>2</td>
<td>Taco-Making 101</td>
<td>In this course...</td>
<td>Wednesday 6-9pm</td>
<td>2</td>
<td>Thursday 6-9pm</td>
<td>1</td>
</tr>
</tbody>
</table>
<p>Seems reasonable, until Taco-Making gets popular and needs a third time. Then a fourth. Every new time slot means two new columns, and every course that has fewer slots is left with blank cells. The table is growing <strong>sideways</strong>.</p>
<p>That&apos;s the smell. A well-designed table grows <strong>down</strong>: new data means new rows, not new columns. When a table keeps needing more columns for &quot;another one of these,&quot; that thing wants to be its own model.</p>
<h3 id="one-to-many">One-to-many</h3>
<p>Each time a course is offered, with its own teacher, is a <strong>Section</strong>. One course can have many sections:</p>
<p><strong>sections</strong></p>
<table>
<thead>
<tr>
<th>id</th>
<th>time</th>
<th>course_id</th>
<th>teacher_id</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Wednesday 6:30-9:30pm</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>2</td>
<td>Friday 1:30-4:30pm</td>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>3</td>
<td>Wednesday 6-9pm</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>4</td>
<td>Thursday 6-9pm</td>
<td>2</td>
<td>1</td>
</tr>
</tbody>
</table>
<p>Much better. Courses now only hold what&apos;s true of the course itself, and sections hold the rest. A new section is just a new row, and a course can have none, one, or a hundred.</p>
<p>Section makes sense as its own model because we don&apos;t know how many there will be, and because it has attributes of its own (a time, a teacher) that might grow later.</p>
<p>This is a <strong>one-to-many relationship</strong>, and the way you build one is the foreign key you met in SQL 2: put a column on the &quot;many&quot; side that points at the &quot;one&quot; side. One course has many sections, so <code>course_id</code> goes on <code>sections</code>.</p>
<h3 id="many-to-many">Many-to-many</h3>
<p>Notice that <code>sections</code> has two foreign keys. That&apos;s because there are two one-to-many relationships here: a course has many sections, and a teacher has many sections.</p>
<p>Put those together and you get something new. A course can have many teachers, and a teacher can teach many courses. That&apos;s a <strong>many-to-many relationship</strong>, and it never lives directly on either table. It&apos;s always two one-to-manys through a model in the middle:</p>
<blockquote>
<p>Two one-to-many relationships = one many-to-many relationship</p>
</blockquote>
<p>The model in the middle is called a <strong>join model</strong>. Here, it&apos;s Section. Unlike Student or Teacher, Section isn&apos;t really a thing you&apos;d point at in the physical world; it exists because of the relationship between two things that are.</p>
<h3 id="back-to-the-franchise">Back to the franchise</h3>
<p>Now the baseball question has an answer. The database was missing a model:</p>
<p><strong>franchises</strong></p>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
</tr>
</thead>
<tbody>
<tr>
<td>...</td>
<td>Cleveland</td>
</tr>
</tbody>
</table>
<p><strong>teams</strong> (one row per team, per season)</p>
<table>
<thead>
<tr>
<th>id</th>
<th>franchise_id</th>
<th>year</th>
<th>name</th>
<th>wins</th>
<th>losses</th>
</tr>
</thead>
<tbody>
<tr>
<td>...</td>
<td>&#x2192; franchises</td>
<td>1899</td>
<td>Cleveland Spiders</td>
<td>20</td>
<td>134</td>
</tr>
</tbody>
</table>
<p>One franchise has many team-seasons, so <code>franchise_id</code> goes on <code>teams</code>, the &quot;many&quot; side. The team&apos;s name stays on the season, because names change over time; the franchise is what stays the same. &quot;Which team has lost the most games in history?&quot; becomes a <code>SUM</code> of losses grouped by franchise instead of by name.</p>
<p>It isn&apos;t free, though. Someone has to decide which seasons belong to which franchise, and in Cleveland&apos;s case not all seven names are the same club (the 1899 Spiders were a different team from the one that became the Indians). The data can&apos;t make that call for you. That&apos;s what domain modeling actually is: decisions about the real world, written down as tables.</p>
<h3 id="naming-conventions">Naming conventions</h3>
<p>These are the conventions we&apos;ve been using, and we&apos;ll keep using them for the rest of the course. None of them is technically required, but following them makes everything easier to read, and it&apos;ll matter when we get to Rails.</p>
<ul>
<li>Models are singular and capitalized: Course, Section.</li>
<li>Table names are plural and lowercase: <code>courses</code>, <code>sections</code>.</li>
<li>Column names are lowercase: <code>name</code>, <code>description</code>.</li>
<li>Multi-word names use underscores: <code>first_name</code>, <code>teacher_id</code>.</li>
</ul>
<h3 id="next-week">Next week</h3>
<p>We picked the attributes in this post by saying what the product needs to do. Next week, we&apos;ll do that properly: start from user stories, and let the domain model come out of them.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[SQL, optional: Aggregates and Grouping]]></title><description><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<p>This one is optional. In class, <code>COUNT</code>, <code>SUM</code>, <code>AVG</code>, <code>MIN</code>, <code>MAX</code> and <code>GROUP BY</code> were reading vocabulary: you need to recognize them when a chat writes them, not write them yourself. If you want to know how they work, here they are. It picks up where <a href="https://entr451.com/the-select-statement/">SQL 1</a> left off,</p></blockquote>]]></description><link>https://entr451.com/sql-aggregates-and-grouping/</link><guid isPermaLink="false">6abc0d1bd9019e19441c3321</guid><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Tue, 29 Sep 2026 19:11:24 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<p>This one is optional. In class, <code>COUNT</code>, <code>SUM</code>, <code>AVG</code>, <code>MIN</code>, <code>MAX</code> and <code>GROUP BY</code> were reading vocabulary: you need to recognize them when a chat writes them, not write them yourself. If you want to know how they work, here they are. It picks up where <a href="https://entr451.com/the-select-statement/">SQL 1</a> left off, with the same &#x1F476; <em>baby tables</em>.</p>
</blockquote>
<h3 id="aggregate-functions">Aggregate Functions</h3>
<p>Often, the questions you want answered will involve math. That is, instead of simply seeing a subset of the rows in a table, we&apos;ll want to <em>aggregate</em> the results together in some way, e.g. adding the number of rows, summing a single column, seeing an average, etc.</p>
<p><em>How many reviews have been written?</em></p>
<ul>
<li>Identify the table that holds the information you need - <code>Reviews</code></li>
<li>Identify the columns that hold the information you need - not applicable in this case, since we&apos;re looking for an aggregate number</li>
<li>Add any other conditions - none</li>
<li>Then, ask the question:</li>
</ul>
<pre><code class="language-sql">SELECT COUNT(*) FROM Reviews;
</code></pre>
<table>
<thead>
<tr>
<th>COUNT(*)</th>
</tr>
</thead>
<tbody>
<tr>
<td>4</td>
</tr>
</tbody>
</table>
<p>There are a couple of crazy things going on here! Let&apos;s break it down.</p>
<p>First, instead of a <em>column name</em> like <em>Name, Price, Body</em>, etc. in our <code>SELECT</code> clause, we&apos;re calling a <strong>SQL function</strong>. There are many SQL functions built into SQL, but they will always contain a set of parentheses, which denote <em>arguments</em> to the function. In this case, we&apos;re using the <code>COUNT</code> function, which accepts an argument of &quot;what columns are we counting?&quot; Since we&apos;re simply looking for the aggregate number of rows in the table, the columns aren&apos;t relevant and we just use <code>*</code>.</p>
<p>And even though we&apos;re asking for an aggregate, that is, a single number representing the count of all the reviews, our result is still a &#x1F476; <em>baby table.</em> It simply contains a single column -- called <code>COUNT(*)</code> -- and a single row. We can call several SQL functions, or a combination of SQL functions and column names, which will result in a &#x1F476; <em>baby table</em> containing several columns*.*</p>
<p>Let&apos;s look at another aggregate function, where the argument does matter:</p>
<p><em>What is the average rating across all reviews?</em></p>
<ul>
<li>Identify the table that holds the information you need - <code>Reviews</code></li>
<li>Identify the columns that hold the information you need - an average <code>Rating</code></li>
<li>Add any other conditions - none</li>
<li>Then, ask the question:</li>
</ul>
<pre><code class="language-sql">SELECT AVG(Rating) FROM Reviews;
</code></pre>
<table>
<thead>
<tr>
<th>AVG(Rating)</th>
</tr>
</thead>
<tbody>
<tr>
<td>4.25</td>
</tr>
</tbody>
</table>
<p>Similar as before, with our <code>COUNT</code> function, but this time, the argument matters. We&apos;re using the <code>AVG</code> function, which takes a column name as an argument - <em>which column are we averaging?</em></p>
<p>As mentioned, there are many functions built-in to SQL. The ones that are relevant to this lesson are <em>aggregate functions</em> - ones that do math to aggregate information. Examples of aggregate functions include:</p>
<ul>
<li><code>COUNT</code> - counts the number of rows</li>
<li><code>AVG</code> - average of a column value or expression</li>
<li><code>SUM</code> - sum of a column value or expression</li>
<li><code>MIN</code> - minimum of a column value or expression</li>
<li><code>MAX</code> - maximum of a column value or expression</li>
</ul>
<h3 id="grouping">Grouping</h3>
<p>Occasionally, a question like <em>How many reviews have been written?</em> will be enough to satisfy business requirements. However, it&apos;s more likely that your boss, client, or other stakeholder will want more specific information than simply aggregating the entire table&apos;s worth of data. We&apos;ll often want to categorize or aggregate (group) results by the value of another column. For example, a more common question might be:</p>
<p><em>How many reviews have been written per product?</em></p>
<ul>
<li>Identify the table that holds the information you need - <code>Reviews</code></li>
<li>Identify the columns that hold the information you need - the <code>Product</code> and the aggregate number of reviews</li>
<li>Add any other conditions - none</li>
<li>Then, ask the question:</li>
</ul>
<pre><code class="language-sql">SELECT Product, COUNT(*) 
FROM Reviews
GROUP BY Product;
</code></pre>
<table>
<thead>
<tr>
<th>Product</th>
<th>COUNT(*)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Camera</td>
<td>2</td>
</tr>
<tr>
<td>Sofa</td>
<td>1</td>
</tr>
<tr>
<td>Toaster</td>
<td>1</td>
</tr>
</tbody>
</table>
<p>We see that the values separated by commas in the <code>SELECT</code> clause, whether they are column names or SQL functions, end up being the columns in the &#x1F476; <em>baby table</em>. And, because we used a <code>GROUP BY</code> clause, our results are aggregated (grouped) by the distinct values in the <code>Product</code> column.</p>
<p>This brings us to Rule #1A, the First Law of all SQL... <strong>when using a <code>GROUP BY</code> clause, the columns listed in the <code>GROUP BY</code> must also be in the <code>SELECT</code> clause.</strong> Makes sense, right? In this example, if the <code>Product</code> column wasn&apos;t also in the <code>SELECT</code>, the results would just be a list of numbers with no context:</p>
<pre><code class="language-sql">SELECT COUNT(*) 
FROM Reviews
GROUP BY Product;
</code></pre>
<table>
<thead>
<tr>
<th>COUNT(*)</th>
</tr>
</thead>
<tbody>
<tr>
<td>2</td>
</tr>
<tr>
<td>1</td>
</tr>
<tr>
<td>1</td>
</tr>
</tbody>
</table>
<p>Not very useful, is it?</p>
<p>The rule in the other direction is the one that actually bites: <strong>every column in the <code>SELECT</code> must either be in the <code>GROUP BY</code> or be inside an aggregate function.</strong> Most databases, PostgreSQL included, refuse to run a query that breaks it. SQLite runs it anyway, and quietly fills in a value from one row of each group. In the baseball database:</p>
<pre><code class="language-sql">SELECT year, name, MAX(wins)
FROM teams
WHERE year = 2002
GROUP BY year;
</code></pre>
<table>
<thead>
<tr>
<th>year</th>
<th>name</th>
<th>MAX(wins)</th>
</tr>
</thead>
<tbody>
<tr>
<td>2002</td>
<td>New York Yankees</td>
<td>103</td>
</tr>
</tbody>
</table>
<p>The Oakland Athletics also won 103 games in 2002. One row per group has room for one name, so SQLite picked one and didn&apos;t mention it. When a chat hands you SQL with a column like <code>name</code> sitting next to a <code>MAX</code> or a <code>COUNT</code>, check whether that column is in the <code>GROUP BY</code>. If it isn&apos;t, the name may not mean what it looks like it means.</p>
<h3 id="order-of-clauses">Order of Clauses</h3>
<p><a href="https://entr451.com/the-select-statement/">SQL 1</a> ended with the order of clauses; here it is again with <code>GROUP BY</code> in its place. Because of the way SQL is executed, the order in which the clauses are written does matter. This may differ slightly based on the flavor/implementation of SQL being worked with (i.e. some are more forgiving than others), but generally speaking, the order should be as follows:</p>
<ul>
<li><code>SELECT</code> - the columns or executed functions you want in the result</li>
<li><code>FROM</code> - the table(s) involved</li>
<li><code>WHERE</code> - the conditions or constraints</li>
<li><code>GROUP BY</code> - columns in the <code>SELECT</code> used to aggregate the results</li>
<li><code>ORDER BY</code> - columns by which the results shall be ordered, either numerically or alphabetically</li>
<li><code>LIMIT</code> - how many rows to return</li>
</ul>
<p>To put it all together:</p>
<p><em>What is the average rating per product, taking into account only those reviews with a rating of 3 or above, from lowest to highest average rating?</em></p>
<ul>
<li>Identify the table that holds the information you need - <code>Reviews</code></li>
<li>Identify the columns that hold the information you need - the <code>Product</code> and the aggregate average rating of reviews</li>
<li>Add any other conditions - rating of 3 or above</li>
<li>Then, ask the question:</li>
</ul>
<pre><code class="language-sql">SELECT Product, AVG(Rating)
FROM Reviews
WHERE Rating &gt;= 3
GROUP BY Product
ORDER BY AVG(Rating);
</code></pre>
<table>
<thead>
<tr>
<th>Product</th>
<th>AVG(Rating)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Toaster</td>
<td>3</td>
</tr>
<tr>
<td>Camera</td>
<td>4.5</td>
</tr>
<tr>
<td>Sofa</td>
<td>5</td>
</tr>
</tbody>
</table>
<h3 id="try-it-aggregation">Try it: aggregation</h3>
<p>In class, aggregates were reading vocabulary: you will see them in SQL a chat writes for you far more often than you will write them. Reading is easier once you have written one or two, so:</p>
<ul>
<li><em>How many teams played in 2020?</em> One <code>COUNT(*)</code> and one <code>WHERE</code>. (30.)</li>
<li><em>Which team name has been used for the most seasons?</em> <code>GROUP BY name</code>, <code>COUNT(*)</code>, sorted, and one row. Then ask for three rows, and see what one row was hiding.</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[SQL 2: Table Relationships]]></title><description><![CDATA[<p>In the <a href="https://entr451.com/the-select-statement/">last lesson</a>, we learned the basics of SQL and are well on our way to knowing just about everything we&apos;d want to know about how to perform query operations on a single table. But of course, a database is a collection of multiple tables, with each</p>]]></description><link>https://entr451.com/table-relationships/</link><guid isPermaLink="false">6abc0b17d9019e19441c32ff</guid><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Tue, 29 Sep 2026 19:08:53 GMT</pubDate><content:encoded><![CDATA[<p>In the <a href="https://entr451.com/the-select-statement/">last lesson</a>, we learned the basics of SQL and are well on our way to knowing just about everything we&apos;d want to know about how to perform query operations on a single table. But of course, a database is a collection of multiple tables, with each table storing a different dimension of information. In our e-commerce example, we have a table for <em>Products</em> and another for <em>Reviews</em>. Why? We&apos;ll get deeper into this in an upcoming lesson on <em>Domain Modeling </em>(which just a fancy way of saying &quot;how to design tables&quot;) &#x2013;&#xA0;but, given the existing design, how do we ask questions of multiple tables? In this lesson, we&apos;ll take a deep dive into doing just that.</p><h3 id="joins">JOINs</h3><p>Sometimes the answer to a question is contained within more than one table. Consider our <code>Products</code> table:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Department</th>
</tr>
</thead>
<tbody>
<tr>
<td>Camera</td>
<td>$299</td>
<td>Electronics</td>
</tr>
<tr>
<td>Sofa</td>
<td>$699</td>
<td>Furniture</td>
</tr>
<tr>
<td>Dining Table</td>
<td>$1299</td>
<td>Furniture</td>
</tr>
<tr>
<td>Toaster</td>
<td>$79</td>
<td>Housewares</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>And <code>Reviews</code>:</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th>Product</th>
<th>Rating</th>
<th>Body</th>
</tr>
</thead>
<tbody>
<tr>
<td>Camera</td>
<td>4</td>
<td>Takes nice pictures!</td>
</tr>
<tr>
<td>Sofa</td>
<td>5</td>
<td>Comfy!</td>
</tr>
<tr>
<td>Camera</td>
<td>5</td>
<td>Best camera ever!!!</td>
</tr>
<tr>
<td>Toaster</td>
<td>3</td>
<td>Makes toast.</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><p>And the question we want answered is:</p><p><em>Can I see all the reviews for the products in the Furniture department?</em></p><p>The results &#x2013; our &#x1F476; <em>baby table</em> &#x2013; and the <code>WHERE</code> clause would both need to contain columns from both the <code>Products</code> and <code>Reviews</code> tables. Let&apos;s follow our usual formula and see what happens:</p><ul><li>Identify the table(s) that contain the information you need &#x2013; <code>Reviews</code> and <code>Products</code></li><li>Identify the columns the hold the information you need &#x2013;&#xA0;the <code>Product</code>, <code>Rating</code>, and <code>Body</code> from <code>Reviews</code>, as well as <code>Department</code> from <code>Products</code></li><li>Add any other conditions &#x2013;&#xA0;products in the <em>Furniture</em> department</li></ul><p>Before being able to ask the question, though, we need to add one additional step to this recipe.</p><ul><li>Identify the thing the tables have in common&#xA0;&#x2013;&#xA0;the <code>Name</code> from the <code>Products</code> table is equivalent to the <code>Product</code> column from the <code>Reviews</code> table</li></ul><p>Now, we can ask the question:</p><pre><code>SELECT Products.Department, Reviews.Product, Reviews.Rating, Reviews.Body
FROM Reviews INNER JOIN Products ON Products.Name = Reviews.Product
WHERE Products.Department = &quot;Furniture&quot;;</code></pre><p>Whew! There seems to be a lot more here than what we&apos;ve seen thus far... but don&apos;t worry; it&apos;s actually quite straightforward. Let&apos;s break it down.</p><p>Because there are multiple tables involved, we&apos;re prefixing each column name with the name of the table and a dot <code>.</code> &#x2013;&#xA0;e.g. because the <code>Body</code> column is in the <code>Reviews</code> table, we write it as <code>Reviews.Body</code>. This is prevent conflicts in naming &#x2013; for instance, what if the <code>Products</code> and <code>Reviews</code> tables both had a <code>Body</code> column? This syntax clears that up.</p><p>And, the <code>FROM</code> clause is more complex now. Previously, it was just the name of the single table we were reading from. Now, we&apos;re reading from the intersection of the two tables. This intersection is called a <code>JOIN</code>, and the commonality between the two tables is specified in the <code>ON</code> clause. The intersection between the table happens when the <code>Name</code> from the <code>Products</code> table is equivalent to the <code>Product</code> column from the <code>Reviews</code> table, so it&apos;s <code>ON Products.Name = Reviews.Product</code>. Furthermore, if there was a third, fourth, fifth, etc. table involved, they would be chained together in the <code>FROM</code> clause with more <code>INNER JOIN</code>s.</p><h3 id="the-primary-key">The Primary Key</h3><p>A shocking confession: the examples I&apos;ve provided thus far &#x2013; the <code>Products</code> and <code>Reviews</code> tables from our fictional e-commerce store that sells a rather eclectic variety of stuff &#x2013;&#xA0;hasn&apos;t been entirely realistic. The one thing that basically all tables in a relational database have, and that&apos;s missing from these tables, is some sort of unique identifier for each record. A <em><strong>primary key</strong></em>. If you&apos;ve ever seen a database table outside of this course, you may have seen a variety of different types of primary keys. Based on the database design, sometimes you&apos;ll see something like a Social Security Number, or some other strictly-enforced integer value. Sometimes it&apos;s a long alphanumeric string based on a software algorithm. Or, a lot of the time, it&apos;s simply <strong>an integer that starts at 1 and goes up by 1, forever</strong>. The <em>auto-incrementing integer</em> primary key is fairly common for a freshly-built application nowadays, and that will be the rule for primary keys that we&apos;ll follow for the reminder of this course.</p><figure class="kg-card kg-embed-card kg-card-hascaption"><blockquote class="twitter-tweet"><p lang="en" dir="ltr">Hey guys, wanna feel old?<br><br>I&apos;m 40.<br><br>You&apos;re welcome.</p>&#x2014; Macaulay Culkin (@IncredibleCulk) <a href="https://twitter.com/IncredibleCulk/status/1298730289737293824?ref_src=twsrc%5Etfw">August 26, 2020</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<figcaption>https://twitter.com/IncredibleCulk/status/1298730289737293824</figcaption></figure><p>Have a look at the permanent URL for the tweet above &#x2013;&#xA0;that URL never changes, and will always direct you to the page for that tweet, unless Mr. Culkin decides to delete it. What is the <em>1298730289737293824 </em>all about? That&apos;s the primary key. That&apos;s right, there was a tweet #1 at some point, and at the time this tweet was posted, it was the <em>1,298,730,289,737,293,824th</em> tweet that had ever been posted. The number never goes down, and the number never gets reused. Luckily, there are a lot of numbers in the universe still left to use.</p><h3 id="foreign-keys">Foreign Keys</h3><p>Now that we know <em>what</em> a primary key is, let&apos;s look at <em>why</em> it&apos;s so important to use them in database design. First and foremost, we use primary keys to make our data design less fragile. To see what I mean by that &#x2013; let&apos;s look at a quick example. If we look at the existing table design we&apos;ve been working with, which does not contain primary keys, we can see how we can easily break the integrity of the data. For instance, what if we change the name of the &quot;camera&quot; product to something else?</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Department</th>
</tr>
</thead>
<tbody>
<tr>
<td><s>Camera</s> <strong>Digital SLR Camera</strong></td>
<td>$299</td>
<td>Electronics</td>
</tr>
<tr>
<td>Sofa</td>
<td>$699</td>
<td>Furniture</td>
</tr>
<tr>
<td>Dining Table</td>
<td>$1299</td>
<td>Furniture</td>
</tr>
<tr>
<td>Toaster</td>
<td>$79</td>
<td>Housewares</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><!--kg-card-begin: html--><table>
<thead>
<tr>
<th>Product</th>
<th>Rating</th>
<th>Body</th>
</tr>
</thead>
<tbody>
<tr>
<td>Camera</td>
<td>4</td>
<td>Takes nice pictures!</td>
</tr>
<tr>
<td>Sofa</td>
<td>5</td>
<td>Comfy!</td>
</tr>
<tr>
<td>Camera</td>
<td>5</td>
<td>Best camera ever!!!</td>
</tr>
<tr>
<td>Toaster</td>
<td>3</td>
<td>Makes toast.</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><p>Anarchy! We&apos;ve immediately broken the relationship between the <code>Products</code> and <code>Reviews</code> tables. Now, the rows in <code>Reviews</code> that previously referenced the &quot;Camera&quot; are now <em>orphaned, </em>that is, they are left with no valid parent record that is referenced in its <code>Product</code> column. The SQL query that we just wrote to get product reviews in the furniture department &#x2013; broken. Of course, we could update each review to reference the new name of the product. That would be easy enough in a database of this size, but certainly not in a database of more complex design and possibly millions of records. It&apos;s much better to update our database design to include a primary key.</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Price</th>
<th>Department</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Digital SLR Camera</td>
<td>$299</td>
<td>Electronics</td>
</tr>
<tr>
<td>2</td>
<td>Sofa</td>
<td>$699</td>
<td>Furniture</td>
</tr>
<tr>
<td>3</td>
<td>Dining Table</td>
<td>$1299</td>
<td>Furniture</td>
</tr>
<tr>
<td>4</td>
<td>Toaster</td>
<td>$79</td>
<td>Housewares</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>The auto-incrementing primary key <code>ID</code> column starts at 1 and simply increments by 1 for each new record in our <code>Products</code> table. Next, we slightly re-design the <code>Reviews</code> table to point to the product&apos;s <code>ID</code> instead of its <code>Name</code>:</p><!--kg-card-begin: markdown--><table>
<thead>
<tr>
<th>ID</th>
<th>ProductID</th>
<th>Rating</th>
<th>Body</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>1</td>
<td>4</td>
<td>Takes nice pictures!</td>
</tr>
<tr>
<td>2</td>
<td>2</td>
<td>5</td>
<td>Comfy!</td>
</tr>
<tr>
<td>3</td>
<td>1</td>
<td>5</td>
<td>Best camera ever!!!</td>
</tr>
<tr>
<td>4</td>
<td>3</td>
<td>3</td>
<td>Makes toast.</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>And to fix our SQL from above, we join by the <code>ID</code> and <code>ProductID</code>, instead of the <code>Name</code> of the product:</p><pre><code>SELECT Products.Department, Reviews.Product, Reviews.Rating, Reviews.Body
FROM Reviews INNER JOIN Products ON Products.ID = Reviews.ProductID
WHERE Products.Department = &quot;Furniture&quot;;</code></pre><p>The <code>ProductID</code> in the <code>Reviews</code> column is known as the <strong>foreign key</strong>. A foreign key is simply a column in a table that points to a primary key in another table.</p><h3 id="try-it-the-baseball-database"><strong>Try it: the baseball database</strong></h3><p>The baseball database is built exactly this way. <code>players</code> and <code>teams</code> each have an <code>id</code>, and <code>stats</code> holds one row per player per team per season, pointing at both with two foreign keys, <code>player_id</code> and <code>team_id</code>. To ask anything about a player&apos;s season, you join through it:</p><!--kg-card-begin: markdown--><pre><code class="language-sql">SELECT players.first_name, players.last_name, teams.year, teams.name, stats.home_runs
FROM stats
INNER JOIN players ON players.id = stats.player_id
INNER JOIN teams ON teams.id = stats.team_id
WHERE players.last_name = &apos;Griffey&apos;;
</code></pre>
<!--kg-card-end: markdown--><p>Run it on the <strong><strong>Run SQL</strong></strong> page, and look at the <code>id</code> of each player in the results (add <code>players.id</code> to the <code>SELECT</code> to see it). This is the Ken Griffey question from class: two players, one name. A query that adds up his home runs by name answers a question nobody asked. A query that uses his <code>id</code> cannot make that mistake, which is the whole argument of this post in one line.</p>]]></content:encoded></item><item><title><![CDATA[SQL 1: Intro to SQL and the SELECT Statement]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>SQL stands for <em>Structured Query Language</em> - see, you&apos;re halfway through the first sentence of this course, and you&apos;re already half asleep! Simply put, SQL is a way for us humans to communicate with databases. And &quot;database&quot; is a pretty generic term for &quot;</p>]]></description><link>https://entr451.com/the-select-statement/</link><guid isPermaLink="false">6abc0ab4d9019e19441c32f0</guid><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Tue, 29 Sep 2026 19:01:13 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>SQL stands for <em>Structured Query Language</em> - see, you&apos;re halfway through the first sentence of this course, and you&apos;re already half asleep! Simply put, SQL is a way for us humans to communicate with databases. And &quot;database&quot; is a pretty generic term for &quot;way for a computer to store data&quot;. There are lots of ways for a computer to store data, including (but certainly not limited to):</p>
<ul>
<li>CSV, JSON, or other structured text file</li>
<li>Spreadsheet</li>
<li>Document database</li>
<li>Relational database management system (RDBMS)</li>
</ul>
<p>That last one -- relational databases or RDBMS -- is the one we&apos;re going to concentrate on in this course. While there are many software applications that store data in other ways, including those listed, a vast majority of business-class software applications use some flavor of relational database to store data. These &quot;flavors&quot; are vast - some are open-source solutions, and some are proprietary and commercial. Examples of these are:</p>
<ul>
<li>MySQL (open-source)</li>
<li>PostgreSQL (open-source)</li>
<li>SQLite (open-source)</li>
<li>IBM DB2 (closed-source, commercial)</li>
<li>Microsoft SQL Server (closed-source, commercial)</li>
<li>Oracle Database (closed-source, commercial)</li>
</ul>
<p>And, although there are a large number of these flavors, developed by many different companies and organizations, the core of the SQL language is a standard that may be used to communicate with any of them.</p>
<p>Let&apos;s dive right in.</p>
<h2 id="the-basics">The Basics</h2>
<p>Relational databases store data in tabular format. If you&apos;ve ever used Microsoft Excel, Google Sheets, or any other spreadsheet application to store a list of anything, you&apos;ve probably invented a way to organize data that is very similar to how databases do it - perhaps:</p>
<ul>
<li>Your data is stored in a grid format</li>
<li>The columns of the grid represent the <em>attributes</em> of the thing you are storing</li>
<li>There is a <em>row</em> in the grid for each one of the things, and maybe that&apos;s called a <em>record</em></li>
</ul>
<p>For example, if I were to build a spreadsheet of my favorite movies, it might look something like this:</p>
<table>
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Year</th>
<th>Rating</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Star Wars</td>
<td>1977</td>
<td>PG</td>
</tr>
<tr>
<td>2</td>
<td>Apollo 13</td>
<td>2004</td>
<td>PG</td>
</tr>
<tr>
<td>3</td>
<td>The Matrix</td>
<td>2007</td>
<td>R</td>
</tr>
</tbody>
</table>
<p>Pretty straightforward. And, if I want to add more attributes to my data -- say, I want to add the studio that produced the movie -- I&apos;d simply add to the columns of data.</p>
<table>
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Year</th>
<th>Rating</th>
<th>Studio</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Star Wars</td>
<td>1977</td>
<td>PG</td>
<td>Lucasfilm</td>
</tr>
<tr>
<td>2</td>
<td>Apollo 13</td>
<td>2004</td>
<td>PG</td>
<td>Universal</td>
</tr>
<tr>
<td>3</td>
<td>The Matrix</td>
<td>2007</td>
<td>R</td>
<td>Warner Bros.</td>
</tr>
</tbody>
</table>
<p>And, if I want to add more movies, I&apos;d add to the rows of data.</p>
<table>
<thead>
<tr>
<th>#</th>
<th>Title</th>
<th>Year</th>
<th>Rating</th>
<th>Studio</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Star Wars</td>
<td>1977</td>
<td>PG</td>
<td>Lucasfilm</td>
</tr>
<tr>
<td>2</td>
<td>Apollo 13</td>
<td>2004</td>
<td>PG</td>
<td>Universal</td>
</tr>
<tr>
<td>3</td>
<td>The Matrix</td>
<td>2007</td>
<td>R</td>
<td>Warner Bros.</td>
</tr>
<tr>
<td>4</td>
<td>Forrest Gump</td>
<td>1994</td>
<td>PG-13</td>
<td>Paramount</td>
</tr>
</tbody>
</table>
<p>As simple as this example is, this is precisely how data is structured in a relational database system. To offer up some definitions - the columns are known as the <em>fields</em> or <em>attributes</em>, the rows are known as <em>records</em>, and the rows+columns together is a <em>table.</em> When we have a collection of many tables, that&apos;s a <em>database.</em></p>
<p>In practice, the main difference between a spreadsheet (e.g. Excel) and a database system is that Excel already includes a standard front-end by which an end-user (like you) is able to read and write to the data. Since the use-cases for RDBMSes are much more varied and programmable than a spreadsheet, there&apos;s no such standard for relational databases. Instead, we use SQL to talk with the database, and to pass the results on to our front-end of choice, whether that&apos;s a custom web application, a PDF report, a commercial front-end like Tableau, or whatever else might be needed.</p>
<h2 id="using-sql-to-read-data">Using SQL to read data</h2>
<p>In this lesson, we&apos;ll be looking at ways we can use SQL to <strong>read</strong> data from a relational database. In a later lesson, we will be looking at using SQL to <strong>write</strong> to and <strong>modify</strong> a database.</p>
<h3 id="the-select-statement">The SELECT statement</h3>
<p>We read data from a database by using the <code>SELECT</code> statement in SQL. Let&apos;s look at an example. Suppose we own an e-commerce store - we&apos;d probably have a a database table used to store the products we sell. This table would likely be named <code>Products</code> and look something like this:</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Department</th>
</tr>
</thead>
<tbody>
<tr>
<td>Camera</td>
<td>$299</td>
<td>Electronics</td>
</tr>
<tr>
<td>Sofa</td>
<td>$699</td>
<td>Furniture</td>
</tr>
<tr>
<td>Dining Table</td>
<td>$1299</td>
<td>Furniture</td>
</tr>
<tr>
<td>Toaster</td>
<td>$79</td>
<td>Housewares</td>
</tr>
</tbody>
</table>
<p>The <code>SELECT</code> statement allows us to ask the database questions. <em>What products do we sell? How much does the Toaster cost? How many products cost more than $500?</em></p>
<p>It would be nice if the SQL language knew how to answer your question, in exactly the way you asked it. But, no, it&apos;s a computer, so we have to follow a simple formula so that you can ask the question in a way that SQL can understand:</p>
<ul>
<li>Identify the table that holds the information you need</li>
<li>Identify the columns that hold the information you need</li>
<li>Add any other conditions</li>
<li>Then, ask the question in SQL</li>
</ul>
<p>For instance, if my question is: <em>what are the names of the products we sell?</em></p>
<ul>
<li>Identify the table that holds the information you need - <code>Products</code></li>
<li>Identify the columns that hold the information you need - <code>Name</code></li>
<li>Add any other conditions - none</li>
<li>Then, ask the question:</li>
</ul>
<p><code>SELECT Name FROM Products;</code></p>
<p>There are 3 things about this <code>SELECT</code> statement that I&apos;ll point out:</p>
<ul>
<li>It ends with a semicolon <code>;</code> - this depends on the flavor of RDBMS and the front-end being used, but many databases will require the semicolon at the end of the SQL statement, so all the examples we&apos;ll use will include it</li>
<li>The column and table names are case-sensitive; again, this will depend on the specific database system, but for illustration&apos;s sake, all examples will match the table/column name casing in the database</li>
<li>The <code>SELECT</code> and <code>FROM</code> <em>clauses</em> are in ALL CAPS; this is NOT a requirement in SQL, but <em>clauses</em> in examples will be written this way, as it is often more readable</li>
</ul>
<p>The result that we&apos;ll receive is actually another, temporary table - i.e. one that is not part of the database structure or <em>schema</em> as-designed, but is simply a temporary subset of the larger table we&apos;ve targeted. I like to think of this as a &quot;&#x1F476;baby table&quot; - a much smaller table that looks a lot like the larger, original table, but only holds the data we asked for:</p>
<table>
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>Camera</td>
</tr>
<tr>
<td>Sofa</td>
</tr>
<tr>
<td>Dining Table</td>
</tr>
<tr>
<td>Toaster</td>
</tr>
</tbody>
</table>
<p><em>To be clear, &quot;&#x1F476; <strong>baby table</strong>&quot; is not an official SQL term... it&apos;s just what I like to call it.</em></p>
<p>Now, it&apos;s up to you to decide if the results provided in this &quot;&#x1F476; baby table&quot; are everything you need, or if you need to modify your question in order to get more information.</p>
<p><em>Hmmm, it would nice if I could see the price too. Can I get a list of all the products we sell, along with the price?</em></p>
<p>Back to the formula:</p>
<ul>
<li>Identify the table that holds the information you need - <code>Products</code></li>
<li>Identify the columns that hold the information you need - <code>Name</code> and <code>Price</code></li>
<li>Add any other conditions - none</li>
<li>Then, ask the question:</li>
</ul>
<p><code>SELECT Name, Price FROM Products;</code></p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Camera</td>
<td>$299</td>
</tr>
<tr>
<td>Sofa</td>
<td>$699</td>
</tr>
<tr>
<td>Dining Table</td>
<td>$1299</td>
</tr>
<tr>
<td>Toaster</td>
<td>$79</td>
</tr>
</tbody>
</table>
<h3 id="sorting">Sorting</h3>
<p>Now that we&apos;ve seen how to use the <code>SELECT</code> statement in SQL to retrieve rows of data from a database table, let&apos;s take one step toward doing something more meaningful. It&apos;s pretty rare that we&apos;ll want to get all records in a database table, in exactly the order that the records were inserted - this is what we&apos;ve done thus far.</p>
<p>We&apos;ll often want our records in our &#x1F476; <em>baby table</em> to be in a specific order - perhaps alphabetically or sorted by a numeric value. This is going to be based on how the data is ultimately consumed - e.g. in a PDF report for a stakeholder in your company, displayed on-screen for a user of a web application, and so on. Let&apos;s continue with our question and answer format for this one:</p>
<p><em>Can we order the results by price?</em></p>
<ul>
<li>Identify the table that holds the information you need - <code>Products</code></li>
<li>Identify the columns that hold the information you need - <code>Name</code> and <code>Price</code></li>
<li>Add any other conditions - sort by <code>Price</code></li>
<li>Then, ask the question:</li>
</ul>
<p><code>SELECT Name, Price FROM Products ORDER BY Price;</code></p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Toaster</td>
<td>$79</td>
</tr>
<tr>
<td>Camera</td>
<td>$299</td>
</tr>
<tr>
<td>Sofa</td>
<td>$699</td>
</tr>
<tr>
<td>Dining Table</td>
<td>$1299</td>
</tr>
</tbody>
</table>
<p>As we can see, ascending order (from the least to the greatest) is the default when sorting a numeric value. (It&apos;s alphabetically from A-Z with a non-numeric value.)</p>
<p><em>Can we order by the more expensive products first?</em></p>
<ul>
<li>Identify the table that holds the information you need - <code>Products</code></li>
<li>Identify the columns that hold the information you need - <code>Name</code> and <code>Price</code></li>
<li>Add any other conditions - sort by <code>Price</code> in descending order</li>
<li>Then, ask the question:</li>
</ul>
<p><code>SELECT Name, Price FROM Products ORDER BY Price DESC;</code></p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Dining Table</td>
<td>$1299</td>
</tr>
<tr>
<td>Sofa</td>
<td>$699</td>
</tr>
<tr>
<td>Camera</td>
<td>$299</td>
</tr>
<tr>
<td>Toaster</td>
<td>$79</td>
</tr>
</tbody>
</table>
<h3 id="limiting">Limiting</h3>
<p>Another common use-case is that we want to limit the number of rows in our &#x1F476;<em>baby table</em> - what we&apos;ve done so far is just fine for our very small table with 4 rows of data. But what if our <code>Products</code> table contained 10 million rows? The <code>SELECT</code> statements we&apos;ve written thus far would be very slow, and not likely to be very helpful or meaningful to the person receiving the results. So we must often reduce our results, and that usually means refining the question we&apos;re asking.</p>
<p><em>What is the single most expensive product we sell?</em></p>
<ul>
<li>Identify the table that holds the information you need - <code>Products</code></li>
<li>Identify the columns that hold the information you need - <code>Name</code> and <code>Price</code></li>
<li>Add any other conditions - sort by <code>Price</code> in descending order and only return 1 row</li>
<li>Then, ask the question:</li>
</ul>
<pre><code class="language-sql">SELECT Name, Price
FROM Products
ORDER BY Price DESC
LIMIT 1;
</code></pre>
<p>Same results as before - but only returns a single record, answering our question. Also note that we&apos;re separating each fragment of the SQL statement here -- AKA <em>clauses</em> -- by putting each on its own line. As our SQL becomes more complex, I&apos;ve found this easier to read and comprehend -- especially when first learning -- so we&apos;ll do this from now on.</p>
<h3 id="try-it-the-baseball-database">Try it: the baseball database</h3>
<p>Let&apos;s play ball! Open your copy of the <code>baseball</code> repository in its Codespace (the one you made in class; reopen it from <a href="https://github.com/codespaces">github.com/codespaces</a> rather than making a new one), run <code>bin/rails server</code> in the terminal, and open the port it offers. The app opens on the <strong>Run SQL</strong> page, a box that runs whatever SQL you type into it. The database has three tables:</p>
<pre><code>teams (id, year, name, park, wins, losses)
players (id, first_name, last_name, bats, throws)
stats (id, team_id, player_id, games, at_bats, runs, hits, doubles, triples, home_runs, rbis)
</code></pre>
<p>Try it:</p>
<p><code>SELECT first_name, last_name FROM players;</code></p>
<p><strong>Don&apos;t forget that semi-colon!</strong> You should see the first and last names of all 20,358 players in the database. Then try the question from class, the five winningest seasons ever, using the formula above: which table, which columns, sorted how, how many rows?</p>
<p>If you would rather work in the terminal, save a query in a file in <code>queries/</code> and run it with <code>bin/query queries/your-file.sql</code>. And if you want to see the database itself, with no page in front of it, <code>bin/rails db</code> opens SQLite&apos;s own prompt (type <code>.quit</code> to leave). We will live there in week 5.</p>
<h3 id="filtering-data">Filtering Data</h3>
<p>The ability to filter data is probably the most powerful and often-used feature of SQL. For many people, gaining insights from arbitrary sets of data is often the objective of learning SQL in the first place.</p>
<p>We&apos;ve already learned about the <code>LIMIT</code> clause, which simply whittles down the size of our result set -- the &#x1F476; <em>baby table</em> -- by some number that we provide. However, we often want to specify criteria for filtering the results, rather than a set number like <code>10</code>. <em>What are our best-selling products? Which sales tactics are producing the best results this month? How many orders did we receive for the new widget this week?</em></p>
<p>For questions like these, we must use the <code>WHERE</code> clause. The <code>WHERE</code> clause allows us to add more criteria to the questions we ask. Let&apos;s jump into an example using our <em>Products</em> table:</p>
<p><em>Which products cost more than $500?</em></p>
<ul>
<li>Identify the table that holds the information you need - <code>Products</code></li>
<li>Identify the columns that hold the information you need - <code>Name</code> and <code>Price</code></li>
<li>Add any other conditions - <code>Price</code> is more than $500</li>
<li>Then, ask the question:</li>
</ul>
<p><code>SELECT Name, Price FROM Products WHERE Price &gt; 500;</code></p>
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Dining Table</td>
<td>$1299</td>
</tr>
<tr>
<td>Sofa</td>
<td>$699</td>
</tr>
</tbody>
</table>
<p>A <code>WHERE</code> clause is going to always have a column name (in this case, <code>Price</code>), a comparison operator (<code>&gt;</code> or &quot;greater than&quot;) and a value to compare (<code>500</code>). Because the column in question contains numbers, it&apos;s going to compare the <code>Price</code> of every row in the table against the numeric value <code>500</code> and filter the results to only include those with a price greater than that number. Let&apos;s look at what happens with a non-numeric comparison:</p>
<p><em>Which products are sold in the Furniture department?</em></p>
<ul>
<li>Identify the table that holds the information you need - <code>Products</code></li>
<li>Identify the columns that hold the information you need - <code>Name</code> and <code>Price</code> again, but we probably want <code>Department</code> now too</li>
<li>Add any other conditions - <code>Department</code> is <em>Furniture</em></li>
<li>Then, ask the question:</li>
</ul>
<pre><code class="language-sql">SELECT Name, Price, Department 
FROM Products 
WHERE Department = &quot;Furniture&quot;;
</code></pre>
<table>
<thead>
<tr>
<th>Name</th>
<th>Price</th>
<th>Department</th>
</tr>
</thead>
<tbody>
<tr>
<td>Sofa</td>
<td>$699</td>
<td>Furniture</td>
</tr>
<tr>
<td>Dining Table</td>
<td>$1299</td>
<td>Furniture</td>
</tr>
</tbody>
</table>
<p>Again, we have a column name (in this case, <code>Department</code>), a comparison operator (<code>=</code> or &quot;exactly equals&quot;) and a value to compare (<code>&quot;Furniture&quot;</code>). Because <code>Department</code> is non-numeric, the value to compare <strong>must be in quotes</strong>. This is necessary to distinguish the comparison value from columns name, other SQL clauses, variables, and the like.</p>
<p>Separate multiple filters/conditions with the <code>AND</code> clause. So if we wanted the answer to:</p>
<p><em>Which products are sold in the furniture department and cost more than $500?</em></p>
<pre><code class="language-sql">SELECT Name, Price, Department 
FROM Products 
WHERE Department = &quot;Furniture&quot;
  AND Price &gt; 500;
</code></pre>
<h3 id="matching-a-pattern">Matching a pattern</h3>
<p><code>=</code> means <em>exactly equal</em>, and sometimes that is not what you want. <code>LIKE</code> matches a pattern, where <code>%</code> means &quot;anything, or nothing, here.&quot; In the baseball database:</p>
<pre><code class="language-sql">SELECT year, name, park
FROM teams
WHERE park = &apos;Wrigley Field&apos;;
</code></pre>
<p>returns 107 rows, every season played at the ballpark in Chicago. Change the last line to:</p>
<pre><code class="language-sql">WHERE park LIKE &apos;%Wrigley%&apos;;
</code></pre>
<p>and you get 108. The extra one is the 1961 Los Angeles Angels, who played their first season at a different ballpark, <code>Wrigley Field (LA)</code>. Neither query is wrong; they answer different questions. The question you asked and the question you typed are not always the same question, and the only way to know which one you got is to read the <code>WHERE</code>.</p>
<h3 id="try-it-filtering">Try it: filtering</h3>
<p>On the <strong>Run SQL</strong> page:</p>
<ul>
<li><em>Every team that has played at Wrigley Field.</em> Then change <code>=</code> to <code>LIKE</code> and find the extra row.</li>
<li><em>Which team won the most games in 2002?</em> You will probably write <code>ORDER BY wins DESC LIMIT 1</code>. Then change the <code>1</code> to a <code>2</code>. Be honest with yourself about whether you would have checked.</li>
</ul>
<h3 id="a-star-is-born">A Star is Born</h3>
<p>We&apos;ve already seen how SQL queries require us to provide the columns that we want to be returned in the &#x1F476; <em>baby table</em> - and how we often need to think about what columns those are when providing clean, concise output. Occasionally though, the columns don&apos;t really matter. In these cases, we can use the asterisk or star (<code>*</code>) to denote that we want <em>all columns</em> in a table. Consider a <code>Reviews</code> table, which contains reviews about our products. If we simply want <em>all columns, all rows</em> from this table, we could write a query that looks like:</p>
<p><code>SELECT * from reviews;</code></p>
<p>And the result would be everything -- all columns and all rows -- from that table.</p>
<table>
<thead>
<tr>
<th>Product</th>
<th>Rating</th>
<th>Body</th>
</tr>
</thead>
<tbody>
<tr>
<td>Camera</td>
<td>4</td>
<td>Takes nice pictures!</td>
</tr>
<tr>
<td>Sofa</td>
<td>5</td>
<td>Comfy!</td>
</tr>
<tr>
<td>Camera</td>
<td>5</td>
<td>Best camera ever!!!</td>
</tr>
<tr>
<td>Toaster</td>
<td>3</td>
<td>Makes toast.</td>
</tr>
</tbody>
</table>
<p>In practice, we&apos;re rarely going to use it like this, unless a table contains just a few columns, or when we don&apos;t care about concise output, like when we&apos;re testing our queries. Or, within an aggregate function, which is where the <a href="https://entr451.com/sql-aggregates-and-grouping/">optional next post</a> picks up.</p>
<h3 id="order-of-clauses">Order of Clauses</h3>
<p>We&apos;ve seen enough clauses now to talk about the order they go in. Because of the way SQL is executed, the order in which the clauses are written does matter:</p>
<ul>
<li><code>SELECT</code> - the columns you want in the result</li>
<li><code>FROM</code> - the table(s) involved</li>
<li><code>WHERE</code> - the conditions or constraints</li>
<li><code>ORDER BY</code> - columns by which the results shall be ordered, either numerically or alphabetically</li>
<li><code>LIMIT</code> - how many rows to return</li>
</ul>
<p>To put it all together, in the baseball database:</p>
<p><em>Which three teams won the most games in the 2000s?</em></p>
<pre><code class="language-sql">SELECT year, name, wins
FROM teams
WHERE year &gt;= 2000 AND year &lt; 2010
ORDER BY wins DESC
LIMIT 3;
</code></pre>
<p>Run it, then change the <code>3</code> to a <code>4</code>. The 2002 Yankees are third with 103 wins. The 2002 Oakland Athletics also won 103, and they&apos;re fourth only because of the order the rows happen to sit in. <code>LIMIT</code> cuts where you tell it to, whether or not that&apos;s a sensible place to cut.</p>
<h3 id="going-further-optional">Going further (optional)</h3>
<p>That&apos;s everything we covered in class, and everything you need to read the SQL a chat writes for you in this course. If you&apos;re curious about the math side of SQL, counting, averaging and grouping, it&apos;s in <a href="https://entr451.com/sql-aggregates-and-grouping/">the optional next post</a>. Otherwise, on to <a href="https://entr451.com/table-relationships/">SQL 2: Table Relationships</a>.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Judging the analyst's answer]]></title><description><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<p>This is the longer version of the exercise at the center of Week 2: asking a chat about a database, reading the SQL it writes, and deciding whether to believe the number. If the loop went by too fast in class, or you want the reasoning behind it, start here.</p></blockquote>]]></description><link>https://entr451.com/judging-the-analysts-answer/</link><guid isPermaLink="false">6abc0870d9019e19441c32ba</guid><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Tue, 29 Sep 2026 18:55:03 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<p>This is the longer version of the exercise at the center of Week 2: asking a chat about a database, reading the SQL it writes, and deciding whether to believe the number. If the loop went by too fast in class, or you want the reasoning behind it, start here. The SQL we covered (SELECT, WHERE, ORDER BY, LIMIT, joins, keys) is in <a href="https://entr451.com/the-select-statement/">SQL 1</a> and <a href="https://entr451.com/table-relationships/">SQL 2</a>.</p>
</blockquote>
<h2 id="you-were-the-stakeholder">You were the stakeholder</h2>
<p>Every company that runs on data has the same three kinds of people touching it. Analysts read data and turn it into decisions. Developers build the applications that read and write it. Database administrators change its structure. And then there is everyone else, which is most people, and probably you: the stakeholder who has a question, hands it to someone who can answer it, and gets a number back.</p>
<p>In class, the chat was the analyst and you were the stakeholder. You did not write the SQL. Your job was harder: decide whether to believe it.</p>
<p>That is the job this course keeps giving you, and it gets more important, not less, as the tools get better. An analyst who is right nine times out of ten is extremely useful, as long as somebody can tell which time is the tenth.</p>
<h2 id="the-analyst-knows-only-what-you-give-it">The analyst knows only what you give it</h2>
<p>Before the first question, you pasted a brief into your chat. It said: this is a SQLite database, these are the tables and their columns, the seasons run from 1871 to 2020, and when I ask a question, give me the SQL and the answer you expect it to return.</p>
<p>That brief is the whole of what the chat knew about your database. It never saw the file. It could not look at a row. Everything it got right came from what you gave it, and everything it got wrong came from what you did not.</p>
<p>If you skipped the brief, you saw what happens instead: the chat invents a schema. It writes a confident query against a <code>franchise</code> column or an <code>HR</code> column that does not exist, and the query fails. That is the cheap version of the lesson. The expensive version is a question with just enough context to run and not enough to be right.</p>
<p>The word for what you pasted is <strong>context</strong>, and it will come back all quarter.</p>
<h2 id="steps">Steps</h2>
<ol>
<li><strong>Ask.</strong> Paste the question exactly as written.</li>
<li><strong>Read.</strong> What comes back is two things: the SQL, and the number the chat expects it to return.</li>
<li><strong>Run.</strong> Paste the SQL into the <strong>Run SQL</strong> page yourself, and compare what the database returns with what the chat predicted.</li>
<li><strong>Save.</strong> Keep the query in <code>queries/</code>, with the question on the first line, so the <strong>Queries</strong> page shows it next to the others.</li>
<li><strong>Decide.</strong> Write one sentence: trust, or don&apos;t trust, because.</li>
</ol>
<p>The &quot;because&quot; is the skill. &quot;Trust, because the SQL filters on the right year and 107 wins is a believable season&quot; is a judgment. &quot;Trust, because it looked right&quot; is a coin flip with extra steps.</p>
<h2 id="the-number-was-memory-the-sql-was-work">The number was memory. The SQL was work.</h2>
<p>Asking for both the SQL and the expected answer was deliberate, because they come from different places.</p>
<p>The chat has read an enormous amount about baseball. Ask it who won the most games in 2019 and it will tell you the Houston Astros, 107, before it has written a line of SQL, because it remembers. That expected answer is a prediction from memory. It is often right. It is not evidence.</p>
<p>The SQL is the only part of the reply that touches your data. When you run it, the database answers the question the SQL asks, which is not always the question you asked.</p>
<p>On the first question, the two agreed: the query returned the Astros with 107, and the chat had said so. That agreement is what trust looks like. On the Ken Griffey question, some of you saw them disagree: the chat said 630 home runs, and its own SQL returned 782. Only one of those numbers came from the data, and it was not the one it was confident about.</p>
<h2 id="three-ways-a-correct-looking-answer-goes-wrong">Three ways a correct-looking answer goes wrong</h2>
<p>None of these produce an error. The query runs, a number appears, and nothing on the screen tells you anything is wrong.</p>
<h3 id="the-question-meant-more-than-one-thing">The question meant more than one thing</h3>
<p>&quot;Which team has lost the most games in history?&quot; In this database there are at least three answers:</p>
<ul>
<li>The worst single season: the 1899 Cleveland Spiders, 20 wins and 134 losses.</li>
<li>The most losses added up by team name: the Philadelphia Phillies, 10,426.</li>
<li>The most losses by franchise, which is what a fan means: <strong>unanswerable</strong>. The database has no idea that the Cleveland Bronchos, Naps and Indians are one club, or that the Cleveland Spiders were a different one. It has team names per season, and Cleveland alone appears under seven of them.</li>
</ul>
<p>The chat had to pick one, and most of them picked without telling you. Some asked which one you meant, or gave you both, which is what a good analyst does. Either way, the ambiguity was in the question before it was in the SQL. If you do not say which answer you want, someone else decides.</p>
<p>And when none of the readings is the one you meant, that is not a SQL problem. It is a data model problem: the database is missing something (here, a franchise) that the question assumes. Which is where the second half of the class went.</p>
<h3 id="the-query-matched-on-a-name">The query matched on a name</h3>
<p>&quot;How many home runs did Ken Griffey hit?&quot; There are two Ken Griffeys: the father, with 152, and the son, the Hall of Famer, with 630. A query that finds him with <code>WHERE first_name = &apos;Ken&apos; AND last_name = &apos;Griffey&apos;</code> adds them together and returns 782, a number that is exactly right for a question nobody asked.</p>
<p>The same thing turned up earlier in the day, on purpose. <code>WHERE park = &apos;Wrigley Field&apos;</code> finds 107 seasons, all in Chicago. <code>WHERE park LIKE &apos;%Wrigley%&apos;</code> finds 108, because it also catches the 1961 Los Angeles Angels, who played their first season at a different ballpark named Wrigley Field. The question you asked and the question you typed are not always the same question.</p>
<p>This is what the primary key lecture was for. Names are for humans: they get misspelled, shared and reused. An id is the one thing guaranteed to mean one row. When the answer is about a particular person, place or thing, look at the WHERE and ask whether it identifies that thing, or just something with the same name.</p>
<h3 id="rows-went-missing">Rows went missing</h3>
<p>&quot;Which team won the most games in 2002?&quot; The natural query is:</p>
<pre><code class="language-sql">SELECT year, name, wins
FROM teams
WHERE year = 2002
ORDER BY wins DESC
LIMIT 1;
</code></pre>
<p>It returns the New York Yankees, 103 wins. It is also wrong, or at least half of the answer: the Oakland Athletics won 103 games that year too. That is the Moneyball season. <code>LIMIT 1</code> kept the Yankees only because of the order the rows happened to be stored in. Change it to <code>LIMIT 2</code> and the A&apos;s appear.</p>
<p>A missing row is the hardest mistake to catch, because there is nothing on the screen to look at. You can read a wrong WHERE clause. You cannot read a row that is not there.</p>
<h2 id="you-can-test-a-feature-you-can-only-check-a-query">You can test a feature. You can only check a query.</h2>
<p>When a developer builds a feature or fixes a bug, there is a way to know it worked. Click through it the way a user would. Better, write a test: a small program that says what should happen and checks that it does, run every time the code changes. At the end of class you watched an agent do exactly that, adding a test for 140-character captions and running it.</p>
<p>A query does not get that. It runs, a number comes back, and nothing on the screen says whether it answered the question. Some teams build a small test database, stocked with the edge cases they already know about (the legacy rows, the duplicates, the weird old records), and run their queries against it. That helps. Mostly, though, someone spot-checks the result, and spot-checking takes knowing the data: its edge cases, its quirks, and the places where it is not as clean as it looks.</p>
<p>Look at the traps above again. The 2002 tie, the two Ken Griffeys, Cleveland under seven names, the other Wrigley Field: none of them was a SQL mistake. Each was a fact about the data that you had to already know to notice. And nobody hands you the expected result. If the stakeholder knew the answer, they would not be asking.</p>
<p>None of this is new with AI. It was true when analysts wrote every line of SQL by hand, and it is still true now that a chat writes it. What changed is how fast a confident answer arrives. The checking did not change.</p>
<h2 id="four-cheap-checks">Four cheap checks</h2>
<p>None of these need more SQL than you already know.</p>
<ol>
<li><strong>Say what you expect before you run it.</strong> Even roughly: &quot;a team, a hundred-ish wins, this century.&quot; A result that surprises you is the only alarm you get, and you cannot be surprised if you had no expectation.</li>
<li><strong>Ask for one more row than you need.</strong> If the question is &quot;which one&quot;, run it with <code>LIMIT 3</code>. Ties and near-ties show up immediately, and so does a second Ken Griffey.</li>
<li><strong>Count what came back.</strong> If you asked for the best team in every season since 1960, you should get at least 61 rows, one per season. Fewer means something was dropped. More is not necessarily wrong (in six of those seasons there was a tie), but you should know why.</li>
<li><strong>Read the WHERE out loud.</strong> Does it say what you asked? A name where an id should be, a <code>LIKE</code> where you meant an exact match, a year range that is off by one: the WHERE clause is where most wrong answers live, and it is usually one line.</li>
</ol>
<h2 id="when-the-analyst-is-good">When the analyst is good</h2>
<p>Some of you watched your chat get everything right: it named both readings of the losing question, split the two Ken Griffeys by id, or wrote the 2002 query so that it returned both teams. The better chats are good at this, and increasingly so.</p>
<p>Check anyway. The chat caught the 2002 tie because it remembers the 2002 season. It does not remember your company&apos;s customers, your pricing table or last quarter&apos;s refunds, and on that data it will write the same careful-looking SQL with nothing to check it against but you. Good analyst. Now check that it was right. The loop does not change.</p>
<h2 id="never-run-update-or-delete-without-where">Never run UPDATE or DELETE without WHERE</h2>
<p>Everything above is about reading. This is about the one time it stops being safe to get it wrong.</p>
<p><code>UPDATE</code> and <code>DELETE</code> change data, and both apply to every row the WHERE clause matches. With no WHERE clause, that is every row in the table. <code>DELETE FROM teams;</code> does not ask which teams. It removes all 2,955 of them.</p>
<p>In class you watched an AI agent (a chat that can run commands itself, not just suggest them) get asked to &quot;clear out the teams table so we can reload it from a fresh export.&quot; It wrote exactly that statement. It stopped and asked first only because the repository has a file, <code>AGENTS.md</code>, telling any agent working there to show a change to the data and wait for confirmation. Without that line, it runs.</p>
<p>Then it ran, and the table was empty. The database came back because in this class the database is a single file that lives in Git, and <code>git checkout db/baseball.sqlite3</code> restored it. Production databases do not work like that. Most of the time there is no undo, only a backup, if someone made one, from some time before the mistake.</p>
<p>So: when you see UPDATE or DELETE, from a chat, an agent, a colleague or yourself, find the WHERE before anything else. If there is not one, stop.</p>
<h2 id="the-thread-through-all-of-it">The thread through all of it</h2>
<p>Every trap in this post ran without an error. The SQL was valid, a number came back, and the only thing between that number and a decision was someone reading it. Writing SQL is increasingly something you will hand off. Deciding whether to believe it is not.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Working in a Codespace]]></title><description><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<p>Most weeks from here on start the same way and end the same way. This is the short version of both, plus how to reset the database and one settings fix for Windows. Keep it open the first few times; after that you will not need it.</p>
</blockquote>
<h2 id="before-week-2-have-a-chat-open">Before Week 2:</h2>]]></description><link>https://entr451.com/working-in-a-codespace/</link><guid isPermaLink="false">6abb3ef6d9019e19441c3286</guid><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Tue, 29 Sep 2026 11:53:33 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<p>Most weeks from here on start the same way and end the same way. This is the short version of both, plus how to reset the database and one settings fix for Windows. Keep it open the first few times; after that you will not need it.</p>
</blockquote>
<h2 id="before-week-2-have-a-chat-open">Before Week 2: have a chat open</h2>
<p>In Week 2 you ask an AI chat questions about a database and decide whether to believe its answers. Bring whichever one you already use: ChatGPT, Claude, Gemini, Copilot. Any of them is fine. There is nothing to install and nothing to sign up for. Have it open in a second window before class starts.</p>
<h2 id="getting-started">Getting started</h2>
<ol>
<li>
<p><strong>Use this template.</strong> On the class repository&apos;s page, click <strong>Use this template</strong>, then<br>
<strong>Create a new repository</strong>. Pick your own account as the owner, keep the name, and click<br>
<strong>Create repository</strong>. You now have your own copy; nothing you do can affect anyone else&apos;s.</p>
</li>
<li>
<p><strong>Create the Codespace.</strong> In your new repository: <strong>Code</strong>, then the <strong>Codespaces</strong> tab,<br>
then <strong>Create codespace on main</strong>. The first start takes a few minutes. Let it finish; the<br>
terminal at the bottom is ready when it shows a prompt and stops scrolling.</p>
</li>
<li>
<p><strong>Start the server.</strong> In the terminal:</p>
<pre><code>bin/rails server
</code></pre>
<p>A box pops up offering to open the port. Click <strong>Open in Browser</strong>. If you miss the box, open the <strong>Ports</strong> tab next to the terminal and click the globe icon on port 3000.</p>
</li>
</ol>
<p>The server keeps running in that terminal until you stop it with <strong>Ctrl-C</strong>. If you need a terminal for anything else, open a second one with the <strong>+</strong> on the terminal panel.</p>
<p><strong>Coming back later.</strong> Do not create a second Codespace. Go to <code>github.com/codespaces</code>, or<br>
<strong>Code</strong> &gt; <strong>Codespaces</strong> on your repository, and open the one you already have. Your files are where you left them. The server is not; run <code>bin/rails server</code> again.</p>
<h2 id="windows-turn-on-right-click">Windows: turn on right-click</h2>
<p>On some Windows machines, right-clicking in the terminal does not open a menu, and the menu is how you find things like <strong>Clear</strong>. To fix it:</p>
<ol>
<li>Click the gear icon at the bottom left of the editor (the one inside the editor, not the one at the far left of your screen), then <strong>Settings</strong>. Not the command palette; the setting is not there.</li>
<li>Search for <code>right click behavior</code>.</li>
<li>Under <strong>Terminal &gt; Integrated: Right Click Behavior</strong>, yours probably says <code>copyPaste</code>. Change it to <code>selectWord</code>.</li>
</ol>
<p>It saves itself. Close the Settings tab and right-click in the terminal again. If you are on a Mac, you can skip this.</p>
<h2 id="resetting-the-database">Resetting the database</h2>
<p>In Week 2 the whole database is one file, <code>db/baseball.sqlite3</code>, and it is committed to the repository. So if anything damages it (a DELETE or UPDATE you did not mean, a table you dropped), you can put it back exactly as it was. In the terminal:</p>
<pre><code>git checkout db/baseball.sqlite3
</code></pre>
<p>Then refresh the page. The rows are back. The server can keep running while you do it.</p>
<p>This works as long as you have not committed the damage. If <code>db/baseball.sqlite3</code> ever shows up under <strong>Changes</strong> when you go to commit, run the line above first, then commit.</p>
<h2 id="wrapping-up">Wrapping up</h2>
<ol>
<li><strong>Stage.</strong> Open <strong>Source Control</strong> in the left bar (the icon that looks like a branching line). Your changed files are listed under <strong>Changes</strong>. Click the <strong>+</strong> next to each one, or the <strong>+</strong> on the <strong>Changes</strong> heading to stage them all.</li>
<li><strong>Message, then commit.</strong> In the box above the list, write what you did in plain words, like <code>answered the three questions</code>. Click <strong>Commit</strong>.</li>
<li><strong>Push.</strong> The button turns into <strong>Sync Changes</strong>. Click it. That sends your commit to GitHub; until you do, it exists only inside the Codespace. Check your repository&apos;s page on github.com: your commit message should be there.</li>
<li><strong>Stop the Codespace.</strong> On your repository&apos;s page: <strong>Code</strong> &gt; <strong>Codespaces</strong>, the three dots next to yours, <strong>Stop codespace</strong>. It stops itself after thirty minutes idle, but doing it yourself is the habit, and Codespaces hours come out of your own monthly allowance.</li>
</ol>
<p>Stopping is not deleting. Your Codespace and everything in it are still there next time.</p>
<h2 id="when-it-does-not-work">When it does not work</h2>
<p>Post in <code>#fall-2026</code> on Slack with a screenshot and the step you were on. If it is during class, raise your hand; an FA will come to you.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Open source and licensing, the longer version]]></title><description><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<p>This is one of two posts from the first week. The other, <a href="https://entr451.com/git-and-github/">Git and GitHub</a>, is the tools: what they are and how a change gets into a codebase. This one is about whose code you are willing to build on in the first place.</p>
</blockquote>
<p>In class we have (or</p>]]></description><link>https://entr451.com/open-source-and-licensing/</link><guid isPermaLink="false">6ab2cfdbd9019e19441c3256</guid><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Tue, 22 Sep 2026 19:10:31 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<p>This is one of two posts from the first week. The other, <a href="https://entr451.com/git-and-github/">Git and GitHub</a>, is the tools: what they are and how a change gets into a codebase. This one is about whose code you are willing to build on in the first place.</p>
</blockquote>
<p>In class we have (or will) spend a little time on open source and licensing -- enough to give you the shape of it and not much else. This is the longer version, for when you need it.</p>
<p>And you likely will need it. Not because you are going to become a lawyer, but because at some point somebody on your team is going to say &quot;let&apos;s just use this library,&quot; and you are going to be the person who decides. That decision has terms attached to it, and the terms occasionally bite.</p>
<h2 id="closed-and-open">Closed and open</h2>
<p>Software gets built and brought to market two ways.</p>
<p><strong>Closed-source</strong> software is a commercial asset. The code is seen only by the people working on it, and the intellectual property belongs to whoever owns the company. Most software that is sold, whether packaged or as a service, works this way. If a company&apos;s secret sauce has anything to do with the code, they are not going to show you the code.</p>
<p><strong>Open-source</strong> software has its source code available to the public. That is the whole definition. Note what it does not say: it does not say free, it does not say non-commercial, and it does not say nobody owns it. Plenty of open-source software is built by large companies, for commercial reasons, and is owned by someone specific.</p>
<h2 id="why-anyone-gives-work-away">Why anyone gives work away</h2>
<p>The reasonable first question is why a developer or a company would put thousands of hours into something and then hand it over.</p>
<ul>
<li><strong>Crowdsourcing.</strong> Publish the code and other people find your bugs, and sometimes fix them. The project gets better than you could have made it alone.</li>
<li><strong>Knowing what is in your food.</strong> Telegram publishes its source so it can prove it is not doing anything malicious with your data. A closed product cannot make that promise; it can only assert it.</li>
<li><strong>Reputation.</strong> An individual developer open-sources a piece of their work so that employers and future partners can see what they are capable of.</li>
<li><strong>Recruiting.</strong> A company open-sources something interesting so that the developers they want to hire come and look at it.</li>
<li><strong>The ecosystem, which is really marketing.</strong> Google does not open-source all of Chrome, but the engine underneath it is open. Other browsers get built on that engine, and when one of those browsers does not work out, moving to Chrome is easy, because it is the thing you were already standing on. Being the foundation other people build on is a competitive position, not charity.</li>
</ul>
<p>The biggest reason underneath all of those is less strategic than any of them. Software is built the way science is built, by people standing on each other&apos;s work. Donald Knuth, the Stanford computer scientist, put it this way:</p>
<blockquote>
<p>People think that computer science is the art of geniuses but the actual reality is the opposite, just many people doing things that build on each other, like a wall of mini stones.</p>
</blockquote>
<p>That has never been more true than it is right now. The reason you can build something sophisticated in a quarter is that thousands of people already built the parts you are standing on.</p>
<p>Here is what that looks like in practice. Every application with users has to handle user security: passwords, sessions, encryption. Almost no business is <em>about</em> user security. It is table stakes, it is subtle, it is easy to get wrong in ways that hurt people, and writing it yourself costs you weeks you needed for the thing your product is actually for. So you take a library that already exists, that thousands of other applications have already stress tested, and you spend your weeks on your own problem instead.</p>
<p>Do not fool yourself into thinking you are going to write all of your own code. Nobody does this. You would never want to, because you would spend all your time in the foundations and never get to the interesting part.</p>
<h2 id="licensing-the-two-families">Licensing: the two families</h2>
<p>Open source is not a free-for-all. Every open-source project carries a license, and the license is a real legal document with real terms. There are many of them and there are endless variations, and when it matters you read the actual text or you ask someone whose job that is. But nearly all of them fall into one of two families.</p>
<p><strong>Permissive.</strong> Do what you like. Use it, change it, sell it, bundle it into something closed-source. Most permissive licenses ask for one thing in return: keep the copyright notice around so people can tell where the code came from. The MIT license is the most common one you will see, and Apache 2.0 is the other big one, which adds some explicit language about patents.</p>
<p><strong>Copyleft.</strong> You may use this, and you may change it, but if you distribute what you built, the result has to carry this same license. The GNU General Public License, the GPL, is the one you will run into. The point is reciprocity: the code was given to you open, and whatever you make out of it goes out open too. Sometimes it is called &quot;share-alike,&quot; which is a better name for what it actually does.</p>
<p><strong>One thing worth being precise about</strong>, because the short version in class makes it easy to get backwards: copyleft does not mean the code is frozen. You are absolutely allowed to modify GPL code. Modifying it is the point. What you owe in return is that your modified version goes out under the same license, so that the next person gets the same freedom you got. The obligation is on <em>what you publish</em>, not on <em>what you touch</em>.</p>
<p>It is permissive licenses, not copyleft ones, where &quot;leave it alone&quot; comes closest to the truth, and even there the obligation is usually just to preserve the notice.</p>
<p><a href="https://choosealicense.com/">choosealicense.com</a> is a genuinely good, short guide to picking one, and it is the right link to send a co-founder who is about to make this decision for your company.</p>
<h2 id="case-study-the-linux-family-tree">Case study: the Linux family tree</h2>
<p>Linux is the most widely deployed operating system in the world. Not Windows, not macOS. Estimates move around and depend on what you are counting, but the large majority of the web servers on the internet run some flavor of Linux, as does every one of the world&apos;s fastest supercomputers, and so does every Android phone. It was written by Linus Torvalds, who also<br>
later wrote Git, which is the other thing we covered in week one. He has had a productive career.</p>
<p>The core of Linux, called the kernel, is licensed under GPLv2. Copyleft. That single fact shapes an entire industry, and the shape is worth following.</p>
<p><strong>Distributions.</strong> Ubuntu, Debian, Red Hat and Fedora are all built around that same kernel. Their kernel remains GPL. They wrap it in package managers, installers, desktop environments and support contracts, and those pieces carry their own licenses. Red Hat built a large public company selling support and certification for software anyone could download for nothing.</p>
<p><strong>Android.</strong> In 2008 a team at Google built a phone operating system on the Linux kernel. Android&apos;s own layers, the parts Google wrote, are licensed under Apache 2.0, which is permissive. That combination surprises people, and it is worth understanding why it is allowed, because it is the single most useful thing in this whole post.</p>
<p>It is not because Google left the kernel alone. Google modifies the kernel substantially for Android, and those modifications are published under the GPL, exactly as the license requires.</p>
<p>It is because of a boundary. Torvalds attached a note to the kernel&apos;s license saying that ordinary user-space programs that call into the kernel through normal system calls are not derivative works of the kernel. They are just using it. Android&apos;s own layers sit on the user-space side of that line, so they do not inherit the kernel&apos;s license and Google can license them as it chooses. Google also deliberately built Android&apos;s C library from permissively licensed code rather than the copyleft one Linux distributions normally use, for exactly this reason.</p>
<p>The lesson generalizes past Android: <strong>where the boundary falls between your code and the copyleft code is the question that decides your obligations.</strong> Linking a GPL library directly into your application is a very different act from running a GPL program as a separate process and talking to it. If your company is ever seriously weighing this, that is the question to bring to a lawyer, and it is a question you can now ask precisely.</p>
<p><strong>On top of Android.</strong> Because Android is permissive, anyone can fork it and build a commercial product. Amazon did, and Fire OS runs the Kindle Fire line. Meta built the software for its VR headsets on it. Peloton runs it on the screen bolted to the bike. None of those companies had to open-source the product they built.</p>
<p>A small correction to something easy to assume here: Amazon is not prevented from charging money for Fire OS. It could. It does not, because Amazon&apos;s interest is in selling devices and content, and giving the operating system away with the hardware serves that. The constraint is a business decision, not a licensing one.</p>
<p>Follow that chain once more, because it is the point of the whole case study. A copyleft kernel, given away in 1991. A permissive operating system built on it by one of the largest companies in the world. Closed commercial products built on that, by three more. Your closed-source product will sit on a stack like this one, whether or not you ever look down.</p>
<h2 id="the-four-questions-you-asked-in-class">The four questions you asked in class</h2>
<p>These came from the room, not from the slides, and they are the right questions.</p>
<h3 id="how-is-any-of-this-actually-enforced">How is any of this actually enforced?</h3>
<p>Mostly the way any contract is: somebody notices. An employee, a competitor, a customer who asks for the source, or one of the organizations that exists specifically to notice. The Free Software Foundation and the Software Freedom Conservancy have both pursued GPL violations, usually by asking first and suing rarely.</p>
<p>The famous case is a router. In the early 2000s Linksys shipped the WRT54G, a wireless router that turned out to be running Linux inside. The GPL obliged them to release the source for what they shipped, they had not, and the community noticed and pushed. Cisco, which by then owned Linksys, released it. The released code became OpenWrt, which is still running on routers today. The violation ended up producing a piece of open-source infrastructure.</p>
<p>Here is the version you are more likely to live through, which happened to a company one of us worked with. They were using an open-source front-end framework. The framework changed its license on a new version. They upgraded, because they wanted the new version, and nobody on the team connected the upgrade to the terms. About a year later somebody worked out that they had been out of compliance the whole time, and it was settled with a payment.</p>
<p>Nobody was reckless. They upgraded a dependency, which is a thing engineering teams do dozens of times a quarter without anyone thinking about it, and the terms had moved underneath them.</p>
<h3 id="does-linux-force-updates-on-you">Does Linux force updates on you?</h3>
<p>No. The project keeps developing, and you run whatever version you chose to run. Nobody can reach into your product and change it.</p>
<p>You will usually want the updates anyway, because that is where the security patches are and because software that stays still gets stranded as everything around it moves. But taking them is your decision, and it is a decision, which matters for the next question.</p>
<h3 id="can-a-project-change-its-license-retroactively">Can a project change its license retroactively?</h3>
<p>Not for the version you already have. The copy you received came with terms, and those terms came with it.</p>
<p>The next version is a different matter. A project can license its next release however it likes, and if you upgrade, you have accepted the new terms. That is the trap in the story above, and it is worth stating plainly: <strong>the risk does not arrive when the license changes. It arrives when you upgrade.</strong></p>
<p>This is not hypothetical or rare. Over the last several years, a number of widely used projects have changed their terms on a new version, sometimes to a license that is no longer open source at all. Redis, MongoDB, Elasticsearch and HashiCorp&apos;s Terraform all did some version of this, and in several cases the community responded by forking the last version under the old terms and continuing from there. If your product depends on something, a license change upstream is a real business event, not a legal footnote.</p>
<p>The practical defense is unglamorous and it is the same defense as for every other dependency risk: know what you depend on, and have someone actually look when a major version lands.</p>
<h3 id="does-everything-built-on-copyleft-code-have-to-be-open-source">Does everything built on copyleft code have to be open source?</h3>
<p>No, and the Android section above is the long answer. The short answer is that it depends on whether what you built is a derivative work of the copyleft code or a separate thing that uses it, and that boundary is technical as much as legal.</p>
<p>What is not optional: the copyleft code itself, and your changes to it, stay under that license and have to be available to the people you distributed it to. You cannot take GPL code private. You can, in many architectures, build something of your own next to it.</p>
<h3 id="and-one-you-asked-that-we-could-not-answer">And one you asked that we could not answer</h3>
<p>Somebody asked why the authors of copyleft projects care, since a permissive license would get their code used more widely. It is a good question and it deserves a real answer.</p>
<p>The answer is that for the people who started this, wide adoption was never the goal. Richard Stallman and the Free Software Foundation designed the GPL in the 1980s to protect a commons. The worry was specific: that software written to be free would get absorbed into closed products, improved there, and that the improvements would never come back, leaving the open version permanently behind. Copyleft is the mechanism that stops that. It is a one-way ratchet, and every improvement anyone distributes lands back in the pool.</p>
<p>Permissive licensing makes a different bet, which is that the widest possible adoption is worth more than the guarantee of reciprocity. Both bets have paid off enormously. They are simply different bets, and a project&apos;s license tells you which one its authors made.</p>
<h2 id="reading-a-repository-before-you-depend-on-it">Reading a repository before you depend on it</h2>
<p>The part of this that will matter to you most often is not the law. It is judgment: deciding whether some project you found is something you want your product to stand on. You will make this call many times, and usually in about four minutes.</p>
<p>Open the repository on GitHub and look at these, roughly in this order.</p>
<ol>
<li><strong>The license.</strong> It is listed in the sidebar. If there is no license file at all, the default is that you have no rights to use it, whatever the author intended by putting it in public. &quot;No license&quot; is a red flag, not a green one.</li>
<li><strong>The last commit.</strong> Near the top of the file list. A project last touched four years ago is a project you are adopting, not using.</li>
<li><strong>Issues and pull requests.</strong> Do not just read the count. Open the tabs. Are maintainers replying? Are pull requests getting merged or quietly piling up? An open issue list is healthy. An unanswered one is not.</li>
<li><strong>Releases.</strong> Regular releases mean somebody is still steering.</li>
<li><strong>Who is behind it.</strong> One person&apos;s side project and a thing maintained by a company or a foundation carry very different risk. Neither is disqualifying. You just want to know which one you are picking.</li>
<li><strong>Stars and forks.</strong> Useful, weak signals. Stars are closer to a bookmark count than a quality score, and old popular projects keep their stars long after the maintainers have moved on. Read them alongside the last commit, never alone.</li>
<li><strong>Documentation, and a demo if there is one.</strong> Many projects run a live demo page. It is the fastest way to find out whether the thing does what you think it does.</li>
</ol>
<p>Three worth opening, because they are good examples of different shapes:</p>
<ul>
<li><a href="https://github.com/microsoft/vscode">microsoft/vscode</a>, the editor you are using in your Codespace, maintained by a large company in the open.</li>
<li><a href="https://github.com/nasa/openmct">nasa/openmct</a>, NASA&apos;s mission control framework, with a demo page you can click through.</li>
<li><a href="https://github.com/chromium/chromium">chromium/chromium</a>, the engine underneath Chrome and most other browsers, and an example of the ecosystem argument from the top of this post.</li>
</ul>
<h2 id="what-this-is-really-about">What this is really about</h2>
<p>You are not going to be the person reading the license text. You are going to be the person who decides whether the team takes the dependency, and who gets asked, a year later, why the company is having this conversation with a lawyer.</p>
<p>The three things worth carrying out of here: <strong>open source is not free of terms</strong>, <strong>the terms change on upgrade and not before</strong>, and <strong>where the boundary falls between your code and theirs is the question that determines what you owe.</strong> Everything else you can look up.</p>
<hr>
<p>Next: <a href="https://entr451.com/practice-github/">Practice: GitHub</a>, where you run the loop once on your own. If you have not read <a href="https://entr451.com/git-and-github/">Git and GitHub</a> yet, read that first; it covers the<br>
difference between the two and how a change actually gets into a codebase.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Practice: GitHub]]></title><description><![CDATA[<!--kg-card-begin: markdown--><p>In class you ran the loop once: a branch, a file, a commit, a pull request, somebody else&apos;s review, a merge. You did it in a room with forty other people doing the same thing and an FA two seats away.</p>
<p>This week you do it again, alone,</p>]]></description><link>https://entr451.com/practice-github/</link><guid isPermaLink="false">6ab2d11cd9019e19441c3267</guid><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Tue, 22 Sep 2026 19:04:56 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><p>In class you ran the loop once: a branch, a file, a commit, a pull request, somebody else&apos;s review, a merge. You did it in a room with forty other people doing the same thing and an FA two seats away.</p>
<p>This week you do it again, alone, on a repository of your own where nothing can go wrong. That is the entire point of this exercise. The first time you follow along. The second time you find out what you actually know.</p>
<p>It should take about thirty minutes. There is nothing to turn in.</p>
<h2 id="part-0-get-your-own-copy">Part 0: get your own copy</h2>
<p>If you already made your own <code>hello-world</code> during class, skip this.</p>
<p>Go to <code>github.com/dsgn425-fall2026/hello-world</code>, click the green <strong>Use this template</strong> button, then <strong>Create a new repository</strong>. Owner: your own account, not the class organization. Name: <code>hello-world</code>. Leave the visibility on <strong>Private</strong>. Click <strong>Create repository</strong>.</p>
<p>Check the name at the top left of the page. It should read <code>your-username/hello-world</code>. If it still says <code>dsgn425-fall2026/hello-world</code>, you are looking at the template, not your copy; go back and click <strong>Use this template</strong>.</p>
<p>It is yours now. You cannot break anything that belongs to anyone else, which is why we are doing this here rather than in the class <code>roster</code> repo.</p>
<h2 id="part-1-the-loop-by-yourself">Part 1: the loop, by yourself</h2>
<p>Open your copy and follow its README from step 2 to step 6. Do not read ahead and do not copy what you did in class from memory. Work it through.</p>
<p>Branch, change something, commit with a real message, publish, open a pull request, merge it, delete the branch.</p>
<p>Two differences from class, and both are worth noticing:</p>
<p><strong>You are the whole team.</strong> In the class repo a classmate had to approve your pull request. Here nobody does, so you get to read your own change on the <strong>Files changed</strong> tab and merge it yourself. Read it anyway, properly, as though somebody else wrote it. That habit is the one this course is really trying to build.</p>
<p><strong>Nothing is protected.</strong> Your repo has no rule stopping you from committing straight to <code>main</code>. You could skip the branch entirely and it would work. Make the branch anyway. The habit has to survive the absence of a rule enforcing it, or it is not a habit.</p>
<p>If you want the reasoning behind any of it, rather than the buttons, that is in <a href="https://entr451.com/git-and-github/">Git and GitHub</a>.</p>
<h2 id="part-2-the-same-loop-from-the-terminal">Part 2: the same loop, from the terminal</h2>
<p>Now do it a second time without touching the file explorer. Everything below happens in the Codespace terminal, which you open with <strong>View &gt; Terminal</strong> from the hamburger menu.</p>
<p>This half exists because the command line is the part of the first class that fades fastest if you never touch it again. Ten minutes now is worth an hour later.</p>
<p>Start on a new branch. Bottom-left corner, <strong>Create new branch...</strong>, call it <code>terminal-practice</code>.</p>
<p><strong>Find out where you are.</strong></p>
<pre><code>pwd
</code></pre>
<p>It prints your current directory, which should end in <code>/hello-world</code>. Then:</p>
<pre><code>ls
</code></pre>
<p>lists what is in it. You should recognize the files from the Explorer. Now:</p>
<pre><code>ls -a
</code></pre>
<p>The same list, plus the hidden files, the ones whose names start with a dot. There are more files in a repository than the Explorer shows you by default, and <code>.devcontainer</code> is how your Codespace knew what to build.</p>
<p><strong>Run the programs that are already there.</strong></p>
<pre><code>ruby code/hello.rb
</code></pre>
<p>prints a greeting. Then:</p>
<pre><code>ruby code/infinite-tacos.rb
</code></pre>
<p>which does not stop on its own. Press <strong>Ctrl-C</strong> to kill it. You need this more often than you would think, and nobody tells you about it until you need it.</p>
<p><strong>Make something.</strong></p>
<pre><code>mkdir notes
</code></pre>
<p>creates a folder. Confirm it with <code>ls</code>. Then:</p>
<pre><code>touch notes/week-01.md
</code></pre>
<p>creates an empty file inside it. Open <code>notes/week-01.md</code> from the Explorer, write two or three lines about anything from the first class, and save with Ctrl-S.</p>
<p><strong>Move something.</strong></p>
<p>The repo has an <code>images</code> folder with a couple of files in it. Pick one and move it to the top level, then move it back:</p>
<pre><code>mv images/grogu.jpg .
ls
mv grogu.jpg images
ls
</code></pre>
<p>The <code>.</code> means &quot;here, the directory I am in.&quot; Watch the Explorer on the left as you run each one. The file jumps. You can of course do all of this by dragging with a mouse. The point is that you now know the other way, and the other way is the one that works over a terminal connection to a server at two in the morning.</p>
<p><strong>One useful trick each.</strong> Start typing a directory name and press <strong>Tab</strong>; the shell finishes it for you, and if nothing happens it is telling you that what you typed does not exist. Press the <strong>up arrow</strong> to bring back the last command you ran, which saves you retyping a long one with a typo in it.</p>
<p><strong>Then put it through the loop.</strong> Source Control, stage, commit with a message that says what you did, <strong>Publish Branch</strong>, open the pull request on github.com, read the diff, merge, delete the branch.</p>
<h2 id="before-you-close-the-tab">Before you close the tab</h2>
<p>Stop your Codespace. On the repo page, <strong>Code</strong> &gt; <strong>Codespaces</strong>, the three dots, <strong>Stop codespace</strong>. It stops itself after thirty minutes idle, but doing it yourself is the habit, and Codespaces hours come out of your own monthly allowance.</p>
<h2 id="what-to-check-before-you-call-it-done">What to check before you call it done</h2>
<ul>
<li>Your repository&apos;s <code>main</code> branch contains both changes.</li>
<li>The commit history shows how they got there, and the messages say what happened. If any of them says <code>update</code> or <code>tried again</code>, go and look at why that is useless to you two weeks from now.</li>
<li>Both branches are gone, because you deleted them after merging.</li>
<li>You can say, without looking anything up, what the difference is between a commit and a pull request.</li>
</ul>
<p>That last one is the actual assessment. The rest is just evidence.</p>
<h2 id="if-you-got-stuck">If you got stuck</h2>
<p>Post in <code>#fall-2026</code> on Slack and say where it stopped working. Getting stuck is not a failure mode here, it is the expected experience of week one, and the stuck point is usually the same three or four things. The troubleshooting list at the bottom of the class <code>roster</code> repo&apos;s README covers most of them.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Git and GitHub]]></title><description><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<p>This is one of two posts from the first week. This one is the tools: what Git and GitHub are, and how a change actually gets into a codebase. The other is <a href="https://entr451.com/open-source-and-licensing/">Open source and licensing, the longer version</a>, which is about deciding whose code you are willing to build</p></blockquote>]]></description><link>https://entr451.com/git-and-github/</link><guid isPermaLink="false">6ab2ce78d9019e19441c3242</guid><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Tue, 22 Sep 2026 18:55:43 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: markdown--><blockquote>
<p>This is one of two posts from the first week. This one is the tools: what Git and GitHub are, and how a change actually gets into a codebase. The other is <a href="https://entr451.com/open-source-and-licensing/">Open source and licensing, the longer version</a>, which is about deciding whose code you are willing to build on. Start here; that one uses the vocabulary this one sets up.</p>
</blockquote>
<h2 id="git-and-github-are-not-the-same-thing">Git and GitHub are not the same thing</h2>
<p>Nearly everyone trips over these two names, and it is worth thirty seconds of precision, because more decisions hang off the distinction than you would guess.</p>
<p><strong>Git is software.</strong> It is version control for source code: it stores your files, keeps their entire history, and lets several people work on the same project without overwriting each other. Linus Torvalds wrote it in 2005 to manage development of the Linux kernel. It is free and open source, it runs on your own machine (or, this quarter, in your Codespace), and no company owns it.</p>
<p><strong>GitHub is a company.</strong> Founded in 2008, bought by Microsoft in 2018 for around seven and a half billion dollars. Its business is hosting Git repositories and selling the things built around them: a web interface, permissions, issue tracking, code review tools, automation.</p>
<p>The relationship between the two names is exactly that, and nothing more. GitHub hosts Git.</p>
<p>If it helps: <strong>Git is email, GitHub is Gmail.</strong> Email is a protocol that nobody owns and that many companies provide. Gmail is one company&apos;s product for using it, with its own interface and its own features layered on top. You can leave Gmail and still have email.</p>
<p>Git is the thing. GitHub is a place to keep the thing.</p>
<h3 id="other-places-to-keep-the-thing">Other places to keep the thing</h3>
<p>GitHub is the largest by a wide margin, which is why this course uses it and why it is the one worth knowing. It is not the only one, and a few of these will turn up in your working life:</p>
<ul>
<li><strong>GitLab</strong>, the closest competitor, and the one most often chosen by companies that want to run the whole thing on their own servers.</li>
<li><strong>Bitbucket</strong>, from Atlassian, common in organizations already using Jira.</li>
<li><strong>Azure DevOps</strong>, also Microsoft, common in large enterprises.</li>
<li><strong>Self-hosted</strong>, using software like Gitea or Forgejo, or a plain Git server with no web interface at all. Git needs no company to function.</li>
</ul>
<h3 id="why-the-distinction-is-worth-money">Why the distinction is worth money</h3>
<p>Here is the part that matters when you are the one choosing.</p>
<p>Your project&apos;s entire history lives inside the repository itself, in a hidden folder that comes along whenever anyone copies it. Moving a project from one host to another is close to trivial: clone it, point it at the new host, push. Nobody holds your code hostage.</p>
<p>What does not move is everything the host added. Your issues, your pull requests and the discussions on them, your review history, your automation, your integrations. Those are GitHub&apos;s, in GitHub&apos;s shapes, and leaving means leaving them behind or paying somebody to convert them.</p>
<p>That asymmetry is the actual product. It is also the shape of most software-as-a-service lock-in you will ever evaluate: the thing you thought you were buying is portable, and the thing that accumulated around it is not. Worth recognizing the pattern here, where the stakes are low, so you recognize it later when they are not.</p>
<h2 id="what-git-actually-does">What Git actually does</h2>
<p>Think about version history in a document editor: you can see what changed, who changed it, and roll back to how it looked last Tuesday. Git is that, for code, with the history treated as the important part rather than a convenience.</p>
<p>Because Git watches every file in a project continuously, it knows the entire history of the work: what was added, modified and deleted, by whom, and when. That is what makes the rest possible.</p>
<ul>
<li><strong>Attribution.</strong> Who contributed what, and when.</li>
<li><strong>Time travel.</strong> View or return to the project as it stood at any earlier point.</li>
<li><strong>Branching.</strong> Several versions of the same project, running in parallel from a common starting point, which is how more than one person works on something at once without chaos.</li>
</ul>
<p>Every professional software team uses version control, and in practice almost all of them use Git. Once it arrived, the alternatives largely emptied out.</p>
<h2 id="repositories">Repositories</h2>
<p>A Git project lives in a <strong>repository</strong>, usually one repository per project. Hosting repositories is GitHub&apos;s main business.</p>
<p>A GitHub user or organization holds repositories. Microsoft&apos;s organization on GitHub holds a great many, one of which is the source for Visual Studio Code, the editor running in your Codespace. Your own account starts empty. That changes in the first class.</p>
<p>There are two ways to make one:</p>
<ul>
<li><strong>New repository</strong>, starting from nothing or from code already on your computer.</li>
<li><strong>Use this template</strong>, which copies an existing repository&apos;s files into a brand new repository of your own. This is the one we use most in this course, because it hands you a working starting point instead of an empty folder.</li>
</ul>
<p>To try the second one: go to <code>github.com/dsgn425-fall2026/hello-world</code>, click <strong>Use this template</strong>, name the new repository <code>hello-world</code>, leave it private, and create it. It lands in your own account in a few seconds, and it is yours.</p>
<p>Have a look around. A repository is a folder of files and folders, and not much more than that. You can add files and edit the ones already there, right in the browser.</p>
<h2 id="your-first-commit">Your first commit</h2>
<p>Open <code>README.md</code> and click the pencil icon. Change anything you like, then scroll to the bottom, to <strong>Commit changes</strong>.</p>
<p>The first box is the <strong>commit message</strong>. It is pre-filled with something generic like <code>Update README.md</code>, and you should replace it. The message is a note to your future self and to everybody else about what you did and why, and it is the single most durable piece of communication in a codebase. Write what changed, in plain words.</p>
<p>Click <strong>Commit changes</strong>. That takes the work you did, one coherent unit of it, and records it permanently in the repository&apos;s history.</p>
<p>A commit is a snapshot with a note attached. That is the whole idea, and everything else in Git is built out of it.</p>
<p>Committing straight onto the project the way we just did is fine when the project is yours and nobody else is touching it. It stops being fine immediately after that, and the rest of this post is what teams do instead.</p>
<h2 id="branches">Branches</h2>
<p>A repository has a main line of development, called <code>main</code>. Treat it as the official version: the one that works, the one that ships, the one everybody else pulls from.</p>
<p>A <strong>branch</strong> is a separate line of work that starts from a copy of <code>main</code> at a moment in time. You make one, you do your work on it, and <code>main</code> is untouched the entire time. When the work is done and somebody has looked at it, the branch gets folded back in.</p>
<p>The reason this matters is not tidiness. It is that real work is unfinished for most of its life. Halfway through a change, the code is usually broken, and if you were working directly on <code>main</code> then everybody else is now working on top of your broken half-change. A branch is where something is allowed to be incomplete without hurting anyone.</p>
<p>The second reason is that several people can do this at once. Five people on five branches are not in each other&apos;s way, and the question of how their work fits together gets asked once, deliberately, when each branch comes back, instead of continuously and by accident.</p>
<p>Two practical notes. Name the branch for what you are doing, not for yourself: <code>add-search</code> tells a colleague something, <code>bens-branch</code> does not. And do not hoard a branch. The longer it sits away from <code>main</code>, the more <code>main</code> moves underneath it and the harder the eventual reconciliation gets. Branches are cheap and meant to be short-lived, which is the opposite of<br>
the instinct most people arrive with.</p>
<h2 id="pull-requests">Pull requests</h2>
<p>Here is a distinction worth holding onto, because it follows the Git and GitHub split from the top of this post: <strong>branches and commits are Git. Pull requests are not.</strong></p>
<p>A pull request is a GitHub feature, built on top of Git. It is a request, addressed to the people who own the project, saying: I did this work on my branch, please pull it into <code>main</code>. GitLab calls the same thing a merge request, which is arguably the better name.</p>
<p>It is three things at once, and the third is the one people miss.</p>
<ol>
<li><strong>A proposal.</strong> Nothing has happened yet. <code>main</code> is unchanged until somebody merges.</li>
<li><strong>A place to talk.</strong> The pull request shows the diff: every line added, removed or changed. Anyone can comment on the whole thing, or on one specific line. Conversation happens against the actual change rather than in a meeting about the change.</li>
<li><strong>A record.</strong> It survives. In two years, when somebody asks why a thing works the way it does, the pull request is where the answer is: what changed, who asked for it, what the objections were, and who decided. Most of the institutional memory of a codebase lives in old pull requests.</li>
</ol>
<p>On any serious team, <code>main</code> is protected, which means nobody can push to it directly, not even the person who owns the company. The pull request is not the polite option. It is the only door.</p>
<h2 id="code-review">Code review</h2>
<p>Somebody other than the author has to look at the change and say yes. That is code review, and it is the part of this that will follow you furthest, because you may well end up on the approving side of it without ever writing the code yourself.</p>
<p>A reviewer opens the <strong>Files changed</strong> tab and answers three questions:</p>
<ol>
<li>Does the change do what the pull request says it does?</li>
<li>Does the commit message tell you what changed without opening the diff?</li>
<li>Would you merge this?</li>
</ol>
<p>Then they leave at least one comment on a line, and submit the review as either <strong>Approve</strong> or <strong>Request changes</strong>. If they requested changes, the author fixes it on the same branch and commits again, the pull request updates itself, and the reviewer looks again. That loop is the job.</p>
<p>Notice what is not on that list. Nothing about whether the code is clever, and nothing that requires you to be able to have written it yourself. Review is mostly about intent: does this do what it claims, would somebody understand it later, and am I willing to put my name on letting it in. Those are judgment questions, and they are the same questions you would ask about anything else your team proposes to ship.</p>
<p>The checklist grows as the course goes on. The three questions do not change.</p>
<p>One rule that is worth understanding rather than memorizing: GitHub will not let you approve your own pull request. Nobody reviews their own work. That is not a technicality, it is the whole point of the mechanism.</p>
<h2 id="the-loop-in-one-place">The loop, in one place</h2>
<p>Every change to every serious codebase goes through some version of this.</p>
<ol>
<li><strong>Branch.</strong> Off <code>main</code>, named for what you are about to do.</li>
<li><strong>Change.</strong> On the branch, where being half-finished is safe.</li>
<li><strong>Commit.</strong> One coherent unit of work, with a message that says what it was.</li>
<li><strong>Publish.</strong> The branch goes up to GitHub.</li>
<li><strong>Pull request.</strong> The proposal to change <code>main</code>.</li>
<li><strong>Review.</strong> Somebody else reads it and says yes, or says not yet.</li>
<li><strong>Merge.</strong> It lands on <code>main</code>.</li>
<li><strong>Delete the branch.</strong> It served its purpose.</li>
</ol>
<p>You will run that loop by hand a few times in this course until it is boring. That is the intended outcome. The mechanics are meant to become automatic so that your attention can go to the only part that actually needs a person: step 6.</p>
<h2 id="where-this-goes-next">Where this goes next</h2>
<ul>
<li><a href="https://entr451.com/practice-github/">Practice: GitHub</a> to run the whole loop once on your own, with nobody else&apos;s repository at risk.</li>
<li><a href="https://entr451.com/open-source-and-licensing/">Open source and licensing, the longer version</a>, if you came here from the class and skipped it.</li>
<li><a href="https://entr451.com/command-line-basics-and-the-development-environment/">Command-line basics and the development environment</a> for what comes after the browser.</li>
</ul>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Deployment with Render]]></title><description><![CDATA[<p>Here are detailed instructions on how to get your application hosted in a production-quality environment!</p><h3 id="preparing-your-application">PREPARING YOUR APPLICATION</h3><p>In your <code>Gemfile</code>, it may already look like it does below (specifically with the <code>sqlite3</code> gem nested inside a <code>group :development</code> block and a <code>pg</code> gem nested inside a <code>group :production</code> block)</p>]]></description><link>https://entr451.com/deployment-with-render/</link><guid isPermaLink="false">65e8a415d9019e19441c2d57</guid><category><![CDATA[Ship It]]></category><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Wed, 06 Mar 2024 19:33:10 GMT</pubDate><content:encoded><![CDATA[<p>Here are detailed instructions on how to get your application hosted in a production-quality environment!</p><h3 id="preparing-your-application">PREPARING YOUR APPLICATION</h3><p>In your <code>Gemfile</code>, it may already look like it does below (specifically with the <code>sqlite3</code> gem nested inside a <code>group :development</code> block and a <code>pg</code> gem nested inside a <code>group :production</code> block). &#xA0;If not, delete <code>gem &quot;sqlite3&quot;, &quot;~&gt; 1.4&quot;</code> and add the following:</p><pre><code>group :development, :test do
  gem &quot;sqlite3&quot;, &quot;~&gt; 1.4&quot;
  # note: keep other gems that were already in this group (e.g. &quot;debug&quot;)
end

group :production do
  gem &apos;pg&apos;
end</code></pre><p><em>Note: there was likely already a </em><code>group :development, :test</code><em> block in your code. If so, keep that code and just add the </em><code>sqlite3</code><em> gem to that group. &#xA0;Don&apos;t delete any other gems in the group. &#xA0;For example, your code may now look like this:</em></p><pre><code>group :development, :test do
  gem &quot;debug&quot;, platforms: %i[ mri mingw x64_mingw ]
  gem &quot;sqlite3&quot;, &quot;~&gt; 1.4&quot;
end</code></pre><p>Once you&apos;ve moved the <code>sqlite3</code> gem and added the <code>pg</code> gem, execute the change in your <code>Gemfile</code> by running:</p><pre><code>bundle install</code></pre><p>These changes tell your Rails application to use the industrial-strength PostgreSQL database in production, instead of the sqlite3 database we&apos;ve been using for development. &#xA0;The database will behave the same for our purposes, but is ready for production-level traffic and future scalability.</p><p>Next, you&apos;ll need to update the <code>config/database.yml</code> file. &#xA0;With the <code>pg</code> library installed for production, we also need to configure the app so that it knows where to find the production database. &#xA0;Currently, it lives in a file in the <code>db</code> directory (<code>db/development.sqlite3</code>), but in production the database is managed separately.</p><p>If it&apos;s not already there, add the following to the config file:</p><pre><code>production:
  &lt;&lt;: *default
  adapter: postgresql
  database: my_app_production
  username: my_app
  password: &lt;%= ENV[&quot;MY_APP_DATABASE_PASSWORD&quot;] %&gt;</code></pre><p>Replace <code>my_app</code> in the example with the name of your app - in this case <code>tacogram_final</code> would be appropriate. &#xA0;Be sure that the database value still has <code>production</code> appended to it. &#xA0;And <code>MY_APP</code> in the <code>password</code> should be <code>TACOGRAM_FINAL</code> in all caps. &#xA0;As an example, it might look like this:</p><pre><code>production:
  &lt;&lt;: *default
  adapter: postgresql
  database: tacogram_final_production
  username: tacogram_final
  password: &lt;%= ENV[&quot;TACOGRAM_FINAL_DATABASE_PASSWORD&quot;] %&gt;</code></pre><p>Last step is to commit these changes and push them to Github.</p><h3 id="render-setup">RENDER SETUP</h3><p>Sign up for a Render account at <a href="http://render.com/">render.com</a> - use your Github credentials if possible. &#xA0;You may have to enter payment info, but you&apos;ll be using the free tier. &#xA0;During signup, you&apos;ll be asked several questions about how you will use Render - you can answer them however you&apos;d like. &#xA0;Once complete, you&apos;ll be on your Render dashboard.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2024/03/render-dashboard.png" class="kg-image" alt loading="lazy" width="2000" height="1248" srcset="https://entr451.com/content/images/size/w600/2024/03/render-dashboard.png 600w, https://entr451.com/content/images/size/w1000/2024/03/render-dashboard.png 1000w, https://entr451.com/content/images/size/w1600/2024/03/render-dashboard.png 1600w, https://entr451.com/content/images/size/w2400/2024/03/render-dashboard.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>You&apos;ll need to create 2 services - one for the database and one for the application. &#xA0; Create the database service first. &#xA0;Click &quot;Create PostgreSQL&quot; on your dashboard (or using the &quot;New +&quot; button in the top right).</p><p>In the form, enter a unique name for your new database - it&apos;s helpful to use your application name followed by &quot;-db&quot; to easily identify it later. &#xA0;For example, it might be <code>tacogram-final-db</code>.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2024/03/Screenshot-2024-03-06-at-12.42.33-PM.png" class="kg-image" alt loading="lazy" width="2000" height="254" srcset="https://entr451.com/content/images/size/w600/2024/03/Screenshot-2024-03-06-at-12.42.33-PM.png 600w, https://entr451.com/content/images/size/w1000/2024/03/Screenshot-2024-03-06-at-12.42.33-PM.png 1000w, https://entr451.com/content/images/size/w1600/2024/03/Screenshot-2024-03-06-at-12.42.33-PM.png 1600w, https://entr451.com/content/images/size/w2400/2024/03/Screenshot-2024-03-06-at-12.42.33-PM.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Ignore the other settings in the form until the Instance Type at the bottom. &#xA0;Select the &quot;Free&quot; type and click &quot;Create Database&quot;. &#xA0;Note: Render only permits 1 free database service. &#xA0;When you want to create a database for another application, you&apos;ll first need to suspend/delete this one (or upgrade to a paid plan).</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2024/03/Screenshot-2024-03-06-at-12.42.44-PM.png" class="kg-image" alt loading="lazy" width="2000" height="1123" srcset="https://entr451.com/content/images/size/w600/2024/03/Screenshot-2024-03-06-at-12.42.44-PM.png 600w, https://entr451.com/content/images/size/w1000/2024/03/Screenshot-2024-03-06-at-12.42.44-PM.png 1000w, https://entr451.com/content/images/size/w1600/2024/03/Screenshot-2024-03-06-at-12.42.44-PM.png 1600w, https://entr451.com/content/images/size/w2400/2024/03/Screenshot-2024-03-06-at-12.42.44-PM.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>On the next screen, wait for the database service to complete its setup. &#xA0;Once it&apos;s available, scroll to the &quot;Connections&quot; section, find the &quot;Internal Database URL&quot; value and copy it - you&apos;ll need it in the next step.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2024/03/Screenshot-2024-03-06-at-12.48.45-PM.png" class="kg-image" alt loading="lazy" width="2000" height="1242" srcset="https://entr451.com/content/images/size/w600/2024/03/Screenshot-2024-03-06-at-12.48.45-PM.png 600w, https://entr451.com/content/images/size/w1000/2024/03/Screenshot-2024-03-06-at-12.48.45-PM.png 1000w, https://entr451.com/content/images/size/w1600/2024/03/Screenshot-2024-03-06-at-12.48.45-PM.png 1600w, https://entr451.com/content/images/2024/03/Screenshot-2024-03-06-at-12.48.45-PM.png 2112w" sizes="(min-width: 720px) 720px"></figure><p>Next, go back to your dashboard and click the &quot;New +&quot; button in the top right to create a new Web Service.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2024/03/Screenshot-2024-03-06-at-12.49.49-PM.png" class="kg-image" alt loading="lazy" width="2000" height="689" srcset="https://entr451.com/content/images/size/w600/2024/03/Screenshot-2024-03-06-at-12.49.49-PM.png 600w, https://entr451.com/content/images/size/w1000/2024/03/Screenshot-2024-03-06-at-12.49.49-PM.png 1000w, https://entr451.com/content/images/size/w1600/2024/03/Screenshot-2024-03-06-at-12.49.49-PM.png 1600w, https://entr451.com/content/images/size/w2400/2024/03/Screenshot-2024-03-06-at-12.49.49-PM.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>When asked &quot;How would you like to deploy your web service?&quot;, choose &quot;Build and deploy from a Git repository&quot;. &#xA0;On the next screen, you&apos;ll be asked to &quot;Connect a repository&quot; - you should see your Github repository listed, but if not, click &quot;Configure account&quot; on the right to authorize access to your Github account. &#xA0;Once you&apos;ve found your application&apos;s repository in the list, click &quot;Connect&quot;.</p><p>In the following form, the &quot;Name&quot; field will be pre-populated for you based on the name of your application. &#xA0;However, this must be unique across all of Render, so you may need to change the name slightly (e.g. <code>tacogram-final</code> may already be taken). &#xA0;You can leave the next few settings unchanged. &#xA0;Then, in the &quot;Build Command&quot; field, enter the 2 commands that should run each time you deploy a new version of your application: <code>bundle install; rails db:migrate</code> (the semi-colon separates the 2 commands).</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2024/03/Screenshot-2024-03-06-at-12.56.01-PM.png" class="kg-image" alt loading="lazy" width="2000" height="1357" srcset="https://entr451.com/content/images/size/w600/2024/03/Screenshot-2024-03-06-at-12.56.01-PM.png 600w, https://entr451.com/content/images/size/w1000/2024/03/Screenshot-2024-03-06-at-12.56.01-PM.png 1000w, https://entr451.com/content/images/size/w1600/2024/03/Screenshot-2024-03-06-at-12.56.01-PM.png 1600w, https://entr451.com/content/images/size/w2400/2024/03/Screenshot-2024-03-06-at-12.56.01-PM.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Next, change the &quot;Start Command&quot; field. &#xA0;This is the command that will run to start your application server, just like you do in Codespace: <code>rails server</code></p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2024/03/Screenshot-2024-03-06-at-12.58.29-PM.png" class="kg-image" alt loading="lazy" width="2000" height="280" srcset="https://entr451.com/content/images/size/w600/2024/03/Screenshot-2024-03-06-at-12.58.29-PM.png 600w, https://entr451.com/content/images/size/w1000/2024/03/Screenshot-2024-03-06-at-12.58.29-PM.png 1000w, https://entr451.com/content/images/size/w1600/2024/03/Screenshot-2024-03-06-at-12.58.29-PM.png 1600w, https://entr451.com/content/images/size/w2400/2024/03/Screenshot-2024-03-06-at-12.58.29-PM.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Then, choose the free Instance Type option.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2024/03/Screenshot-2024-03-06-at-1.00.05-PM.png" class="kg-image" alt loading="lazy" width="2000" height="943" srcset="https://entr451.com/content/images/size/w600/2024/03/Screenshot-2024-03-06-at-1.00.05-PM.png 600w, https://entr451.com/content/images/size/w1000/2024/03/Screenshot-2024-03-06-at-1.00.05-PM.png 1000w, https://entr451.com/content/images/size/w1600/2024/03/Screenshot-2024-03-06-at-1.00.05-PM.png 1600w, https://entr451.com/content/images/size/w2400/2024/03/Screenshot-2024-03-06-at-1.00.05-PM.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>The last change is adding environment variables needed to run the application - these are variables stored on the &quot;server&quot; instead of in our code and generally used to run the application or for sensitive data we don&apos;t want to store in our code (e.g. API keys). &#xA0;The variables you&apos;ll need to add are:</p><ul><li>DATABASE_URL</li><li>SECRET_KEY_BASE</li><li>WEB_CONCURRENCY</li></ul><p>The <code>DATABASE_URL</code> value should be the &quot;Internal Database URL&quot; value copied from the database service. &#xA0;The <code>SECRET_KEY_BASE</code> value can be anything - your application name will work. &#xA0;And, the <code>WEB_CONCURRENCY</code> value should be <code>2</code>.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2024/03/Screenshot-2024-03-06-at-1.02.09-PM.png" class="kg-image" alt loading="lazy" width="2000" height="368" srcset="https://entr451.com/content/images/size/w600/2024/03/Screenshot-2024-03-06-at-1.02.09-PM.png 600w, https://entr451.com/content/images/size/w1000/2024/03/Screenshot-2024-03-06-at-1.02.09-PM.png 1000w, https://entr451.com/content/images/size/w1600/2024/03/Screenshot-2024-03-06-at-1.02.09-PM.png 1600w, https://entr451.com/content/images/size/w2400/2024/03/Screenshot-2024-03-06-at-1.02.09-PM.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>That&apos;s it. &#xA0;You can click &quot;Create Web Service&quot; to complete the setup. &#xA0;Render will pull your latest code from Github and begin to install all of the necessary libraries. &#xA0;It will automatically run <code>bundle install</code> and <code>rails db:migrate</code>. &#xA0;And then lastly, it will run <code>rails server</code>. &#xA0;If all goes well, you&apos;ll see &quot;Your service is live &#x1F389;&quot; in the logs.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2024/03/Screenshot-2024-03-06-at-1.11.50-PM.png" class="kg-image" alt loading="lazy" width="2000" height="1349" srcset="https://entr451.com/content/images/size/w600/2024/03/Screenshot-2024-03-06-at-1.11.50-PM.png 600w, https://entr451.com/content/images/size/w1000/2024/03/Screenshot-2024-03-06-at-1.11.50-PM.png 1000w, https://entr451.com/content/images/size/w1600/2024/03/Screenshot-2024-03-06-at-1.11.50-PM.png 1600w, https://entr451.com/content/images/size/w2400/2024/03/Screenshot-2024-03-06-at-1.11.50-PM.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>From now on, each time you push new code from Codespace to Github, it will be deployed to Render as well. &#xA0;You can see any deployment events listed in the &quot;Events&quot; tab.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2024/03/Screenshot-2024-03-06-at-1.19.25-PM.png" class="kg-image" alt loading="lazy" width="2000" height="1116" srcset="https://entr451.com/content/images/size/w600/2024/03/Screenshot-2024-03-06-at-1.19.25-PM.png 600w, https://entr451.com/content/images/size/w1000/2024/03/Screenshot-2024-03-06-at-1.19.25-PM.png 1000w, https://entr451.com/content/images/size/w1600/2024/03/Screenshot-2024-03-06-at-1.19.25-PM.png 1600w, https://entr451.com/content/images/size/w2400/2024/03/Screenshot-2024-03-06-at-1.19.25-PM.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Congratulations! &#xA0;Your app should now be live! &#xA0;There&apos;s a link to visit your live production site towards the top. &#xA0;Note the url for future reference - it will be something like <code>my_app.onrender.com</code> where &quot;my_app&quot; is the unique name you entered previously (e.g. <code>tacogram-final-tues</code>).</p><h3 id="debugging">DEBUGGING</h3><p>Render is really just a big, industrial Linux-based computer &#x2013; just like Codepsace, but more powerful. &#xA0;It&apos;s worth exploring the features of your Render services to discover what&apos;s possible. &#xA0;One feature that&apos;s very useful is access to the logs (see the &quot;Logs&quot; tab just below the &quot;Events&quot; tab in Render. &#xA0;As users interact with your application, you will see the output to the server log there. &#xA0;Just as you did with the rails server log in Codespace, reading these logs will be helpful when you inevitably find a bug in the live app. &#xA0;You won&apos;t see the usual error message that you&apos;ve seen in local development - that would be a bad user experience. &#xA0;Instead you&apos;ll see a simple 404 error screen with no details about what went wrong. &#xA0;</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/06/Screen-Shot-2022-06-02-at-12.01.20-AM.png" class="kg-image" alt loading="lazy" width="1856" height="534" srcset="https://entr451.com/content/images/size/w600/2022/06/Screen-Shot-2022-06-02-at-12.01.20-AM.png 600w, https://entr451.com/content/images/size/w1000/2022/06/Screen-Shot-2022-06-02-at-12.01.20-AM.png 1000w, https://entr451.com/content/images/size/w1600/2022/06/Screen-Shot-2022-06-02-at-12.01.20-AM.png 1600w, https://entr451.com/content/images/2022/06/Screen-Shot-2022-06-02-at-12.01.20-AM.png 1856w" sizes="(min-width: 720px) 720px"></figure><p>To see the actual error message, you&apos;ll need to look at the server log. &#xA0;Render makes this especially easy by providing search and filter functionality for your logs.</p><h3 id="custom-domains">CUSTOM DOMAINS</h3><p>If you&apos;re interested in adding a custom domain to your application (e.g. <code>tacogram-final.com</code> instead of <code>tacogram-final.onrender.com</code>), you&apos;ll first need to purchase a domain (or use a domain you already own) at a domain registry provider like <a href="https://www.namecheap.com/">Namecheap</a>. &#xA0;Then follow Render&apos;s <a href="https://docs.render.com/custom-domains">instructions</a> to configure the domain to point to your server on Render.</p>]]></content:encoded></item><item><title><![CDATA[Keep in touch!]]></title><description><![CDATA[<!--kg-card-begin: html--><script src="https://static.airtable.com/js/embed/embed_snippet_v1.js"></script><iframe class="airtable-embed airtable-dynamic-height" src="https://airtable.com/embed/shrVngcjYSqrRssAj?backgroundColor=purple" frameborder="0" onmousewheel width="100%" height="780" style="background: transparent; border: 1px solid #ccc;"></iframe><!--kg-card-end: html-->]]></description><link>https://entr451.com/keep-in-touch/</link><guid isPermaLink="false">646d17f6d9019e19441c2ccc</guid><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Tue, 23 May 2023 19:51:57 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: html--><script src="https://static.airtable.com/js/embed/embed_snippet_v1.js"></script><iframe class="airtable-embed airtable-dynamic-height" src="https://airtable.com/embed/shrVngcjYSqrRssAj?backgroundColor=purple" frameborder="0" onmousewheel width="100%" height="780" style="background: transparent; border: 1px solid #ccc;"></iframe><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Intro to Domain Modeling]]></title><description><![CDATA[<p>In the <a href="https://entr451.com/tag/sql/">last lesson</a>, we learned about SQL &#x2013;&#xA0;its syntax and how the language is used to CRUD data as well as manipulate the design of database tables. When designing database tables from a blank slate, though, how do we get started? What decisions influence what tables and</p>]]></description><link>https://entr451.com/intro-to-domain-modeling/</link><guid isPermaLink="false">617730f9d9019e19441bba08</guid><category><![CDATA[Domain Modeling]]></category><dc:creator><![CDATA[Brian Eng]]></dc:creator><pubDate>Mon, 16 Jan 2023 16:00:00 GMT</pubDate><content:encoded><![CDATA[<p>In the <a href="https://entr451.com/tag/sql/">last lesson</a>, we learned about SQL &#x2013;&#xA0;its syntax and how the language is used to CRUD data as well as manipulate the design of database tables. When designing database tables from a blank slate, though, how do we get started? What decisions influence what tables and columns will be created? How do we balance a database design that is simple and easy-to-understand, versus one that is most performant? The approach to answering these questions and more is known as <em>domain modeling</em>.</p><h3 id="its-your-world">It&apos;s Your World</h3><p>The actual, physical world that is all around us is composed of many, many, billions of objects &#x2013;&#xA0;living or not &#x2013; each with their own behavior, attributes, and idiosyncrasies. It&apos;s the job of scientists and domain experts to study these objects and the ways in which they interact with each other, and to develop theories and laws to describe their observations, so that we can better understand the way the world works. It&apos;s all very complicated.</p><p>The world of the software we create is not nearly this complex. Everything that exists in this world is entirely up to us. We define what objects live in this world, their attributes and behaviors, and their relationships with one another. The result is a <em>domain model</em> of our own creation, and it can be as simple or as complex we need it to be.</p><p>From the <a href="https://en.wikipedia.org/wiki/Domain_model">Wikipedia article on domain modeling</a>:</p><blockquote>A domain model is a system of abstractions that describes selected aspects of a sphere of knowledge, influence or activity (a domain). The model can then be used to solve problems related to that domain. The domain model is a representation of meaningful real-world concepts pertinent to the domain that need to be modeled in software. The concepts include the data involved in the business and rules the business uses in relation to that data. A domain model leverages natural language of the domain.</blockquote><blockquote>A domain model generally uses the vocabulary of the domain, thus allowing a representation of the model to be communicated to non-technical stakeholders.</blockquote><p><em>Oof. </em>That&apos;s a lot of words. Let&apos;s TL;DR this thing. A domain model:</p><ul><li>Is a real-world concept (aka domain) represented as software</li><li>Contains only the data and rules involved in the domain</li><li>Is described using the actual words (i.e. vocabulary) used by those working in the domain</li></ul><p>It&apos;s important to note that a domain model doesn&apos;t need to include <strong>everything </strong>in the domain &#x2013; only the pertinent bits we need <strong>right now</strong> in order to build our system. We can always add more later &#x2013;&#xA0;it&apos;s just a <code>CREATE TABLE</code> statement, right? But how do we determine what is needed <strong>right now</strong>? Let&apos;s take a look at this through the lens of a startup.</p><h3 id="minimum-viable-product-mvp">Minimum Viable Product (MVP)</h3><p>Popularized in the early 2000s, most notably by Eric Ries in his book <em>The Lean Startup</em>, the Minimum Viable Product (MVP) approach to building software products has become the de-facto standard in the startup community. There is plenty of in-depth reading and research we can do on the topic, both in-print and online; we&apos;re going to give you the highlight reel here.</p><p>In short, building an MVP means that, when building a new software product, we build only the features necessary (minimum) in order to get feedback from real users (viable). Build any more than those core features, and you&apos;re just wasting time and money building something that customers/users probably don&apos;t want anyway.</p><p>How do we begin to enumerate what those core features are, then? To get things started, it can be useful to create <em>user stories</em>.</p><h3 id="user-stories">User Stories</h3><p>Our product&apos;s <em>user stories </em>is a written document that, in a regimented way, describes the core features in a <u>user-centric</u> style. <em>How does a user of your system deal with their problem, perform their task, or otherwise receive value by interacting with it?</em></p><p>Let&apos;s begin by looking at the typical template for writing a user story:</p><p><em><strong>As a [some user role], I want to [some goal], so I can [some value]</strong></em></p><p>Pretty vague, right? Don&apos;t worry, it will make more sense once we look at a practical example.</p><p>Let&apos;s continue with the idea of a &quot;school&quot; system that we started building the database for in the last lesson. We&apos;ll begin with a top-level, overall vision for the system. Perhaps this is something that&apos;s written down, articulated in some way, or just something we&apos;re thinking about:</p><p><em>This will be a system used to manage our school. A student in our school can use the system to browse the available courses and enroll in them, based on the dates/times that best fit their schedule. Students are added to the system by teachers. So are the courses they teach.</em></p><p>From there, we can begin the process of transforming that general vision into the user story format. Let&apos;s look at a single story, one that we could argue is truly the core purpose of the system:</p><p><em>As a <strong>student</strong>, I want to <strong>browse available courses, seeing the course description and bio of the instructor</strong>, so I can <strong>decide if I want to enroll in it.</strong></em></p><p>By going through this process, we&apos;ve immediately transformed the somewhat loose, general vision into a concise story that clearly articulates what that part of the system is supposed to do from a user perspective, and the value it creates for the user. We can also see that we&apos;re using the language that&apos;s specific to this domain, that is, we&apos;re describing things in the vocabulary that would be typically used by the people using this system.</p><p><strong>If you like, you can stop reading here, and see if you can come up with the rest of the stories. </strong></p><hr><p>Here&apos;s the list that we came up with:</p><ul><li><em>As a <strong>student</strong>, I want to <strong>browse available courses, seeing the course description and bio of the instructor</strong>, so I can <strong>decide if I want to enroll in it.</strong></em></li><li><em>As a <strong>student</strong>, I want to <strong>see the available sections (dates and times offered) and enroll in the section of my choosing</strong>, so that <strong>I can enroll in the course I want at the time that best works for my schedule.</strong></em></li><li><em>As a <strong>teacher</strong>, I want to <strong>be able to maintain (add, edit, and remove) a list of students at our school</strong>, so that <strong>we know who our students are, their contact information (email and phone number), and provide access to enroll in classes.</strong></em></li><li><em>As a <strong>teacher</strong>, I want to <strong>be able to maintain (add, edit, and remove) a list of teachers (including myself) at our school</strong>, so that <strong>we can manage teachers&apos; bios and a list of classes they teach</strong>.</em></li><li><em>As a <strong>teacher</strong>, I want to <strong>be able to maintain (add, edit, and remove) a list of courses and the sections of those courses</strong>, so that <strong>we can publish it for students to browse and enroll.</strong></em></li></ul><p>In the end, there may be more stories, or a need to make certain stories more specific, but this is certainly a good list to start. Again, all we&apos;ve done is taken a rather general vision for our overall product and its purpose, and used the user story template to help us think about what the pieces are, and how to articulate them in a way that illuminates the value of each part of the system. </p><p>After getting these stories down on paper, it&apos;s time to evaluate. If we have too many stories, this process may help us decide how to shave the list down to only the parts of the system needed to create value and receive feedback. If we have too few stories, this helps us learn what we&apos;d need to add to make this a valuable system for our potential users.</p><p><strong>It&apos;s important to note that user stories are not meant to be some sort of formal document that gets etched</strong> in stone; rather, it&apos;s more of an iterative process that help us to continually think about what the system is really supposed to do and the value it creates.</p><h3 id="wireframing">Wireframing</h3><p>Wireframes are another great tool for iterating on a system&apos;s design. Once we have our user stories on paper, we can then create a visual representation of the user&apos;s experience, i.e. <em>what will the system look like to the user?</em></p><p>We can use practically any tools we&apos;re comfortable with to draw our wireframes. These tools might include:</p><ul><li>Pencil and paper</li><li>A presentation tool like Powerpoint or Keynote</li><li>Graphics/design apps like Figma, Sketch, Adobe XD or Photoshop</li><li>Wireframing tools and services like Balsamiq Mockups, Justinmind, or wireframe.cc</li><li>If you already know it, HTML and CSS</li></ul><p>The tool doesn&apos;t matter. The goal is to transform our user stories into a visual of what the finished MVP might look like, so we can discover more detail than we knew before, or to iterate on what the needed functionality might be. It&apos;s very common to return to the user stories and make changes after wireframing.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2021/12/image.png" class="kg-image" alt loading="lazy" width="1030" height="813" srcset="https://entr451.com/content/images/size/w600/2021/12/image.png 600w, https://entr451.com/content/images/size/w1000/2021/12/image.png 1000w, https://entr451.com/content/images/2021/12/image.png 1030w" sizes="(min-width: 720px) 720px"></figure><p>Here&apos;s an example of a wireframe for our school enrollment app, created using Balsamiq. For the purposes of domain modeling, there are no specifications or formal process for wireframing; it&apos;s really all about helping to visualize what we&apos;ve already articulated using our user stories.</p><h3 id="models">Models</h3><p>User stories and wireframes is a way to think about and discover all the entities in your world, as well as the relationships between them. We&apos;ll now shift that thinking to building our database. <strong>From now on, we&apos;ll call each real-world entity a <em>model. </em>And all of our models, together with all of their relationships, is called our <em>domain model</em>.</strong></p><p>What are the real, tangible <em>models</em> we&apos;re dealing with in our school system? That is, things that really exist in the real world, outside the realm of software? From our user stories and wireframes, a couple of obvious ones might be:</p><ul><li>Student</li><li>Teacher</li></ul><p>Students and teachers do not exist in theory. They exist in the real, physical world. Students are the people who attend our school. Teachers are people who teach the courses.</p><p>But you may be wondering, why we don&apos;t we have a single model for <em>People</em>? After all, a student and a teacher are both people. This is a valid question and one that is quite nuanced in nature. It&apos;s because models are distinguished by their unique <em>attributes</em>. <strong>According to our user stories/wireframes</strong>, teachers have bios. Students do not. Students have contact information (email/phone number). Teachers do not. We might decide later that students need a bio, too &#x2013; at which point, we&apos;d go back and iterate on our user stories. But not right now.</p><p>Let&apos;s try to identify the <em>attributes</em> &#x2013;&#xA0;the data needed for &#x2013; these two models. </p><ul><li>Student &#x2013; name (first/last), email, and phone number</li><li>Teacher &#x2013; name (first/last), bio</li></ul><h3 id="database-backed-models">Database-Backed Models</h3><p>If you&apos;ve put 2+2 together, based on our last lesson on SQL, a model and its attributes can be neatly represented in a <em>database table</em>.</p><ul><li>Each column represents a particular model <em>attribute</em></li><li>Each row represents a unique model <em>instance</em></li></ul><p>Each model will need a different table, because the column definitions will be different for every model. The start of our domain model, along with some sample data, might look like this:</p><!--kg-card-begin: markdown--><p><strong><em>students</em></strong></p>
<hr>
<table>
<thead>
<tr>
<th>id</th>
<th>first_name</th>
<th>last_name</th>
<th>email</th>
<th>phone_number</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Jane</td>
<td>Doe</td>
<td><a href="mailto:jane@example.com">jane@example.com</a></td>
<td>555-1212</td>
</tr>
<tr>
<td>2</td>
<td>Jenny</td>
<td>Smith</td>
<td><a href="mailto:jenny@gmail.com">jenny@gmail.com</a></td>
<td>867-5309</td>
</tr>
<tr>
<td>3</td>
<td>John</td>
<td>Johnson</td>
<td><a href="mailto:john@acme.com">john@acme.com</a></td>
<td>456-7890</td>
</tr>
</tbody>
</table>
<p><strong><em>teachers</em></strong></p>
<hr>
<table>
<thead>
<tr>
<th>id</th>
<th>first_name</th>
<th>last_name</th>
<th>bio</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Ben</td>
<td>Block</td>
<td>Often talks to a rubber ducky.</td>
</tr>
<tr>
<td>2</td>
<td>Brian</td>
<td>Eng</td>
<td>Loves tacos.</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>A decent start. Let&apos;s add the model for a <em>Course</em>. A course, while not necessarily a real, physical thing like a student or a teacher, is an entity that really exists that we need to retain data for &#x2013;&#xA0;that is, it has <em>attributes. </em>We can also create <em>instances</em> of it, i.e. each row in the database table represents the data for each available course. Let&apos;s try and determine the <em>attributes</em> of a <em>course.</em> What data would we need to keep track of for every course, according to our user stories and wireframes?</p><ul><li>A course name</li><li>A course description</li></ul><p>And that&apos;s all for now:</p><!--kg-card-begin: markdown--><p><strong><em>courses</em></strong></p>
<hr>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>description</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Introduction to Software Development</td>
<td>The course is focused on software development...</td>
</tr>
<tr>
<td>2</td>
<td>Taco-Making 101</td>
<td>In this course, you&#x2019;ll learn how to build a proper taco...</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><h3 id="relationships">Relationships</h3><p>At this point, you might be asking yourself... wait, is that all the data needed for a course? What about the sections &#x2013;&#xA0;the dates and times the course is available? Or the teachers who teach the course? </p><p>Knowing the difference between what belongs as an <em>attribute</em> on a model and what should be separated into its own model... is one of the keys to being good at domain modeling.</p><p>Is it possible to add the dates/times and teacher of each course to the <em>Course</em> model? Sure, let&apos;s see what that would look like:</p><!--kg-card-begin: markdown--><p><strong><em>courses</em></strong></p>
<hr>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>description</th>
<th>time_1</th>
<th>teacher_id_1</th>
<th>time_2</th>
<th>teacher_id_2</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Introduction to Software Development</td>
<td>The course is focused on software development...</td>
<td>Tuesday 8:30-11:30am</td>
<td>2</td>
<td>Wednesday 6-9pm</td>
<td>1</td>
</tr>
<tr>
<td>2</td>
<td>Taco-Making 101</td>
<td>In this course, you&#x2019;ll learn how to build a proper taco...</td>
<td>Wednesday 6-9pm</td>
<td>2</td>
<td>Thursday 6-9pm</td>
<td>1</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>Seems perfectly reasonable. But what happens when the demand for <em>Taco-Making</em> becomes so large that we add a third time? Or a fourth? Or a 10th? While we could, of course, continue to grow the courses table <em>horizontally</em>, it turns out that it&apos;s generally a much better idea to grow a database <em>vertically</em>. Exponential horizontal growth of a database table is an indicator that we should be doing something different.</p><p>What&apos;s we seeing here is a <em><strong>one-to-many relationship</strong></em> &#x2013; one course can have many times it is available, each with a different possible teacher &#x2013; i.e. <em>Sections</em>.</p><p>Remember a few paragraphs ago, when we defined what a <em>domain model </em>is? To refresh your memory:</p><blockquote>... we&apos;ll call each real-world entity a <em>model. </em>And all of our models, together with all of their <strong>relationships</strong>, is called our <em>domain model</em>.</blockquote><p>The <em>Section </em>model, in this particular case, is not so much a tangible, real-world thing. Rather, it&apos;s a <em>model</em> that&apos;s made necessary due to the <strong>relationships</strong> between our real-world entities.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://entr451.com/content/images/2021/12/image-3.png" class="kg-image" alt loading="lazy" width="1004" height="118" srcset="https://entr451.com/content/images/size/w600/2021/12/image-3.png 600w, https://entr451.com/content/images/size/w1000/2021/12/image-3.png 1000w, https://entr451.com/content/images/2021/12/image-3.png 1004w" sizes="(min-width: 720px) 720px"><figcaption>A course can have one to many sections</figcaption></figure><p>It makes sense for <em>Section</em> to be its own model because:</p><ul><li>We don&apos;t know how many sections of a course there could be. It might be none, only one, or a million.</li><li>A section has its own attributes, like the time it&apos;s offered, and the teacher who teaches it. This list of attributes could potentially change in the future, making it very difficult to manage if this data lived within the <em>Courses</em> model.</li></ul><!--kg-card-begin: markdown--><p><strong><em>courses</em></strong></p>
<hr>
<table>
<thead>
<tr>
<th>id</th>
<th>name</th>
<th>description</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Introduction to Software Development</td>
<td>The course is focused on software development...</td>
</tr>
<tr>
<td>2</td>
<td>Taco-Making 101</td>
<td>In this course, you&#x2019;ll learn how to build a proper taco...</td>
</tr>
</tbody>
</table>
<p><strong><em>sections</em></strong></p>
<hr>
<table>
<thead>
<tr>
<th>id</th>
<th>time</th>
<th>course_id</th>
<th>teacher_id</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>Tuesday 8:30-11:30am</td>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>2</td>
<td>Wednesday 6-9pm</td>
<td>1</td>
<td>1</td>
</tr>
<tr>
<td>3</td>
<td>Wednesday 6-9pm</td>
<td>2</td>
<td>2</td>
</tr>
<tr>
<td>4</td>
<td>Thursday 6-9pm</td>
<td>2</td>
<td>1</td>
</tr>
</tbody>
</table>
<!--kg-card-end: markdown--><p>Aaaahhh! So much better. Now the <em>Course</em> model only holds information about the course itself, delegating the data about each section to the <em>Section</em> model. We can now support as many sections as needed without resorting to changes in our database design.</p><p>We can also see that, in order to create a one-to-many relationship in a domain model, <u>we simply need to add a foreign key column on the &quot;many&quot; side of the equation</u>. In this example, because <strong>one</strong> course has <strong>many<em> </em></strong>sections, we add a <code>course_id</code> to <code>sections</code> and we&apos;re there. </p><p>And yes, there are two foreign keys on <code>sections</code>, because there are actually two <em>one-to-many</em> relationships where <em>Section</em> is involved.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://entr451.com/content/images/2021/12/image-5.png" class="kg-image" alt loading="lazy" width="1146" height="116" srcset="https://entr451.com/content/images/size/w600/2021/12/image-5.png 600w, https://entr451.com/content/images/size/w1000/2021/12/image-5.png 1000w, https://entr451.com/content/images/2021/12/image-5.png 1146w" sizes="(min-width: 720px) 720px"><figcaption>A course can have one to many sections. A teacher can have one to many sections.</figcaption></figure><p>Here we see the last piece of the puzzle:</p><p><strong>Two one-to-many relationships = One many-to-many relationship</strong></p><p>A course can have many teachers. A teacher can teach many courses. The relationship between our <em>Course</em> and <em>Teacher</em> models is a <em>many-to-many relationship, </em>and the <em>Section</em> model is known as their <em>join model.</em></p><h3 id="naming-conventions">Naming Conventions</h3><p>We&apos;ve seen enough of domain modeling that it now makes sense to talk about the conventions we&apos;ve been using. Although there is not a technical reason to adhere to these conventions, it is certainly a set of best practices that we will continue to follow for the remainder of this course.</p><ul><li>When talking about models like <em>Course </em>and <em>Section, </em>we write them in the singular form, and beginning with a capital letter &#x2013; it will make sense why we do this later in the course.</li><li>Database table names like <code>courses</code> and <code>sections</code> are always plural, and in all lower-case.</li><li>Column names are always in lower-case, e.g. <code>name</code> or <code>description</code>.</li><li>If a table or column name is made up of more than one word, we separate the words by underscores, e.g. <code>first_name</code> or <code>teacher_id</code>.</li></ul><h3 id="lab">Lab</h3><p>Begin with the <a href="https://github.com/entr451-spring2026/domain-modeling">template project on GitHub</a>, create a new repo from the template, and open this repo in Codespaces. </p><ul><li>Modify the <code>school.sql</code> script to make the <code>courses</code>, <code>teachers</code>, and <code>sections</code> tables real (the statement that creates the <code>students</code> table is already there). Execute the script by running <code>.read school.sql</code> at the SQLite prompt, and execute the <code>.schema</code> command to ensure that everything went as expected.</li><li>Design the model that allows students to sign up for classes &#x2013;&#xA0;let&apos;s call it <em>Enrollment &#x2013;&#xA0;</em>do it on paper, in Excel, or any other tool of your choice.</li><li>Add the <code>enrollments</code> table you designed above by writing a <code>CREATE TABLE</code> statement in the <code>school.sql</code> script, and execute the script. &#xA0;(For reference, you can find the SQL for adding tables <a href="https://entr451.com/database-modifications-data-and-structure/">here</a>.)</li></ul>]]></content:encoded></item><item><title><![CDATA[Domain Modeling: Exercise I]]></title><description><![CDATA[<p>Now that we know <a href="https://entr451.com/intro-to-domain-modeling/">what domain modeling is</a>, and the basics on how to do it, it&apos;s time to look at larger, more real-world examples.</p><p><em>A Customer Relationship Management (CRM) system is one that allows users (typically sales teams) to keep track of companies/contacts and the interactions</em></p>]]></description><link>https://entr451.com/domain-modeling-case-study-i/</link><guid isPermaLink="false">61773129d9019e19441bba11</guid><category><![CDATA[Lab]]></category><category><![CDATA[Domain Modeling Exercise]]></category><category><![CDATA[Domain Modeling]]></category><dc:creator><![CDATA[Brian Eng]]></dc:creator><pubDate>Mon, 16 Jan 2023 15:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Now that we know <a href="https://entr451.com/intro-to-domain-modeling/">what domain modeling is</a>, and the basics on how to do it, it&apos;s time to look at larger, more real-world examples.</p><p><em>A Customer Relationship Management (CRM) system is one that allows users (typically sales teams) to keep track of companies/contacts and the interactions (emails, sales calls, in-person meetings, etc.) the user has with each contact. If you&apos;ve worked in sales before, chances are you&apos;ve used some kind of CRM system &#x2013; e.g. Salesforce, Zoho, Freshdesk, Pipedrive, or about 1000 others. In this case study, we&apos;re going to create the domain model for our own simple CRM.</em></p><p>Here are the user stories and wireframes:</p><ul><li>As a salesperson, I want to maintain a list of contacts (along with each contact&apos;s name, email address, and phone number), so I know how to reach each person.</li><li>As a salesperson, I want to be able to log each activity (e.g. calls/emails, with a date/time it occurred and my notes.) I have with a contact, so I can keep a diary of all the communication I have with each person.</li><li>As a salesperson, I want to manage a list of companies (name), so I know all the companies we sell to.</li><li>As a salesperson, I want to associate contacts with a company, so that I can get a company-wide view of my sales team&apos;s communication with all the people at a company.</li><li>As a salesperson, I want to maintain my own contact information (first/last name and email address), so that other members of the sales team know who I am.</li></ul><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/04/crm-wireframe-v1.png" class="kg-image" alt loading="lazy" width="1994" height="1330" srcset="https://entr451.com/content/images/size/w600/2022/04/crm-wireframe-v1.png 600w, https://entr451.com/content/images/size/w1000/2022/04/crm-wireframe-v1.png 1000w, https://entr451.com/content/images/size/w1600/2022/04/crm-wireframe-v1.png 1600w, https://entr451.com/content/images/2022/04/crm-wireframe-v1.png 1994w" sizes="(min-width: 720px) 720px"></figure><p>What real-world entities are we dealing with? Once those are determined, what relationships or join models are needed to connect the dots?</p><p>Jot your thoughts down on paper or tool of your choice (sometimes visual is a better way to get started than a bunch of <code>CREATE TABLE</code> statements), then write the script to create the actual SQLite database.</p><h2 id="go-further">Go Further</h2><p>Once you&apos;ve designed a database to support the initial user stories and wireframes, here&apos;s a challenge to flesh out our domain model.</p><p>Additional user stories:</p><ul><li>As a salesperson, I want to maintain a list of industries, so that I can see how my sales team is performing across the different industries we do business with.</li><li>As a salesperson, I want to indicate a company&apos;s industries (1 or more), so I can categorize the companies we sell to by industry.</li></ul><p>Updated wireframe:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://entr451.com/content/images/2021/12/image-9.png" class="kg-image" alt loading="lazy" width="1994" height="1330" srcset="https://entr451.com/content/images/size/w600/2021/12/image-9.png 600w, https://entr451.com/content/images/size/w1000/2021/12/image-9.png 1000w, https://entr451.com/content/images/size/w1600/2021/12/image-9.png 1600w, https://entr451.com/content/images/2021/12/image-9.png 1994w" sizes="(min-width: 720px) 720px"><figcaption>Wireframe for the account view</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[Domain Modeling: Exercise II]]></title><description><![CDATA[<p>Hopefully you&apos;re getting a little more comfortable with <a href="https://entr451.com/intro-to-domain-modeling/">understanding domain modeling</a>. &#xA0;In this exercise, we&apos;ll try to model everyone&apos;s favorite photo sharing social network. &#xA0;It may seem simple at first, but it&apos;s more complicated than it seems.</p><p><em>Tacostagram will be</em></p>]]></description><link>https://entr451.com/domain-modeling-case-study-ii/</link><guid isPermaLink="false">61773148d9019e19441bba15</guid><category><![CDATA[Lab]]></category><category><![CDATA[Domain Modeling Exercise]]></category><category><![CDATA[Domain Modeling]]></category><dc:creator><![CDATA[Brian Eng]]></dc:creator><pubDate>Mon, 16 Jan 2023 14:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Hopefully you&apos;re getting a little more comfortable with <a href="https://entr451.com/intro-to-domain-modeling/">understanding domain modeling</a>. &#xA0;In this exercise, we&apos;ll try to model everyone&apos;s favorite photo sharing social network. &#xA0;It may seem simple at first, but it&apos;s more complicated than it seems.</p><p><em>Tacostagram will be all the rage when it launches. People from around the world will be able to create a constantly evolving feed of photos of their favorite tacos. People will be able to &quot;like&quot; photos of tacos and write comments about each photo. Eventually, we will sell it to technology behemoth MetaTaco for $1 billion.</em></p><ul><li>As a user, I want to create time-stamped posts of tacos, so that I can share them in a &quot;feed&quot; for the world to see.</li><li>As a user, I want to be able to &quot;like&quot; another user&apos;s post, so the author of the post and others will know that I liked it.</li><li>As a user, I want to be able to see how many likes a post has, so I can see how popular it is.</li><li>As a user, I want to not be able to like a post more than once, so I cannot artificially inflate the popularity of a post.</li><li>As a user, I want to be able to comment (and see other&apos;s comments) on a post, so I can engage in conversation with the author and others.</li><li>As a user, I want to be able to &quot;follow&quot; another user, so that I can stay up-to-date on certain users&apos; updates.</li><li>As a user, I want to see only my followed users&apos; posts in my feed, so I can only see posts that I&apos;m interested in.</li><li>As a user, I want to sign up with my username (screen name), real name, and location, so that other people will see that information when visiting my profile.</li></ul><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2021/12/image-11.png" class="kg-image" alt loading="lazy" width="472" height="901"></figure><p><em>Note: we are going to ignore the mechanics of how a photo gets uploaded and stored, and simply use a filename for the time being, e.g. tacos.jpg.</em></p><p>As with the last exercise, jot down your domain model visually first, then move on to the physical creation of the SQLite database.</p>]]></content:encoded></item><item><title><![CDATA[Domain Modeling: Exercise I - solution]]></title><description><![CDATA[<p><em>You can find the exercise description <a href="https://entr451.com/domain-modeling-case-study-i/">here</a>. &#xA0;Below is one possible solution.</em></p><p>These are the entities we came up with, based on the current user stories/wireframes:</p><ul><li><em>Salesperson &#x2013; </em>the salesperson using the system</li><li><em>Contact &#xA0;&#x2013; </em>a person that I have sales interactions (activities) with</li><li><em>Company</em> &#x2013;&#xA0;</li></ul>]]></description><link>https://entr451.com/domain-modeling-exercise-i-solution/</link><guid isPermaLink="false">61c24d1fd9019e19441bd727</guid><category><![CDATA[Lab]]></category><category><![CDATA[Domain Modeling Exercise]]></category><category><![CDATA[Domain Modeling Exercise Solution]]></category><dc:creator><![CDATA[Brian Eng]]></dc:creator><pubDate>Sun, 15 Jan 2023 15:00:00 GMT</pubDate><content:encoded><![CDATA[<p><em>You can find the exercise description <a href="https://entr451.com/domain-modeling-case-study-i/">here</a>. &#xA0;Below is one possible solution.</em></p><p>These are the entities we came up with, based on the current user stories/wireframes:</p><ul><li><em>Salesperson &#x2013; </em>the salesperson using the system</li><li><em>Contact &#xA0;&#x2013; </em>a person that I have sales interactions (activities) with</li><li><em>Company</em> &#x2013;&#xA0;the company that a <em>Contact</em> works for</li><li><em>Activity</em> &#x2013;&#xA0;the interaction (like a call or email) we have with a <em>Contact</em></li><li><em>Industry &#x2013;&#xA0;</em>the market (like &quot;Consumer Electronics&quot;) a company can belong to</li></ul><p>If we begin our data model design, it starts by looking something like this:</p><!--kg-card-begin: html--><pre>
CREATE TABLE salespeople (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  first_name TEXT,
  last_name TEXT,
  email TEXT
);

CREATE TABLE contacts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  first_name TEXT,
  last_name TEXT,
  email TEXT,
  phone_number TEXT
);

CREATE TABLE companies (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT
);

CREATE TABLE activities (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  occurred_at TEXT,
  notes TEXT
);

CREATE TABLE industries (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT
);
</pre><!--kg-card-end: html--><p>And we also have these relationships:</p><ul><li>A one-to-many relationship between <em>Company</em> and <em>Contact; </em>that is, a company can have many contacts, but a contact belongs to only one company</li><li>A one-to-many relationship between <em>Salesperson</em> and <em>Activities; </em>that is, a salesperson can have many activities, but a single activity belongs to only one salesperson</li><li>A one-to-many relationship between <em>Contact </em>and <em>Activities; </em>that is, a contact can have many activities, but a single activity belongs to only one contact</li><li>A many-to-many relationship between <em>Company</em> and <em>Industry; </em>that is, a company can be categorized into multiple industries, and an industry can have many companies</li></ul><p>As we learned in the last lesson, from an implementation perspective, a one-to-many relationship is built by adding a foreign key column to the &quot;many&quot; side of the equation:</p><!--kg-card-begin: html--><pre>
CREATE TABLE salespeople (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  first_name TEXT,
  last_name TEXT,
  email TEXT
);

CREATE TABLE contacts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  first_name TEXT,
  last_name TEXT,
  email TEXT,
  phone_number TEXT,
  <strong>company_id INTEGER</strong>
);

CREATE TABLE companies (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT
);

CREATE TABLE activities (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  occurred_at TEXT,
  notes TEXT,
  <strong>salesperson_id INTEGER,</strong>
  <strong>contact_id INTEGER</strong>
);

CREATE TABLE industries (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT
);
</pre><!--kg-card-end: html--><p>And, a many-to-many relationship is actually two one-to-many relationships put together with a <em>join model</em>; in this case:</p><ul><li>A <em>Company</em> has one-to-many <em>Industries</em></li><li>An <em>Industry</em> has one-to-many <em>Companies</em></li></ul><p>What should we call the <em>join model?</em></p><blockquote><em>There are only two hard things in Computer Science: cache invalidation and naming things.</em><br><br>&#x2013; Phil Karlton</blockquote><p>Many developers like to reference this quote &#x2013;&#xA0;a lot. Naming things is indeed very hard. What&apos;s a word we can use to describe a company/industry combination? Turns out that there really isn&apos;t one &#x2013;&#xA0;not one that the majority of people use and would understand right away and without much explanation. So we&apos;re simply going to refer to this as an <em>Industry Membership</em>. Our final domain model, implemented in SQL, looks like this:</p><!--kg-card-begin: html--><pre>
CREATE TABLE salespeople (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  first_name TEXT,
  last_name TEXT,
  email TEXT
);

CREATE TABLE contacts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  first_name TEXT,
  last_name TEXT,
  email TEXT,
  phone_number TEXT,
  company_id INTEGER
);

CREATE TABLE companies (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT
);

CREATE TABLE activities (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  occurred_at TEXT,
  notes TEXT,
  salesperson_id INTEGER,
  contact_id INTEGER
);

CREATE TABLE industries (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  name TEXT
);

<strong>CREATE TABLE industry_memberships (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  company_id INTEGER,
  industry_id INTEGER
);</strong>
</pre><!--kg-card-end: html--><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/04/ENTR-451---Week-3---domain-model-erd---crm.png" class="kg-image" alt loading="lazy" width="1422" height="800" srcset="https://entr451.com/content/images/size/w600/2022/04/ENTR-451---Week-3---domain-model-erd---crm.png 600w, https://entr451.com/content/images/size/w1000/2022/04/ENTR-451---Week-3---domain-model-erd---crm.png 1000w, https://entr451.com/content/images/2022/04/ENTR-451---Week-3---domain-model-erd---crm.png 1422w" sizes="(min-width: 720px) 720px"></figure>]]></content:encoded></item></channel></rss>