<?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>Fri, 17 Apr 2026 17:32:58 GMT</lastBuildDate><atom:link href="https://entr451.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><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 Gitpod: <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 Gitpod 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 Gitpod, 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 Gitpod, 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><item><title><![CDATA[Domain Modeling: Exercise II - solution]]></title><description><![CDATA[<p><em>You can find the exercise description <a href="https://entr451.com/domain-modeling-case-study-ii/">here</a>. &#xA0;Below is one possible solution.</em></p><p>This exercise is a good example of a domain modeling a more abstract concept. Our primary model &#x2013; let&apos;s call it <em>Post</em> &#x2013; doesn&apos;t line up precisely with a real-world thing, like <em>Student</em></p>]]></description><link>https://entr451.com/domain-modeling-exercise-ii-solution/</link><guid isPermaLink="false">61c24d59d9019e19441bd730</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 14:00:00 GMT</pubDate><content:encoded><![CDATA[<p><em>You can find the exercise description <a href="https://entr451.com/domain-modeling-case-study-ii/">here</a>. &#xA0;Below is one possible solution.</em></p><p>This exercise is a good example of a domain modeling a more abstract concept. Our primary model &#x2013; let&apos;s call it <em>Post</em> &#x2013; doesn&apos;t line up precisely with a real-world thing, like <em>Student </em>or <em>Teacher</em> from the previous exercise. Rather, it&apos;s a digital analogy for something that could exist in the real world (like a Polaroid photo pinned to a physical cork-board, displayed for the world to see, for instance), and <em>Post </em>is something that only exists in your digital world.</p><p>Here are two main entities we came up with to start, based on the current user stories/wireframes:</p><ul><li>User &#x2013; a person using the system</li><li>Post &#x2013;&#xA0;the taco image being posted, along with data that describes it, like the time it was posted and the user who posted it</li></ul><p><em>As noted, 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><pre><code>CREATE TABLE users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_name TEXT,
  real_name TEXT,
  location TEXT
);

CREATE TABLE posts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id INTEGER,
  image_file TEXT,
  posted_at TEXT
);</code></pre><p>The column names are going to vary based on what you thought they should be called, but generally, this is straightforward. The one thing to note is that there is a one-to-many relationship between <em>User</em> and <em>Post</em>, that is, a <em>User</em> may have one-to-many <em>Posts</em>. As we have learned, in SQL, a one-to-many relationship is built by adding a foreign key column to the &quot;many&quot; side of the equation &#x2013; in this case, we have a <code>user_id</code> on the <code>posts</code> table.</p><h3 id="likes">Likes</h3><p>Next, we need our domain model to support the ability for a user to &quot;like&quot; a post. This &quot;like count&quot; is then displayed on the post itself; for example, on the wireframe, our example post has <em>85 likes. </em>Our instinct might be to say, <em>the number of likes is simply an attribute of a post, </em>and implement it by simply adding a column to the <code>posts</code> table:</p><!--kg-card-begin: html--><pre>
CREATE TABLE posts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id INTEGER,
  image_file TEXT,
  posted_at TEXT,
  <b>number_of_likes INTEGER</b>
);
</pre><!--kg-card-end: html--><p>This certainly satisfies our requirement that a <em>Post</em> has likes, and is simply an integer that can be incremented based on the number of people who like the post. However, it does not work when considering the requirement in our fourth user story: <em>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. </em></p><p>At some point, we&apos;d need to add some application-level logic that prevents a user from incrementing this number more than once &#x2013;&#xA0;however, it&apos;s pretty difficult to do that unless we know whether or not a user has liked a post before. We can do this once we shift our domain model towards the idea that <em>a <strong>like</strong> is the join model between a <strong>user</strong> and a <strong>post:</strong></em></p><ul><li>A <em>User</em> has a many-to-many relationship with a <em>Post</em></li><li>A many-to-many relationship is actually two one-to-many relationships put together with a join model &#x2013;&#xA0;in this case, a <em>Like</em></li><li>A user has one-to-many liked posts</li><li>A post is liked by one-to-many users</li></ul><!--kg-card-begin: html--><pre>
CREATE TABLE users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_name TEXT,
  real_name TEXT,
  location TEXT
);

CREATE TABLE posts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id INTEGER,
  image_file TEXT,
  posted_at TEXT
);

<b>CREATE TABLE likes (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id INTEGER,
  post_id INTEGER
);</b>
</pre><!--kg-card-end: html--><p>This creates the many-to-many relationship between a user and a post, while also creating a way to track which users have liked which posts.</p><h3 id="comments">Comments</h3><p>A <em>Comment</em> is very similar to a <em>Like</em>. It is another join model, representing the many-to-many relationship between a <em>Post </em>and a <em>User</em>.</p><ul><li>A <em>User</em> has a many-to-many relationship with a <em>Post</em></li><li>A many-to-many relationship is actually two one-to-many relationships put together with a join model &#x2013;&#xA0;in this case, a <em>Comment</em></li><li>A user has one-to-many commented on posts</li><li>A post is commented on by one-to-many users</li></ul><pre><code>CREATE TABLE comment (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id INTEGER,
  post_id INTEGER,
  body TEXT
);</code></pre><p>The only difference between a <em>Comment </em>and a<em> Like</em>, in practice, is that we need to add an additional field &#x2013;&#xA0;<code>body</code> &#x2013; to hold the body of the comment itself.</p><h3 id="followers">Followers</h3><p>There are a couple of concepts to discuss, in order to properly model one user following another user.</p><p>The first is that it&apos;s perfectly ok for a domain model relationship to be <em>self-referential</em>. That is, a model can have a relationship with itself, i.e. <em>a user may followed by one-to-many users. </em>This is not super-common, but it is sometimes necessary to do.</p><p>The second is that, while we&apos;ve learned that a foreign key is, by convention, the singular name of a model followed by <code>_id</code>, it is occasionally necessary to break this convention to make the database design more clear. Let&apos;s look at the finished <em>Follower</em> model:</p><pre><code>CREATE TABLE followers (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  followed_user_id INTEGER,
  follower_user_id INTEGER
);</code></pre><p>In this particular case, it&apos;s more important to name our columns this way, to make a distinction between the user being followed and the user doing the following. Note that we&apos;re still using the word <code>user_id</code> in the names of these columns, to ensure that we continue to know that the foreign keys reference a primary key in the <code>users</code> table.</p><p>Here is our complete domain model, implemented using SQL:</p><pre><code>CREATE TABLE users (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_name TEXT,
  real_name TEXT,
  location TEXT
);

CREATE TABLE posts (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id INTEGER,
  image_file TEXT,
  posted_at TEXT
);

CREATE TABLE likes (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id INTEGER,
  post_id INTEGER
);

CREATE TABLE comments (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  user_id INTEGER,
  post_id INTEGER,
  body TEXT
);

CREATE TABLE followers (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  followed_user_id INTEGER,
  follower_user_id INTEGER
);</code></pre><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/04/ENTR-451---Week-3---domain-model-erd---tacogram.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---tacogram.png 600w, https://entr451.com/content/images/size/w1000/2022/04/ENTR-451---Week-3---domain-model-erd---tacogram.png 1000w, https://entr451.com/content/images/2022/04/ENTR-451---Week-3---domain-model-erd---tacogram.png 1422w" sizes="(min-width: 720px) 720px"></figure>]]></content:encoded></item><item><title><![CDATA[Practice: GitHub]]></title><description><![CDATA[<p>In this week&apos;s practice exercise, we&apos;re going to reinforce the skills acquired in this week&apos;s class by creating and modifying a GitHub repository, using as much command-line as possible along the way.</p><h3 id="creating-a-repo">Creating a Repo</h3><p>Begin by creating a brand-new GitHub repository in your</p>]]></description><link>https://entr451.com/practice-github/</link><guid isPermaLink="false">61ca2d92d9019e19441bd73e</guid><category><![CDATA[Getting Started]]></category><dc:creator><![CDATA[Brian Eng]]></dc:creator><pubDate>Tue, 03 Jan 2023 23:02:00 GMT</pubDate><content:encoded><![CDATA[<p>In this week&apos;s practice exercise, we&apos;re going to reinforce the skills acquired in this week&apos;s class by creating and modifying a GitHub repository, using as much command-line as possible along the way.</p><h3 id="creating-a-repo">Creating a Repo</h3><p>Begin by creating a brand-new GitHub repository in your GitHub account. Call it <em>shopping-list</em>, and keep the repository public. Then, open the repository in Codespaces.</p><p>Open the <em>Terminal</em> in Codespaces &#x2013;&#xA0;<em>hamburger menu -&gt; View -&gt; Terminal. </em>The current working directory should be <code>/workspace/shopping-list</code>, but if it isn&apos;t, you can <code>cd</code> into that directory. Create a new file called <code>README.md</code> by typing <code>touch README.md</code> at the prompt. You should see it appear in the <em>Explorer</em> in the left-hand sidebar. Click on the file in <em>Explorer</em> to open it.</p><p>Edit the <code>README.md</code> file and add a bulleted list of items to purchase. This can be a list of anything you want, with each item on its own line, and each line starting with a dash and a space. Like this:</p><pre><code>- Bacon
- Eggs
- Xbox</code></pre><p>Commit your changes using the <em>Source Control</em> pane on the left-hand side. Add a meaningful commit message, such as &quot;adds initial list of items&quot;. Then, click on the three dots under <em>Source Control</em>, and choose &quot;Push&quot;.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2021/12/image-10.png" class="kg-image" alt loading="lazy" width="463" height="224"></figure><p>This will publish your initial code to GitHub. Head back over to the repository page on GitHub.com, and you should see your code there, complete with commit message and list of files (there should only be one). Because you named the file <code>README.md</code>, the contents of the file should appear on the repository&apos;s home page.</p><h3 id="push-changes">Push changes</h3><p>Head back to Codespaces. Next, add a few items to your shopping list. Then, commit the changes (with a good commit message describing the work), and sync the changes to GitHub.</p><p>After syncing, go back to the repository page on GitHub.com, and refresh. Notice how the changes you&apos;ve made have affected the repository. At this point, feel free to poke around and see how Git and GitHub track your changes. For example, click on your latest commit message to see how GitHub presents a visual of the changes you made in that commit.</p><h3 id="add-files">Add files</h3><p>Use the <em>Explorer</em> in Codespaces to upload an image file from your computer. Right-click in the <em>Explorer</em> and select <em>Upload..., </em>then select the file from your computer. The file will be uploaded and appear in your <em>Explorer</em>. Then, commit and sync your changes.</p><p>Head back to GitHub.com and see how those changes have affected the online repository.</p><p>Let&apos;s decide that images belong in a sub-directory called <code>images</code>. Head back to the Codespaces Terminal, and create the directory:</p><pre><code>mkdir images</code></pre><p>Then, move the image you uploaded into the newly created <code>images</code> directory. If the image you uploaded was called <code>tacos.jpg</code>, you would do:</p><p><code>mv tacos.jpg images</code></p><p>(Of course, we can do this using the mouse and the graphical interface within Codespaces; we just want practice using the command-line instead.)</p><p>Finally, commit and sync to GitHub, view the repository at GitHub.com, and verify how the repository contents have changed.</p>]]></content:encoded></item><item><title><![CDATA[Command-Line Basics and the Development Environment]]></title><description><![CDATA[<p>Making very basic changes to our code files within GitHub works perfectly fine, but soon enough, we must graduate to a more full-featured development environment. The <em>development environment</em> is a generic term used to describe the hardware, operating system, and software necessary for a developer to do his or her</p>]]></description><link>https://entr451.com/command-line-basics-and-the-development-environment/</link><guid isPermaLink="false">61772fd9d9019e19441bb9e9</guid><category><![CDATA[Getting Started]]></category><dc:creator><![CDATA[Brian Eng]]></dc:creator><pubDate>Tue, 03 Jan 2023 23:01:00 GMT</pubDate><content:encoded><![CDATA[<p>Making very basic changes to our code files within GitHub works perfectly fine, but soon enough, we must graduate to a more full-featured development environment. The <em>development environment</em> is a generic term used to describe the hardware, operating system, and software necessary for a developer to do his or her job. The following are just a few examples of a development environment:</p><ul><li>Apple MacBook Pro (hardware) + Mac OS (operating system) + Atom (code editor)</li><li>Apple iMac (hardware) + Mac OS (operating system) + XCode (code editor)</li><li>Microsoft Windows Surface Pro (hardware) + Windows 11 (operating system) + Microsoft Visual Studio Code (code editor)</li><li>Lenovo Thinkpad X1 Carbon (hardware) + Ubuntu Linux 20 (operating system) + Microsoft Visual Studio Code (code editor)</li></ul><p>Of course, in addition to the hardware, OS, and code editor, we must add other job-specific tools, such as a web server (for web developers), device emulators (for mobile developers), and hardware development kits (for IoT developers). The possibilities are endless.</p><p>As mentioned previously, we are going to be working on building web applications in this course. We could spend a few hours installing the command-line interfaces, web servers, and other applications needed to build them, specific to the computer hardware and operating systems you might have, debugging and tweaking what we need along the way. Or, we could use a cloud-based development environment, which includes 100% of what we need, already packaged up and ready to go. This, of course, is the value proposition of <a href="https://github.com/features/codespaces">Github Codespaces</a> &#x2013; an all-in-one (i.e. dedicated hardware, OS, application software, and code editor), cloud-based development environment that will put all the setup and infrastructure behind us, and get us to coding right away.</p><h2 id="getting-started">Getting Started</h2><p>Assuming you already have a Github account, getting our development environment up-and-running is (mostly) a click away.</p><p>Head over to the <em>hello-world</em> repository you created in the last lesson. If you can&apos;t find it, head over to your Github home page and look on the left-hand side under <em>Repositories</em>. On the project page for the repository, click the button that says <em>Code</em>, select the <em>Codespaces</em> tab, then click the &quot;Create codespace&quot; button<em>.</em></p><p>If everything goes smoothly, you should be launched into the Codespaces development environment, with a column of folders and files contained within your <em>hello-world </em>project on the left-hand side. You might see a <em>Welcome</em> page &#x2013; feel free to close it.</p><p>Click on <code>README.md</code> on the left-hand side (the area known as the <em>Explorer</em>); you should see the editable contents of that file appear on the right (the <em>Editor</em>).</p><h3 id="editing-committing-and-syncing-files-in-your-codespace">Editing, Committing, and Syncing Files in your Codespace</h3><p>Go ahead and make a change &#x2013; any change &#x2013; to your <code>README.md</code> file. Changes are auto-saved automatically by default, so there&apos;s no need to worry about saving yourself. When you make a change, you&apos;ll immediately see the file you&apos;re working on change color in the <em>Explorer</em>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://entr451.com/content/images/2021/11/image-1.png" class="kg-image" alt loading="lazy" width="345" height="174"><figcaption>Something similar to what you should see after editing a file.</figcaption></figure><p>Notice the funny icon (the one that looks like branches) now has a (1) next to it? That&apos;s letting you know that you&apos;ve made 1 change since the last time this code was committed. If you click on that icon, you&apos;ll find yourself in the <em>Source Control</em> pane.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://entr451.com/content/images/2021/11/image-2.png" class="kg-image" alt loading="lazy" width="344" height="186"><figcaption>The Source Control pane.</figcaption></figure><p>When you&apos;re ready to commit your changes, the process is essentially the same as when we did it using GitHub&apos;s web-based interface. The <em>Message</em> that it&apos;s asking for is the commit message, i.e. a description of this unit of work that you&apos;re about to commit. Enter a message and hit the check mark icon (&#x2714;&#xFE0F;) to commit.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://entr451.com/content/images/2021/11/image-3.png" class="kg-image" alt loading="lazy" width="349" height="179"><figcaption>Committing our changes.</figcaption></figure><p>This being the first time you&apos;re committing changes, you&apos;ll be asked what you want to do:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://entr451.com/content/images/2021/11/image-4.png" class="kg-image" alt loading="lazy" width="520" height="179"><figcaption>Basically, an &quot;are you sure?&quot; message.</figcaption></figure><p>You can either select <em>Yes</em> here, or <em>Always</em>, so you don&apos;t see this message again. At this point, you&apos;ve successfully committed your changes to your local Git (i.e. the Git software that runs on the cloud-based computer that we&apos;re using), but <strong>you have not yet sent those changes back to GitHub</strong>. To do so, click the <em>Sync Changes</em> button.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://entr451.com/content/images/2021/11/image-5.png" class="kg-image" alt loading="lazy" width="345" height="177"><figcaption>Code has been committed but not yet sent to GitHub. Click Sync Changes to send to GitHub.</figcaption></figure><p>Since it&apos;s the first time syncing (see a theme here?), you&apos;ll see this prompt:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://entr451.com/content/images/2021/11/image-6.png" class="kg-image" alt loading="lazy" width="515" height="164"><figcaption>Yet another &quot;are you sure?&quot; message.</figcaption></figure><p>Click <em>OK, Don&apos;t Show Again</em>. What we&apos;re saying here is, when you hit <em>Sync Changes</em> from now on, push my changes to GitHub, and also get changes from the GitHub-hosted repository, if there are any. Because we&apos;re not working in teams (yet), the pulling of commits will almost always do nothing.</p><p>Along the same lines, you&apos;ll also be asked:</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2021/11/image-7.png" class="kg-image" alt loading="lazy" width="452" height="80"></figure><p>Again, because we&apos;re not working in teams or otherwise won&apos;t have multiple contributors to the same online GitHub repository, this doesn&apos;t really matter &#x2013; it&apos;s simply asking if you want to automatically pull changes from the hosted repository on GitHub every few minutes. You can safely click either option.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://entr451.com/content/images/2021/11/image-8.png" class="kg-image" alt loading="lazy" width="344" height="176"><figcaption>The Source Control pane after successfully committing and syncing changes.</figcaption></figure><p>It&apos;s always a good idea to confirm that your commit made it to your GitHub repository by going to the project page.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://entr451.com/content/images/2021/11/image-9.png" class="kg-image" alt loading="lazy" width="781" height="44" srcset="https://entr451.com/content/images/size/w600/2021/11/image-9.png 600w, https://entr451.com/content/images/2021/11/image-9.png 781w" sizes="(min-width: 720px) 720px"><figcaption>Confirmation that the commit made it to GitHub</figcaption></figure><h3 id="how-often-should-you-commit-and-sync">How often should you commit and sync?</h3><p>Now that you know <em>how</em> to edit code, commit, and sync, it begs the question: how often should you do it? After all, there&apos;s nothing to physically prevent you from writing 10,000 lines of code across 200 different code files, then commit and sync it all. <em>(Spoiler alert &#x2013; don&apos;t do that.)</em></p><p>The answer can vary based on the culture of the company/development team that a software developer works with, but in general, each commit should represent a single unit of work. Some examples of a unit of work might be:</p><ul><li>Built a feature</li><li>Added a user-interface element</li><li>Modified some documentation</li></ul><p>Again, there&apos;s usually no hard-and-fast rule as to when to commit/sync, but broadly speaking, some good guidelines to follow are:</p><ul><li>Commit when you have something worth committing &#x2013; e.g. a working feature or a big step toward a larger goal.</li><li>Don&apos;t commit code that is broken &#x2013;&#xA0;don&apos;t use commits to &quot;save&quot; your code to disk in the cloud, just for the sake of doing it.</li><li>Commit as often as possible, BUT &#x2013; it&apos;s based on the work, not the time. You could potentially commit 20 times in a day, or once in a week, based on your pace of work and what you&apos;ve accomplished.</li></ul><h2 id="the-command-line-interface-cli-aka-terminal">The Command-Line Interface (CLI) aka &quot;Terminal&quot;</h2><p>One trait that is very common among professional software developers is that they are also power-users of the computer. When was the last time you saw a pro dev using the mouse pointer, double-clicking to slowly do things on their computer? No &#x2013; you usually see their fingers moving lightning-fast over a text-based interface, heaps of text scrolling by as they fire off commands to have the computer do what they want. What they&apos;re doing is using the computer&apos;s <em>command-line interface</em>, or CLI for short. </p><p>The CLI is simply a way for you to tell your computer to do things by inputting text commands, instead of using the mouse. It&apos;s old-school. But it&apos;s also a way to do things much, much faster than clicking around all the time, so it&apos;s the preferred way for many professional developers to manage files and run software. We&apos;ll be using it a lot.</p><h3 id="what-can-you-do-with-it">What can you do with it?</h3><p>Almost anything you&apos;d normally do with the mouse and keyboard. In this lesson, we&apos;ll focus on simple things, like how to traverse and browse the directory structure on your computer, move files around, and run programs. </p><h3 id="accessing-the-cli">Accessing the CLI</h3><p>As mentioned, every computer has a command-line interface. On Mac OS, you could access it using the built-in <em>Terminal</em> application or maybe <em>iTerm</em>. On Windows, you&apos;d use <em>Command Prompt</em> or perhaps <em>Powershell. </em>But since we&apos;re not using our own computers for development &#x2013; we&apos;re using the cloud-based computer provided to us by Codespaces &#x2013; we&apos;ll use the Codespaces Terminal, in this case.</p><p>Start with the main menu in Codespaces (the &quot;hamburger&quot; menu in the upper-left). Choose <em>View -&gt; Terminal.</em> </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://entr451.com/content/images/2021/11/image-10.png" class="kg-image" alt loading="lazy" width="1620" height="622" srcset="https://entr451.com/content/images/size/w600/2021/11/image-10.png 600w, https://entr451.com/content/images/size/w1000/2021/11/image-10.png 1000w, https://entr451.com/content/images/size/w1600/2021/11/image-10.png 1600w, https://entr451.com/content/images/2021/11/image-10.png 1620w" sizes="(min-width: 720px) 720px"><figcaption>A new Terminal window.</figcaption></figure><p>A brand-new Terminal window will open, and is ready to accept your commands. Hit <em>Return</em> a few times. You&apos;ll notice that, each time you hit <em>Return</em>, you&apos;ll going to be presented with the same Terminal prompt &#x2013;&#xA0;that is, some text followed by the <code>$</code> symbol. That <code>$</code> symbol is telling you that the Terminal is ready to accept your input. This is important to know, because sometimes you won&apos;t see it! When you don&apos;t see it, that&apos;s letting you know that you can&apos;t type commands, either because it&apos;s still doing something else, or that it&apos;s confused about what you want it to do next. Most of the time, you can hit <code>Control-C</code> to get back to the prompt. When in doubt, hit <code>Control-C</code>.</p><h3 id="the-working-directory">The Working Directory</h3><p>The first thing you might notice about the prompt is that it always prints the current <em>working directory</em>. What&apos;s the <em>working directory</em>? It&apos;s the directory that where you&apos;re, well... working. Any commands you type are going to apply relative to the files and folders in this directory. To try it out, type <code>ls</code> and hit <em>Return.</em></p><p><code>ls</code> is the <em>List </em>command. It lists all the files in the working directory. You should see an output similar to:</p><p><code>code &#xA0;images &#xA0;LICENSE &#xA0;README.md</code></p><p>This is a simple list of files in our working directory, and should be the same as the files you see in <em>Explorer</em> in the left sidebar.</p><p>You can also create a new directory. Try typing <code>mkdir tacos</code> and hitting <em>Return.</em></p><p><code>mkdir</code> stands for <em>Make Directory. </em>This command takes an <em>argument, </em>i.e. more words after the command name that are used as inputs to the <code>mkdir</code> command itself. Naturally, it makes a new directory &#x2013;&#xA0;relative to the current working directory &#x2013; with the new directory name provided as an argument. You should see a new <code>tacos</code> directory appear in the <em>Explorer</em>.</p><h3 id="traversing-directories">Traversing directories</h3><p>Now that you have a new directory, let&apos;s make that directory the new working directory. Type <code>cd tacos</code> and hit <em>Return. </em>You&apos;ll see that the current working directory has now changed to <code>/workspace/hello-world/tacos</code>.</p><p><code>cd</code> stands for <em>Change Directory</em>. <code>cd</code> accepts an argument&#xA0;of the directory you want to change to. This argument can either be a <em>relative path</em> &#x2013;&#xA0;like you&apos;ve done here, i.e. <code>tacos</code> is the name of the directory inside your current working directory &#x2013;&#xA0;or it can be an <em>absolute path &#x2013;&#xA0;</em>i.e. the full path to the directory on the computer.</p><p>Said a different way, <code>cd tacos</code> works because the working directory is <code>/workspace/hello-world</code> and <code>tacos</code> is a directory inside of it. If your working directory was somewhere else, and you wanted to change your working directory to the <code>tacos</code> directory, you&apos;d have to provide the absolute path, i.e. <code>cd /workspace/hello-world/tacos</code>.</p><p>Ok, your current working directory is <code>/workspace/hello-world/tacos</code> &#x2013; but now you want to change it back to the parent <code>hello-world</code> directory. You could use the absolute path:</p><p><code>cd /workspace/hello-world</code></p><p>And that would work perfectly fine. But there&apos;s also a shortcut to change the working directory to traverse up one level, to the parent directory:</p><p><code>cd ..</code></p><p><code>..</code> is simply shortcut syntax for &quot;parent directory&quot;.</p><h3 id="ok-what-now">Ok, what now?</h3><p>Here&apos;s just a quick cheat-sheet of other things you can do with files using the CLI. </p><p>Try creating a file in the current working directory:</p><p><code>touch hello.txt</code></p><p>You can have a look at its contents, if you want:</p><p><code>cat hello.txt</code></p><p>You can also copy it:</p><p><code>cp hello.txt hello2.txt</code></p><p>Or move (rename it):</p><p><code>mv hello2.txt hello3.txt</code></p><p>Or delete it:</p><p><code>rm hello3.txt</code></p><h3 id="bonus-level-expert">Bonus Level: Expert</h3><p>There are a couple of ways you can move extra-fast through the CLI. If you&apos;ve ever watched a pro developer work, this is how they make it seem like they can type at superhuman speeds. The first trick is using the <code>tab</code> key. The <code>tab</code> key is essentially &quot;auto-complete&quot; for the CLI. Type the first couple of letters of a command and hit <code>tab</code> &#x2013; the command will be auto-completed for you. To try it, type <code>mkd</code> and hit <code>tab</code> &#x2013;&#xA0;it will auto-complete to <code>mkdir</code>. Works on filenames and pathnames as well.</p><p>The other trick is using the <em>arrow keys</em>. <code>&#x2191;</code>will repeat the previous command &#x2013; and you can use <code>&#x2191;</code> and <code>&#x2193;</code>to cycle through all recently used commands.</p><h3 id="running-programs">Running Programs</h3><p>These commands you&apos;ve been using like <code>ls</code>, <code>mkdir</code>, <code>cd</code> &#x2013;&#xA0;these are all programs (applications) that are built-in to the operating system we&apos;re using (Linux). Of course, you can run our own programs too... let&apos;s try it! There&apos;s a program located in the <code>code</code> directory called <code>hello.rb</code>. This is a program written in the Ruby programming language, so you&apos;ll have to use the <code>ruby</code> command to execute it, i.e. <code>ruby hello.rb</code>. Try typing that now, and perhaps you&apos;ll get a message that looks like this:</p><pre><code>/workspace/hello-world $ ruby hello.rb
Traceback (most recent call last):
ruby: No such file or directory -- hello.rb (LoadError)</code></pre><p>This is an error message that is coming back from the <code>ruby</code> program. Why did you get this? Because you&apos;re in the wrong working directory! There&apos;s no program in the <code>/workspace/hello-world</code> directory &#x2013;&#xA0;it lives in the <code>code</code> directory underneath it. So first change to that directory by typing <code>cd code</code>. Now that your working directory is <code>/workspace/hello-world/code</code>, you can try running <code>ruby hello.rb</code> again:</p><pre><code>Hello, world!</code></pre><p>Let&apos;s try running the other program in that directory &#x2013;&#xA0;go ahead and type <code>ruby infinite-tacos.rb</code>.</p><pre><code>Tacos!
Tacos!
Tacos!
Tacos!
Tacos!</code></pre><p>What a useful program! It simply prints <code>Tacos!</code> forever. You&apos;ll notice, though, that you never get returned to the prompt, because the CLI is stuck in the execution of the program. When you want to halt the execution of the program and return to the prompt, <code>Ctrl+C</code> to the rescue - hold down the control key and press &quot;C&quot;.</p><pre><code>infinite-tacos.rb:2:in `write&apos;: Interrupt</code></pre><p>The program will stop and you should be back at the prompt.</p><h3 id="working-with-github-codespaces">Working with Github Codespaces</h3><p>As you&apos;ve seen, Codespaces is simply a cloud-based computer, running the Linux operating system, with a nice code editor (which is actually Microsoft&apos;s Visual Studio Code &#x2013;&#xA0;open source FTW!) and user interface running on top of it.</p><p>Everything we&apos;ll want to do in this course can be done from the user interface or from the Terminal command-line. But there are a few things which are less intuitive due to the nature of a cloud-based environment.</p><p>The first thing is getting files to/from Codespaces from your actual computer. You won&apos;t have to do this very often in this course, because almost everything will be provided to you via a GitHub repository. But occasionally you might want to upload your own image or CSV file, or download some output created. Fortunately, doing so is very straightforward. To download a file, simply right-click on it in <em>Explorer</em> and choose <em>Download</em>. To upload a file, right-click a folder in <em>Explorer</em>, and select <em>Upload</em>. Try it now &#x2013; upload an image from your computer to the <code>images</code> sub-folder.</p><p>The other, and more important, thing to know about Codespaces is that <strong>your development environment is not permanent</strong>. Your workspace will time out and shut down if not used within 30 minutes. No big deal &#x2013;&#xA0;you can start it back up. To do so, don&apos;t follow the same steps as before (Code &gt; Codespaces tab &gt; Create codespace) - that will create a new codespace which may not have all your recent work (and will reduce the available resources on your free plan). &#xA0;Instead, go directly to your <a href="https://github.com/codespaces/">Codespaces dashboard</a> and reopen the recent codespace from there - everything will be just as you left it.</p><p>That said, eventually an unused codespace &#xA0;&#x2013;&#xA0;ones that haven&apos;t been started up within a 1-2 weeks&#xA0;&#x2013; are deleted. Don&apos;t fret though! There&apos;s a simple solution to always preserving your work... <strong>commit and sync any work you do in Codespaces back to GitHub</strong><em>... do it early and do it often. </em>GitHub repositories <u>are</u> permanent &#x2013; they (and the entire history of your work within them) remain in your account unless you explicitly choose to delete them.</p>]]></content:encoded></item><item><title><![CDATA[Git, GitHub, and Open Source]]></title><description><![CDATA[<p>If you&apos;ve been working in or around technology at all in the last few years, chances are high that you&apos;ve heard the words <em>Git </em>or maybe <em>GitHub.</em> You&apos;ve almost certainly heard the terms <em>Open Source Software</em> or <em>OSS, </em>for short. What do these all</p>]]></description><link>https://entr451.com/github-and-open-source/</link><guid isPermaLink="false">61772fbdd9019e19441bb9e5</guid><category><![CDATA[Getting Started]]></category><dc:creator><![CDATA[Brian Eng]]></dc:creator><pubDate>Tue, 03 Jan 2023 23:00:00 GMT</pubDate><content:encoded><![CDATA[<p>If you&apos;ve been working in or around technology at all in the last few years, chances are high that you&apos;ve heard the words <em>Git </em>or maybe <em>GitHub.</em> You&apos;ve almost certainly heard the terms <em>Open Source Software</em> or <em>OSS, </em>for short. What do these all mean, and why are these concepts important to understand, as we&apos;re examining the software development industry at-large?</p><p>First, let&apos;s define the two main ways in which software is built and brought to market:</p><ul><li><strong>Closed-Source:</strong> The software you work on is typically a commercial product, or exists to serve a commercial interest. Every line of code written is only seen by those working on it. The code, and the intellectual property that goes along with it, is an asset of the individual or company that owns it. Most software that is sold, whether as packaged software or software-as-a-service, fits into this model. &#xA0;The source code behind your favorite web-based service likely does as well. After all, if a company&apos;s &quot;secret sauce&quot; has anything to do with the software code, they probably don&apos;t want anyone else having access to it. Companies compete by recruiting the best developers to build the best software, and these assets are to be kept behind closed doors under lock-and-key.</li><li><strong>Open-Source:</strong> The code behind the software you work on, whether it is a commercial product or not, is available to the public.</li></ul><p>You may be asking &#x2013;&#xA0;what motivates a software developer or company to pour hours &#x2013; in some cases, <em>thousands of hours</em> &#x2013; of work into a software project, only to make the code free and fully available to the public? The answer may vary based on the specific situation, but few examples may include:</p><ul><li>Crowdsourcing. By releasing source code to the public, a developer may be hoping for suggestions on improvement or, better still, contributions of work to the code that make the project better over time.</li><li>The idea of transparency and &quot;knowing what&apos;s in your food&quot;. For example, the developers of <a href="https://telegram.org/">Telegram</a>, an open-source messenger app, publish the source code to prove that they&apos;re not using your personal data for anything malicious &#x2013; something that other commercial products can&apos;t promise.</li><li>Credibility and reputation. An individual developer may choose to make a portion of their work open-source, in order to showcase their abilities to potential employers or future partners.</li><li>Recruiting. A company may make some of their work open-source to attract potential future employees with the cool stuff they&apos;re building.</li></ul><p>These are all great reasons for a developer or company to contribute their work to the world of OSS. But the biggest reason is a science-based culture of sharing and &quot;no need to reinvent the wheel&quot;. As renowned computer scientist and Stanford CS professor emeritus Donald Knuth once said, <em>&quot;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.&quot; </em>Software, like most things, is better built in cooperation with other humans. This idea &#x2013; even within the context of commercial enterprises &#x2013; has largely prevailed in the last decade or so over the concept of closed-source, proprietary software.</p><h3 id="licensing">Licensing</h3><p>How open-source software is licensed is a big topic, with many licensing models and derivatives of each model. Generally though, there are two ways in which an open-source developer might decide to treat their software project when sharing it with others:</p><ul><li>You may use this code in your project, but if you modify it, you must license the new work under the same model &#x2013;&#xA0;the code can be used unmodified without restriction (GNU General Public License and other &quot;copyleft&quot; licenses)</li><li>You may use this code however you want &#x2013; you can modify it, sell it commercially, or include it within another open-source project (Apache, MIT, and other &quot;permissive&quot; licensing models)</li></ul><p>More information on choosing a software license can be found at <a href="https://choosealicense.com/">https://choosealicense.com/</a>. Regardless of the licensing model used, it is certainly possible to use OSS to augment, or as the basis for, commercial ventures.</p><h3 id="case-study-the-linux-family-tree">Case Study: The Linux Family Tree</h3><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://github.com/torvalds/linux"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - torvalds/linux: Linux kernel source tree</div><div class="kg-bookmark-description">Linux kernel source tree. Contribute to torvalds/linux development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">torvalds</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/05fb7b4896b485b58d31062572157cb9645d540c92eb8b263b993091845f8165/torvalds/linux" alt></div></a><figcaption>The operating system that powers most of the Internet</figcaption></figure><p>Here we have the source code for the Linux operating system. The core functionality of Linux (the &quot;kernel&quot;) has been utilized (aka &quot;forked&quot;) many times for use in both other OSS projects and commercial projects. Developers have taken the open-source Linux kernel source code and created their own &quot;flavors&quot; or &quot;forks&quot; of Linux, such as Ubuntu Linux, Red Hat Linux, Fedora Linux, and so on. It&apos;s estimated that nearly 78% of the Internet runs on servers powered by some flavor of Linux (96% of the top 1 million).</p><p>Meanwhile, in 2008, a team at Google built another &quot;flavor&quot; of Linux &#x2013;&#xA0;the Android operating system. Because the Linux kernel is being used to power Android without modification, Google is able to license Android under the Apache License &#x2013; a less restrictive, <em>permissive</em> license that allows others to modify and/or build on top of it, even for commercial purposes.</p><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://source.android.com/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Android Open Source Project</div><div class="kg-bookmark-description">Android unites the world! Use the open source Android operating system to power your device. COPY</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.gstatic.com/devrel-devsite/prod/vff7e8445fa7d53cc21534f5556b6a4ee6bfada97ce5d2ad049b51bab5c641518/androidsource/images/touchicon-180.png" alt><span class="kg-bookmark-author">Android Open Source Project</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://source.android.com/images/landing_icon-porting.png" alt></div></a><figcaption>The Android OS, from Google, built on top of Linux</figcaption></figure><p>Today, Android is, as most of us know, the #2 mobile phone operating system behind iOS. As mentioned, Android is permissively open-source, although Google and its partners do bundle commercial, closed-source products, like Google Chrome and Google Play Store, alongside Android on the mobile hardware they sell. This is a straightforward example of one way open-source can be used to power for-profit enterprises.</p><p>Because Android is licensed using the Apache licensing model, developers are free to incorporate, modify, and distribute Android as they wish, even in commercial ventures. As a result, many companies have now forked and transformed the Android OS into the basis for its own products, including manufacturers of tablets, set top entertainment devices, and many other types of hardware. For example, Amazon has its own commercial, closed-source fork of Android &#x2013; Fire OS &#x2013; that it uses on its Amazon Kindle Fire products. Facebook has built its own closed-source and proprietary Oculus VR product on top of Android, as has Peloton as the backbone for its home-based fitness devices. All of these ventures can find their roots in Android and ultimately, Linux.</p><h3 id="other-interesting-open-source-projects">Other Interesting Open-Source Projects</h3><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://github.com/chromium/chromium"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - chromium/chromium: The official GitHub mirror of the Chromium source</div><div class="kg-bookmark-description">The official GitHub mirror of the Chromium source. Contribute to chromium/chromium development by creating an account on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">chromium</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/e2c3f40e2ebdcae3ea44f11bcb2ead909fa8b5e6d419759e1f6719b22e04b262/chromium/chromium" alt></div></a><figcaption>The engine that powers Google Chrome and other web browsers</figcaption></figure><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://github.com/ethereum"><div class="kg-bookmark-content"><div class="kg-bookmark-title">ethereum</div><div class="kg-bookmark-description">ethereum has 265 repositories available. Follow their code on GitHub.</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt><span class="kg-bookmark-author">GitHub</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://avatars.githubusercontent.com/u/6250754?s=280&amp;v=4" alt></div></a><figcaption>The many open-source projects that make up the Ethereum protocol and ecosystem</figcaption></figure><h3 id="git-and-github">Git and GitHub</h3><p>Those trying to understand open-source software for the first time often experience a bit of naming confusion among all the products and companies in this particular niche of an industry, so here&apos;s just a quick overview:</p><ul><li><strong>Git</strong> is source control software, used by developers to store, manage, version, and collaborate on source code. It was created by Linus Torvalds (the creator of Linux) and, naturally, Git is <a href="https://github.com/git/git">open-source software</a>. &#xA0;</li><li><strong>GitHub</strong> is a for-profit corporation (owned by Microsoft) that is in the business of providing Git-related products and services, including Git project hosting and open-source developer productivity tools. Although we will be using GitHub to consume and share source code in this course, there certainly are companies that offer similar services, such as GitLab and BitBucket.</li></ul><h3 id="gitgithub-how-to">Git/GitHub How-To</h3><p>Code projects in Git are organized into <em>repositories</em>. Typically, a Git repository houses a single code project, like some of the examples that we&apos;ve discussed thus far. One of the primary services provided by GitHub is <em>Git repository hosting</em>. A GitHub user or organization will have typically have several Git repositories housed within it. For instance, the <a href="https://github.com/microsoft">Microsoft organization on GitHub</a> contains many repositories, one of which is the source code for the popular <a href="https://github.com/microsoft/vscode">Visual Studio Code</a> code editor.</p><p>As a first time software developer and GitHub user, you&apos;re not going to have any repositories within your GitHub account. It&apos;s time to change that.</p><p>There are two primary ways to create our own repository in GitHub:</p><ul><li>Create a repository by clicking the <em>New repository</em> option &#x2013; either start from scratch or from code you already have on your computer, or;</li><li>Go to an existing repository and use it as a template to start your own repository. This is the option we&apos;ll begin with, and will be using quite a bit throughout this course.</li></ul><p>Start by visiting <a href="https://github.com/entr451-spring2026/hello-world">https://github.com/entr451-spring2026/hello-world</a> and clicking the <em>Use this template</em> button. You&apos;ll be directed to a <em>Create a new repository</em> page &#x2013;&#xA0;for the <em>Repository Name</em>, type <code>hello-world</code>, set the repository to be <em>Public</em> and click the <em>Create repository from template</em> button. It should take just a few seconds to create the brand-new repository in your GitHub account. You did it &#x2013;&#xA0;you created your first GitHub repository!</p><p>Have a look around &#x2013; you&apos;ll see that a GitHub repository is nothing more than a folder filled with files and other folders. You&apos;ll find that you can add your files, as well as edit the files already there. Try editing the <code>README.md</code> file by clicking little &quot;pencil&quot; icon. Make any change you want, then scroll to the bottom, to the <em>Commit Changes</em> section. The first box, which is pre-filled with <em>Update README.md, </em>is known as the <em>Commit Message.</em> This message is used to make a note to yourself (and others), describing the change you&apos;ve made. Type something in the commit message (e.g. <em>Made the README more awesome</em>) and click the <em>Commit Changes </em>button. This takes the work you&apos;ve done &#x2013;&#xA0;in this case, making an edit to the <code>README.md</code> file &#x2013;&#xA0;and <em>commits</em> the work. That is, it takes the unit of work you&apos;ve performed and uses it to affect a permanent change to your Git repository.</p><p>It&apos;s important to understand that code and other files that are part of a Git repository are always being watched by Git. Because this constant observation is taking place, Git is able to know the entire history of your code; that is,&#xA0;what files are added, modified, and deleted by you and other contributors over time. This is what gives Git and other <em>version control systems </em>powerful abilities, such as reporting on individuals&apos; contributions to our code project (what was contributed and when), time-travel (the ability to view or revert to our code at a previous period in time), and branching (different versions of the same project that began at the same starting point). Professional software developers use these features (and more) to manage complex software projects each and every day.</p>]]></content:encoded></item><item><title><![CDATA[Hello, World!]]></title><description><![CDATA[<p>Welcome to the course! Over the next 10 weeks, we will be exploring the world of software development from a product perspective. If you&apos;ve ever wondered how a software developer/entrepreneur gets from &quot;idea&quot; to the finished product that people are able to use, we&apos;</p>]]></description><link>https://entr451.com/hello-world/</link><guid isPermaLink="false">61775b4fd9019e19441bbab5</guid><category><![CDATA[Getting Started]]></category><dc:creator><![CDATA[Ghost]]></dc:creator><pubDate>Thu, 29 Dec 2022 23:01:00 GMT</pubDate><content:encoded><![CDATA[<p>Welcome to the course! Over the next 10 weeks, we will be exploring the world of software development from a product perspective. If you&apos;ve ever wondered how a software developer/entrepreneur gets from &quot;idea&quot; to the finished product that people are able to use, we&apos;re going to be pulling back the curtain on the processes, technologies, and tools used to do so.</p><h2 id="course-structure">Course Structure</h2><p>There is a tremendous amount of material covered in this course &#x2013; the end-goal is to take a student who has done little to no software development before, and give them the ability to ship a software-based product by the end of the 10 weeks. To achieve this goal in such a compressed timeline, each week of this course will be broken down into the following:</p><ul><li>3 40-60 minute units, with a 5-10 minute break in-between</li><li>A typical unit will consist of some lecture, some hands-on demonstrations, and some lab exercises</li><li>Practice exercises or a graded assignment to work on at home between classes</li></ul><h2 id="getting-started-%E2%80%93-aka-stuff-to-do-before-the-first-class">Getting Started &#x2013; AKA stuff to do before the first class</h2><p>There are three primary tools we will need to set up before the first class session, so we can hit the ground running:</p><ol><li>A <strong><strong>Web Browser</strong></strong>. We&apos;ll be using Google Chrome. We like Chrome for its fast and consistent performance, and its built-in Developer Tools.</li><li><strong><strong>Git and Github</strong></strong>. <strong><strong>Git</strong></strong> is a technology that professional developers use to store their code and share it with others. <strong><strong>GitHub</strong></strong> is a company that provides a popular hosted Git service. If you are familiar with Dropbox, you can think of GitHub as &quot;Dropbox for code files.&quot;</li><li>A <strong>Development Environment</strong>. The &quot;development environment&quot; is just a fancy way of saying &quot;all the stuff needed to build software&quot;. In the context of this course, this means &#x2013; a computer, operating system, command-line interface, and code editor (something to edit code files). Professional developers often have their own personalized setups for all of this, but in this class, we&apos;ll be doing it all in the cloud, with a tool called <a href="https://github.com/features/codespaces">Github Codespaces</a>. </li></ol><h3 id="step-1-download-and-install-google-chrome">Step 1: Download and Install Google Chrome</h3><p>Maybe you&apos;re already using Chrome to watch cat videos. If not, go get it: <a href="https://www.google.com/chrome">https://www.google.com/chrome.</a></p><h3 id="step-2-github">Step 2: GitHub</h3><p>Sign up for a free GitHub account at <a href="https://github.com/">the GitHub website</a>. <strong>IMPORTANT: please use your Kellogg email address to sign-up.*</strong> &#xA0;You&apos;ll be using your GitHub account for lots of things in class &#x2013; including turning in your homework and doing lab work in class &#x2013; so keep your account information handy at all times.</p><p><em>* If you already have a Github account with a different email address, you&apos;ll need to add your Kellogg email address in <a href="https://github.com/settings/emails">your Github settings</a>, and then set it as your primary email.</em></p><h3 id="step-3-github-codespaces">Step 3: Github Codespaces</h3><p>Go to <a href="https://ona.com/">the Codespaces website</a> and click &quot;Get started for free&quot;.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2026/03/Screenshot-2026-03-21-at-11.27.41-PM.png" class="kg-image" alt loading="lazy" width="2000" height="1118" srcset="https://entr451.com/content/images/size/w600/2026/03/Screenshot-2026-03-21-at-11.27.41-PM.png 600w, https://entr451.com/content/images/size/w1000/2026/03/Screenshot-2026-03-21-at-11.27.41-PM.png 1000w, https://entr451.com/content/images/size/w1600/2026/03/Screenshot-2026-03-21-at-11.27.41-PM.png 1600w, https://entr451.com/content/images/size/w2400/2026/03/Screenshot-2026-03-21-at-11.27.41-PM.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>If you&apos;re not already logged in to your Github account, you&apos;ll first need to sign-in (you already created an account in Step 2).</p><p>Once you&apos;ve authenticated, you&apos;ll see your Codespaces dashboard.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2026/03/Screenshot-2026-03-21-at-11.32.07-PM.png" class="kg-image" alt loading="lazy" width="2000" height="1117" srcset="https://entr451.com/content/images/size/w600/2026/03/Screenshot-2026-03-21-at-11.32.07-PM.png 600w, https://entr451.com/content/images/size/w1000/2026/03/Screenshot-2026-03-21-at-11.32.07-PM.png 1000w, https://entr451.com/content/images/size/w1600/2026/03/Screenshot-2026-03-21-at-11.32.07-PM.png 1600w, https://entr451.com/content/images/size/w2400/2026/03/Screenshot-2026-03-21-at-11.32.07-PM.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>My hope is to keep you on a free tier for the entire quarter. But, depending on your usage, you may need to upgrade at some point - the paid tier (i.e. &quot;Pro&quot;) costs $4/mo. &#xA0;I will provide tips to avoid this, but access to Codespaces is required for the course, so be prepared to upgrade if needed. &#xA0;Once the course is over, you can choose to downgrade/cancel at any time.</p><h2 id="course-resources">Course Resources</h2><h3 id="entr-451-on-github">ENTR-451 on GitHub</h3><p>Our course GitHub page will be the main resource for all the code we&apos;ll be writing in the course. Find it at <a href="https://github.com/entr451-spring2026">github.com/entr451-spring2026</a>.</p><p>You won&apos;t see anything there yet, but we&apos;ll begin most exercises and assignments on Github, so it&apos;s worth bookmarking.</p><h3 id="slack">Slack</h3><p>We will be using Slack for all communication, inside and outside of class. In class, you&apos;ll use Slack to post questions and get help from Faculty Associates and Professors. Outside of class, we&apos;ll use Slack for collaborating on lab exercises and homework assignments, and general questions/conversation.</p><p>We have our own Slack channel (not part of Kellogg/Northwestern&apos;s Slack). Access it via:</p><p><a href="https://entr451.slack.com/">https://entr451.slack.com/</a></p><p><strong>Important:</strong> You will need to first create an account. &#xA0;To do so, click the &quot;Create an account&quot; link in the top right and <strong>use your Kellogg email address</strong> to signup. &#xA0;Once you signup and login, you&apos;ll be dropped into the default <em><em>#</em>spring-2026</em> channel. Say hi!</p><h3 id="canvas">Canvas</h3><p>Every week, links to resources, slides, and code will be posted to the course Canvas page. &#xA0;You will find the content in the <strong>Announcements</strong> and <strong>Assignments</strong> tabs.</p><p>That&apos;s it! You&apos;re ready. See you in class!</p>]]></content:encoded></item><item><title><![CDATA[Twilio API Integration]]></title><description><![CDATA[<h2 id="what-is-twilio"><strong>What is Twilio?</strong></h2><p><a href="https://www.twilio.com/">Twilio</a> is a SaaS (Software as a Service) platform offering programmable telephony services. &#xA0;Twilio allows software developers to programmatically make and receive phone calls, send and receive text messages, and perform other communication functions using its web service APIs.</p><h3 id="initial-setup"><strong>Initial Setup</strong></h3><p>First, you&apos;ll need</p>]]></description><link>https://entr451.com/twilio-api/</link><guid isPermaLink="false">62fa6c9ad9019e19441c2976</guid><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Mon, 15 Aug 2022 18:47:04 GMT</pubDate><content:encoded><![CDATA[<h2 id="what-is-twilio"><strong>What is Twilio?</strong></h2><p><a href="https://www.twilio.com/">Twilio</a> is a SaaS (Software as a Service) platform offering programmable telephony services. &#xA0;Twilio allows software developers to programmatically make and receive phone calls, send and receive text messages, and perform other communication functions using its web service APIs.</p><h3 id="initial-setup"><strong>Initial Setup</strong></h3><p>First, you&apos;ll need to signup for a free Twilio account. &#xA0;Go to <a href="https://www.twilio.com">twilio.com </a>and click &quot;<strong>Signup</strong>&quot;. &#xA0;You&apos;ll enter your name, email, and password. &#xA0;Afterwards, you will need to verify your email address and your cell phone number.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/03/Twilio-Verify-Phone-Number-Screenshot.png" class="kg-image" alt loading="lazy" width="2000" height="912" srcset="https://entr451.com/content/images/size/w600/2022/03/Twilio-Verify-Phone-Number-Screenshot.png 600w, https://entr451.com/content/images/size/w1000/2022/03/Twilio-Verify-Phone-Number-Screenshot.png 1000w, https://entr451.com/content/images/size/w1600/2022/03/Twilio-Verify-Phone-Number-Screenshot.png 1600w, https://entr451.com/content/images/2022/03/Twilio-Verify-Phone-Number-Screenshot.png 2118w" sizes="(min-width: 720px) 720px"></figure><p>During verification, you will receive a text message with a 6-digit verification code (you can request a phone call instead). &#xA0;Afterwards, you&apos;ll be asked a series of questions about how you intend to use Twilio, beginning with &quot;Do you write code?&quot; and in what language. &#xA0;Yes, you code!!! &#xA0;(And in Ruby). &#xA0;There are a few additional questions, but feel free to click &quot;Skip to Dashboard&quot;.</p><p>Ok, up next, you need to get a free trial number to send messages from. &#xA0;You should be on your dashboard at this point. &#xA0;Click &quot;<strong>Get a Trial Number</strong>&quot;.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/03/Twilio-Get-a-Trial-Number-Screenshot.png" class="kg-image" alt loading="lazy" width="2000" height="888" srcset="https://entr451.com/content/images/size/w600/2022/03/Twilio-Get-a-Trial-Number-Screenshot.png 600w, https://entr451.com/content/images/size/w1000/2022/03/Twilio-Get-a-Trial-Number-Screenshot.png 1000w, https://entr451.com/content/images/size/w1600/2022/03/Twilio-Get-a-Trial-Number-Screenshot.png 1600w, https://entr451.com/content/images/size/w2400/2022/03/Twilio-Get-a-Trial-Number-Screenshot.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Twilio gives you a starting balance to play with, so the number will be free. &#xA0;You&apos;ll see the number Twilio suggests to you. &#xA0;You&apos;re able to change it to a number with a specific area code and/or numeric pattern. &#xA0;But, for now, just click &quot;<strong>Choose this Number</strong>&quot; and then &quot;<strong>Done</strong>&quot;.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/03/Twilio-Choose-Trial-Number-Screenshot.png" class="kg-image" alt loading="lazy" width="2000" height="894" srcset="https://entr451.com/content/images/size/w600/2022/03/Twilio-Choose-Trial-Number-Screenshot.png 600w, https://entr451.com/content/images/size/w1000/2022/03/Twilio-Choose-Trial-Number-Screenshot.png 1000w, https://entr451.com/content/images/size/w1600/2022/03/Twilio-Choose-Trial-Number-Screenshot.png 1600w, https://entr451.com/content/images/size/w2400/2022/03/Twilio-Choose-Trial-Number-Screenshot.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>If all goes well, you&apos;ll be back on your dashboard and see your new Twilio number. Finally, it&apos;s time to code!</p><h3 id="adding-the-twilio-ruby-library"><strong>Adding the Twilio Ruby Library</strong></h3><p>Twilio has a ruby library that makes the API integration really easy. &#xA0;In order to use it, you add the following line of code into your <code>Gemfile</code>.</p><pre><code>gem &quot;twilio-ruby&quot;</code></pre><p>Be sure this is not inside of one of the <code>development</code> or <code>test</code> groups - this library is needed in all environments (especially production).</p><p>Once you&apos;ve added that code, you&apos;ll need to tell your application to install this library by running <code>bundle install</code> from your terminal.</p><h3 id="twilio-api-credentials">Twilio API Credentials</h3><p>Next, you&apos;ll need your Twilio API credentials. &#xA0;You can find your credentials on your dashboard.</p><p>You&apos;ll need to add those credentials as environment variables to Gitpod (and later to Heroku). &#xA0;Follow <a href="https://entr451.com/api-keys/">these steps</a> before moving on.</p><p>Once you&apos;ve set up your API credentials per the steps above, whenever you need access to those credentials in your code, you can do so this way:</p><pre><code>twilio_account_sid = ENV[&quot;TWILIO_ACCOUNT_SID&quot;]
twilio_auth_token = ENV[&quot;TWILIO_AUTH_TOKEN&quot;]</code></pre><h3 id="ruby-code">Ruby Code</h3><p>Now that the setup is done, we can use the Twilio library and our API credentials to send a text message (along with other Twilio functionality).</p><p>You can trigger a text message from anywhere in your code - you just need to know the number you want to send it to. &#xA0;For example, let&apos;s say we want to send a welcome text to a user when they successfully complete signup in our application.</p><p>There&apos;s probably a <code>users_controller.rb</code> with a <code>create</code> action that saves a new user to our database. &#xA0;To send the user a text message, we&apos;ll need to know their cell phone, so let&apos;s assume the <code>users</code> table has a <code>phone</code> column and the <code>users/new.html.erb</code> form requires that field. &#xA0;The controller code then looks something like this:</p><pre><code>class UsersController &lt; ApplicationController
  def new
  end

  def create
    @user = User.new
    @user[&quot;first_name&quot;] = params[&quot;first_name&quot;]
    @user[&quot;last_name&quot;] = params[&quot;last_name&quot;]
    @user[&quot;email&quot;] = params[&quot;email&quot;]
    @user[&quot;password&quot;] = BCrypt::Password.create(params[&quot;password&quot;])
    @user[&quot;phone&quot;] = params[&quot;phone&quot;]
    @user.save
    redirect_to &quot;/posts&quot;
  end
end</code></pre><p>So between the <code>save</code> and the <code>redirect</code>, we want to send a text message.</p><p>For the first step, we&apos;ll use the Twilio credentials to connect to the Twilio API.</p><pre><code>twilio_account_sid = ENV[&quot;TWILIO_ACCOUNT_SID&quot;]
twilio_auth_token = ENV[&quot;TWILIO_AUTH_TOKEN&quot;]
client = Twilio::REST::Client.new(twilio_account_sid, twilio_auth_token)</code></pre><h3 id="twilio-free-trial">Twilio Free Trial</h3><p>During your free trial with Twilio, you can only send messages to verified phone numbers. &#xA0;For now, that&apos;s your cell phone that you signed up with. &#xA0;If you&apos;d like to verify additional numbers (like those of your teammates or friends who will be testing your application), you can do so <a href="https://www.twilio.com/console/phone-numbers/verified">here</a> and click the &quot;+&quot; sign.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/03/Twilio-Add-Verified-Number-Screenshot.png" class="kg-image" alt loading="lazy" width="2000" height="677" srcset="https://entr451.com/content/images/size/w600/2022/03/Twilio-Add-Verified-Number-Screenshot.png 600w, https://entr451.com/content/images/size/w1000/2022/03/Twilio-Add-Verified-Number-Screenshot.png 1000w, https://entr451.com/content/images/size/w1600/2022/03/Twilio-Add-Verified-Number-Screenshot.png 1600w, https://entr451.com/content/images/size/w2400/2022/03/Twilio-Add-Verified-Number-Screenshot.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Once you the free trial period ends, you&apos;ll be able to text any user&apos;s number (not just verified numbers). &#xA0;We&apos;ll see below how this impacts the code.</p><h3 id="sending-a-text-message">Sending a text message</h3><p>In the following code, the <em>from</em> is your Twilio trial number (the one on your Twilio dashboard). &#xA0;The <em>to</em> is the number that will receive the text. &#xA0;And the <em>body</em> is the message being sent.</p><p>The <em>from</em> and <em>to</em> numbers should use the <a href="https://www.twilio.com/docs/glossary/what-e164">E.164 format</a> (i.e. <code>[+][country code][number with area code]</code> without any spaces or punctuation). &#xA0;There are many technical ways to ensure this format, but while you&apos;re getting things working, you can just assume the user has entered it correctly.</p><pre><code>client.messages.create(
  from: &quot;+11234567890&quot;,
  to: @user[&quot;phone&quot;],
  body: &quot;Hey, welcome to my application!&quot;
)
</code></pre><p>The above code will send a message to the user&apos;s phone. &#xA0;However, this is when the verified numbers during the free trial period come into play. &#xA0;</p><p>Once everything is working and you&apos;re ready to start paying for real text messages to real users, you can use the above code. &#xA0;However, during the trial period, you should hardcode this number to be your number (or any of the verified numbers you&apos;ve added to Twilio) so that only you or your team receive this text.</p><pre><code>client.messages.create(
  from: &quot;+11234567890&quot;,
  to: &quot;+15555555555&quot;,
  body: &quot;Hey, welcome to my application!&quot;
)
</code></pre><p>Also, note that during the trial period, whatever message body you send will be prepended with <em>&quot;Sent from your Twilio trial account -&quot;</em>.</p><h3 id="completed-code">Completed Code</h3><p>All together it looks like this:</p><pre><code>class UsersController &lt; ApplicationController
  def new
  end

  def create
    @user = User.new
    @user[&quot;first_name&quot;] = params[&quot;first_name&quot;]
    @user[&quot;last_name&quot;] = params[&quot;last_name&quot;]
    @user[&quot;email&quot;] = params[&quot;email&quot;]
    @user[&quot;password&quot;] = BCrypt::Password.create(params[&quot;password&quot;])
    @user[&quot;phone&quot;] = params[&quot;phone&quot;]
    @user.save
    
    twilio_account_sid = ENV[&quot;TWILIO_ACCOUNT_SID&quot;]
    twilio_auth_token = ENV[&quot;TWILIO_AUTH_TOKEN&quot;]
    client = Twilio::REST::Client.new(twilio_account_sid, twilio_auth_token)
    client.messages.create(
        from: &quot;+11234567890&quot;,
        to: &quot;+15555555555&quot;,
        body: &quot;Hey, welcome to my application!&quot;
    )


    redirect_to &quot;/posts&quot;
  end
end
</code></pre><p>Don&apos;t forget to replace the <em>to</em> value with the user&apos;s phone when it&apos;s time to send real texts to real users!</p><p>And that&apos;s it.</p><p>There&apos;s a lot more you can do with the Twilio API. &#xA0;It&apos;s incredibly powerful. &#xA0;And hopefully a reminder that your application&apos;s backend code can do so much more than just expose a webpage.</p><p>This example is sending a text message on signup, but that is likely not your desired use case. &#xA0;So the last step is deciding where in your application a text message feature makes sense - and, therefore, where to put the code. &#xA0;We leave that to you...</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/03/pooh-bear-think.webp" class="kg-image" alt loading="lazy" width="300" height="162"></figure>]]></content:encoded></item><item><title><![CDATA[Securing API Keys]]></title><description><![CDATA[<p>We went over this in class, but here is some documentation on the steps to secure your API keys using environment variables.</p><h4 id="the-problem"><strong>The Problem</strong></h4><p>When first working with an API, it&apos;s often easiest to include your API credentials in your code.</p><p>It might look something like this:</p><pre><code># put</code></pre>]]></description><link>https://entr451.com/api-keys/</link><guid isPermaLink="false">6228b7bdd9019e19441c0fef</guid><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Mon, 15 Aug 2022 17:56:51 GMT</pubDate><content:encoded><![CDATA[<p>We went over this in class, but here is some documentation on the steps to secure your API keys using environment variables.</p><h4 id="the-problem"><strong>The Problem</strong></h4><p>When first working with an API, it&apos;s often easiest to include your API credentials in your code.</p><p>It might look something like this:</p><pre><code># put your API credentials here (usually found on your API dashboard)
account_sid = &quot;YOUR_ACCOUNT_SID&quot;
auth_token = &quot;YOUR_AUTH_TOKEN&quot;</code></pre><p>Exposing your API credentials in a Github repository isn&apos;t the best idea, and it&apos;s a horrible idea if the repository is public! &#xA0;A public Github repository is just that, <strong>public</strong> - anyone can access it. &#xA0;And then they can use your API credentials as they wish. &#xA0;It&apos;s like sharing your account password with the world. &#xA0;#bad</p><p>It&apos;s so bad, in fact, that many API services (Google, Amazon, Twilio, etc) scan public repositories for their API keys and, if they recognize one, they immediately revoke it (making it unusable). &#xA0;If you pushed your code to Github with an API key, you may have received an email explaining this.</p><p>But even if your repository is private, API credentials that are in your code are still exposed to more people than necessary - everyone with access to the repository.</p><h4 id="the-solution"><strong>The Solution</strong></h4><p><em>Note: the following solution is applicable for securing any API keys. &#xA0;The example is for Twilio, but it&apos;s good practice to apply to all credentials that should not be viewable in your repository&apos;s code.</em></p><p>So the solution is to &quot;hide&quot; our API keys by storing them in &quot;environment variables&quot; - variables stored on the &quot;server environment&quot; instead of in our code.</p><p><strong>Step 1 - Replace this code...</strong></p><pre><code># put your API credentials here (found on your Twilio dashboard)
twilio_account_sid = &quot;YOUR_TWILIO_ACCOUNT_SID&quot;
twilio_auth_token = &quot;YOUR_TWILIO_AUTH_TOKEN&quot;</code></pre><p>...with this code, where <code>TWILIO_ACCOUNT_SID</code> is whatever variable you want (it could be <code>TACOS</code>); and same for <code>TWILIO_AUTH_TOKEN</code>.</p><pre><code># put your API credentials here (found on your Twilio dashboard)
twilio_account_sid = ENV[&quot;TWILIO_ACCOUNT_SID&quot;]
twilio_auth_token = ENV[&quot;TWILIO_AUTH_TOKEN&quot;]</code></pre><p>If you have not yet added your API credentials into your code, just note that this is how you will do so when needed and move on to the next step.</p><p><strong>Step 2 - Configure the environment variables in Gitpod:</strong></p><p>To do so, go to <a href="http://gitpod.io/workspaces"><em>gitpod.io/workspaces</em></a> and click on your avatar in the top right. &#xA0;You&apos;ll see a dropdown. &#xA0;Click &quot;Environment Variables&quot;.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/03/Gitpod-Profile-Dropdown-Screenshot.png" class="kg-image" alt loading="lazy" width="2000" height="687" srcset="https://entr451.com/content/images/size/w600/2022/03/Gitpod-Profile-Dropdown-Screenshot.png 600w, https://entr451.com/content/images/size/w1000/2022/03/Gitpod-Profile-Dropdown-Screenshot.png 1000w, https://entr451.com/content/images/size/w1600/2022/03/Gitpod-Profile-Dropdown-Screenshot.png 1600w, https://entr451.com/content/images/2022/03/Gitpod-Profile-Dropdown-Screenshot.png 2284w" sizes="(min-width: 720px) 720px"></figure><p>On the next screen, click &quot;Add Variable&quot;. &#xA0;In the new row, the <em>Name</em> is the variable name you used above (e.g. <code>TWILIO_ACCOUNT_SID</code> or <code>TACOS</code>); the <em>Value</em> is the actual API key from Twilio; and the last column, the <em>Organization/Repository</em> needs to be <code>*/*</code>. &#xA0;Then repeat for the <code>TWILIO_AUTH_TOKEN</code><em>. &#xA0;</em>When you&apos;re done, it should look something like this:</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/03/Gitpod-Environment-Variables-Screenshot.png" class="kg-image" alt loading="lazy" width="2000" height="709" srcset="https://entr451.com/content/images/size/w600/2022/03/Gitpod-Environment-Variables-Screenshot.png 600w, https://entr451.com/content/images/size/w1000/2022/03/Gitpod-Environment-Variables-Screenshot.png 1000w, https://entr451.com/content/images/size/w1600/2022/03/Gitpod-Environment-Variables-Screenshot.png 1600w, https://entr451.com/content/images/2022/03/Gitpod-Environment-Variables-Screenshot.png 2312w" sizes="(min-width: 720px) 720px"></figure><p>If you had your workspace running, you&apos;ll probably need to stop it and restart it so that your &quot;environment&quot; (i.e. workspace) recognizes these new environment variables. &#xA0;But then it should work properly.</p><p><strong>Step 3 - Configure the environment variables in Heroku (optional):</strong></p><p>If you&apos;re deploying your code to Heroku, you&apos;ll need to configure the environment variables there as well. &#xA0;Every &quot;environment&quot; needs to know about these variables.</p><p>Go to <a href="https://dashboard.heroku.com/apps"><em>your Heroku dashboard</em></a> and click on your application. &#xA0;Then navigate to the <em>Settings</em> tab. &#xA0;In the 2nd section, there&apos;s click &quot;Reveal Config Vars&quot;. &#xA0;You&apos;ll see a list of environment variables that are preset by Heroku to make your application work in <em>production</em>. &#xA0;There should be an empty row at the bottom. &#xA0;Fill it in with the same <em>Name</em> and <em>Value</em> that you entered in Gitpod and click &quot;Add&quot;. &#xA0;Repeat so that both Twilio API credentials are configured. &#xA0;It should look something like this when you&apos;re done:</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/03/Heroku-Environment-Variables-Screenshot.png" class="kg-image" alt loading="lazy" width="2000" height="608" srcset="https://entr451.com/content/images/size/w600/2022/03/Heroku-Environment-Variables-Screenshot.png 600w, https://entr451.com/content/images/size/w1000/2022/03/Heroku-Environment-Variables-Screenshot.png 1000w, https://entr451.com/content/images/size/w1600/2022/03/Heroku-Environment-Variables-Screenshot.png 1600w, https://entr451.com/content/images/size/w2400/2022/03/Heroku-Environment-Variables-Screenshot.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>And that&apos;s it!</p>]]></content:encoded></item><item><title><![CDATA[File Attachments]]></title><description><![CDATA[<p>You may, at some point, want to give your users the ability to upload files - images or videos or pdfs or some other file type. &#xA0;As we&apos;ve seen, storing data for a record requires a column in the database and a matching form field. &#xA0;For</p>]]></description><link>https://entr451.com/file-attachments/</link><guid isPermaLink="false">622abfbad9019e19441c106c</guid><category><![CDATA[Potpourri]]></category><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Thu, 02 Jun 2022 20:34:38 GMT</pubDate><content:encoded><![CDATA[<p>You may, at some point, want to give your users the ability to upload files - images or videos or pdfs or some other file type. &#xA0;As we&apos;ve seen, storing data for a record requires a column in the database and a matching form field. &#xA0;For example, if we want to save a user&apos;s name, we need a <code>name</code> column in the <code>users</code> table and a matching <code>text_field &quot;name&quot;</code> in the user form.</p><p>Storing files, however, isn&apos;t as simple. &#xA0;We can&apos;t just store a file in the database - that&apos;s not what database software is built for and it would take up too much space. Instead, files get uploaded to a publicly available computer and the file&apos;s location on that computer is what we store in the database. &#xA0;Sending file data over the internet is a complicated bit of technology, but as with many aspects of our software development journey, we don&apos;t need to solve a problem that&apos;s been already solved - we just need to know when and how to apply the solution.</p><p>For the following examples, we&apos;ll use <a href="https://github.com/entr451-winter2026/tacogram-final">this repository</a> - a final version of our Tacogram application.</p><h2 id="activestorage">ActiveStorage</h2><p>File attachments are so common that the rails framework comes built with a library to handle it called ActiveStorage. &#xA0;If you&apos;re curious about the library, the code lives <a href="https://github.com/rails/rails/tree/main/activestorage">here</a> in the rails codebase. &#xA0;There&apos;s also a thorough description of it in the Rails Guides <a href="https://edgeguides.rubyonrails.org/active_storage_overview.html">here</a>. &#xA0;However, we&apos;ll walk through all the steps below (spoiler alert, there aren&apos;t that many), so the documentation is only useful/relevant when you want to dig in or do something a little out of the ordinary.</p><h3 id="installation-setup">Installation &amp; Setup</h3><p>The first step is to generate a few new tables that the library will need - just to reiterate, the files don&apos;t get stored in the database, but the location of the files do. &#xA0;Stop your server if it was running. &#xA0;Then, from the terminal prompt, run the following command:</p><pre><code>rails active_storage:install</code></pre><p>The output will show a migration file copied over from the library into your <code>db/migrate</code> directory. &#xA0;The file name has the usual migration timestamp followed by <code>_create_active_storage_tables.active_storage.rb</code>. &#xA0;This migration file will create a few tables, but, as with all migration files, they need to be executed to take effect. &#xA0;So the next step is:</p><pre><code>rails db:migrate</code></pre><p>Now if you check the schema, you&apos;ll see 3 new tables:</p><ul><li><code>active_storage_attachments</code></li><li><code>active_storage_blobs</code></li><li><code>active_storage_variant_records</code></li></ul><p>We won&apos;t really think much about these tables - the library will be responsible for using them - but it&apos;s good to know they exist.</p><p>Next, open the file <code>config/storage.yml</code>. &#xA0;This is a file we provided, but it never really differs from this, so you can copy it into any future applications. &#xA0;A <code>.yml</code> file is a list of key-value pairs that is most often used for application configuration details. &#xA0;Here we have a <code>:local</code> storage configuration which will store files into the server&apos;s file system.</p><pre><code>local:
  service: Disk
  root: &lt;%= Rails.root.join(&quot;storage&quot;) %&gt;</code></pre><p>The last setup step is to use this storage configuration in each environment running our application. &#xA0;In the <code>config/environments</code> directory, open the file <code>development.rb</code>.</p><p>Development is the name of the &quot;environment&quot; used when we&apos;re building an application. &#xA0;It&apos;s our Gitpod workspace or your personal computer. &#xA0;It&apos;s where we &quot;develop&quot;. &#xA0;Production is the name of the &quot;environment&quot; where our application lives on the internet so that users can use it. &#xA0;It&apos;s the &quot;live&quot; version of our application. &#xA0;This <code>development.rb</code> file includes the configuration decisions for your application when it runs in the <em>development</em> environment.</p><p>At the bottom of the file, just before the closing <code>end</code>, add this line of code to indicate that we&apos;ll use the <code>:local</code> storage option in development.</p><pre><code>config.active_storage.service = :local</code></pre><p>Open up the <code>production.rb</code> configuration file and add the same code just before the closing <code>end</code>.</p><p>The ActiveStorage setup is done. &#xA0;Configuration files load once when your server starts, which is why we haven&apos;t started the server yet. &#xA0;You can start your rails server now and run the application. &#xA0;Next we need to use the library in our code.</p><h3 id="file-attachments">File Attachments</h3><p>In the Tacogram application, we&apos;ve been storing a post&apos;s image as a url in the <code>image</code> column of the <code>posts</code> table. &#xA0;Instead, we want to upload an image file, so we need to augment the <code>Post</code> model with the ability to do so (using the ActiveStorage library). &#xA0;Open up the <code>models/post.rb</code> file and add this code:</p><pre><code>class Post &lt; ApplicationRecord
  has_one_attached :uploaded_image
end</code></pre><p>The <code>has_one_attached</code> method is given to us by ActiveStorage as a way of adding this upload ability. &#xA0;We can use any name we want to reference the attachment (similar to the name of a column in the table) - <code>uploaded_image</code> is descriptive, but name it whatever you want as long as it doesn&apos;t conflict with any column names in the <code>posts</code> table (e.g. &quot;image&quot; is already used as a column name).</p><p>Now that we&apos;ve given a <code>post</code> the ability to store an <code>uploaded_image</code> file, we need to modify the form so that a user can do so. &#xA0;In <code>views/posts/new.html.erb</code>, first make sure the <code>&lt;form&gt;</code> tag can support file uploading - update it with <code>enctype=&quot;multipart/form-data&quot;</code> so that it looks like this, if it doesn&apos;t already:</p><pre><code class="language-html">&lt;form action=&quot;/posts&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;</code></pre><p>Then, add a new label and input for the <code>uploaded_image</code>:</p><pre><code class="language-html">&lt;div class=&quot;mb-3&quot;&gt;
  &lt;label for=&quot;uploaded_image_input&quot; class=&quot;form-label&quot;&gt;Upload Image&lt;/label&gt;
  &lt;input type=&quot;file&quot; name=&quot;uploaded_image&quot; id=&quot;uploaded_image_input&quot; class=&quot;form-control&quot;&gt;
&lt;/div&gt;</code></pre><p>Note that the label and input name match the name we used in the model, <code>uploaded_image</code>. &#xA0;Also note that the input type is not <code>text</code>, but instead <code>file</code> which tells the browser to display this form input differently. &#xA0;Refresh your browser to see the new input.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/06/Screen-Shot-2022-06-02-at-2.54.42-PM.png" class="kg-image" alt loading="lazy" width="828" height="326" srcset="https://entr451.com/content/images/size/w600/2022/06/Screen-Shot-2022-06-02-at-2.54.42-PM.png 600w, https://entr451.com/content/images/2022/06/Screen-Shot-2022-06-02-at-2.54.42-PM.png 828w" sizes="(min-width: 720px) 720px"></figure><p>If you click on &quot;Choose File&quot;, you&apos;ll be given the opportunity to browse and select a file on your device to upload. &#xA0;You may notice, however, that you can choose any file. &#xA0;For this application, we only want the user to upload images. &#xA0;We can narrow down the user&apos;s visible options by adding an option to the form field.</p><pre><code class="language-html">&lt;div class=&quot;mb-3&quot;&gt;
  &lt;label for=&quot;uploaded_image_input&quot; class=&quot;form-label&quot;&gt;Upload Image&lt;/label&gt;
  &lt;input type=&quot;file&quot; name=&quot;uploaded_image&quot; id=&quot;uploaded_image_input&quot; class=&quot;form-control&quot; accept=&quot;image/png, image/jpg, image/jpeg, image/gif&quot;&gt;
&lt;/div&gt;</code></pre><p>The <code>accept</code> attribute has a string value with the file types the user can select. &#xA0;A clever user can modify the html (see the frontend CSS lesson) so this doesn&apos;t protect our application very well, but it does improve the user&apos;s experience so that they don&apos;t unintentionally make a mistake. &#xA0;Here we&apos;ve accepted several image file types. &#xA0;If you&apos;re looking for others, read the <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept">MDN documentation</a> or google for &quot;list of mime types&quot;.</p><p>Now when you submit a new post and look at the server log, you&apos;ll see new params related to this <code>uploaded_image</code> field. &#xA0;Our <code>create</code> action in the posts controller isn&apos;t currently handling these params. &#xA0;Open up the <code>controllers/posts_controller.rb</code> file so that we can modify the code. &#xA0;Instead of the code that assigns the url to the <code>image</code> column, use this code to attach the file</p><pre><code>@post.uploaded_image.attach(params[&quot;uploaded_image&quot;])</code></pre><p>The full <code>create</code> action now looks like this:</p><pre><code>def create
  @user = User.find_by({ &quot;id&quot; =&gt; session[&quot;user_id&quot;] })
  if @user != nil
    @post = Post.new
    @post[&quot;body&quot;] = params[&quot;body&quot;]
    @post.uploaded_image.attach(params[&quot;uploaded_image&quot;])
    @post[&quot;user_id&quot;] = @user[&quot;id&quot;]
    @post.save
  else
    flash[&quot;notice&quot;] = &quot;Login first.&quot;
  end
  redirect_to &quot;/posts&quot;
end</code></pre><p>The <code>@post.uploaded_image.attach()</code> code is functionality we enabled when we added <code>has_one_attached</code> to the <code>Post</code> model. &#xA0;The argument we&apos;re passing into the parentheses is the <code>params</code> data from the form which includes the file selected by the user.</p><p>Go back to the new post form and submit another post with an image file. &#xA0;You&apos;ll notice the index view of the posts does not show the uploaded image. &#xA0;That&apos;s because it&apos;s still trying to read the url from the <code>image</code> column in the table. &#xA0;So this is the last bit of code we need to change.</p><p>Open the <code>views/posts/index.html.erb</code> file. &#xA0;The code currently displaying images is an <code>&lt;img&gt;</code> html element with an <code>src</code> attribute value of the post&apos;s <code>image</code> column.</p><pre><code>&lt;img src=&quot;&lt;%= post[&quot;image&quot;] %&gt;&quot; class=&quot;img-fluid&quot;&gt;</code></pre><p>That&apos;s the old functionality. &#xA0;We want to display images using the new functionality. &#xA0;We could just modify this code, however, that won&apos;t account for any existing data that was created using the old way. &#xA0;Instead, the code should handle data created the old way (aka &quot;legacy data&quot;) and data created the new way. &#xA0;Sounds like conditional logic.</p><p>The condition we&apos;ll check for is if the post has an attached <code>uploaded_image</code>:</p><pre><code>&lt;% if post.uploaded_image.attached? %&gt;</code></pre><p>The <code>attached?</code> method will return true or false and is more functionality enabled by that <code>has_one_attached</code> change we made to the model.</p><p>If it&apos;s attached, display it, otherwise, display the <code>image</code> column.</p><pre><code>&lt;% if post.uploaded_image.attached? %&gt;
  ...display the attached image file
&lt;% else %&gt;
  &lt;img src=&quot;&lt;%= post[&quot;image&quot;] %&gt;&quot; class=&quot;img-fluid&quot;&gt;
&lt;% end %&gt;
</code></pre><p>It would be nice if it was as simple as <code>post[&quot;uploaded_image&quot;]</code>, but again remember that we&apos;re not reading a column from a table. &#xA0;Instead, we need to find where this file exists on the server&apos;s computer (or anywhere on the internet for that matter) and then use that url. &#xA0;The code is <code>url_for(post.uploaded_image)</code>. &#xA0;And the final code looks like this:</p><pre><code>&lt;% if post.uploaded_image.attached? %&gt;
  &lt;img src=&quot;&lt;%= url_for(post.uploaded_image) %&gt;&quot; class=&quot;img-fluid&quot;&gt;
&lt;% else %&gt;
  &lt;img src=&quot;&lt;%= post[&quot;image&quot;] %&gt;&quot; class=&quot;img-fluid&quot;&gt;
&lt;% end %&gt;
</code></pre><p>Refresh the browser and you should now see the uploaded image file. &#xA0;If you inspect the html for each post, you&apos;ll notice that the <code>src</code> value for the old posts are the urls submitted via the form, but the <code>src</code> value for new posts are all uniform.</p><pre><code>/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--0bb9db65c8a23883995f028cdb4bea3a68fde563/your_image.jpeg</code></pre><p>They&apos;re stored on the Gitpod server&apos;s machine within a subdirectory of our rails app.</p><h3 id="technical-debt">Technical Debt</h3><p>This conditional logic is what we call &quot;technical debt&quot; - code that solves a problem but should be cleaned up eventually. &#xA0;Since we&apos;re no longer supporting the feature of simple image urls for posts, it would be best to migrate the legacy data so that all records use the new behavior. &#xA0;This will help with maintainability over time and avoid fragility in our code. &#xA0;But modifying the legacy data is not a simple task and is not without risk. &#xA0;We could deal with it now, but that would block this new feature. &#xA0;So instead we decide to deal with it later, or &quot;pay off that technical debt&quot; later and use a simple fix for now.</p><p>Technical debt is a reality of any application as it evolves - it&apos;s not a bad thing, just a reality that we need to be aware of and make time for when possible. &#xA0;</p>]]></content:encoded></item><item><title><![CDATA[Authorization]]></title><description><![CDATA[<p>If you haven&apos;t already, first read the <a href="https://entr451.com/cookies/">previous post</a> about Cookies.</p><p>Authorization refers to what a user can do once they&apos;re authenticated. &#xA0;It also can include what a user sees (and also what a non-user doesn&apos;t see). &#xA0;Let&apos;s start with</p>]]></description><link>https://entr451.com/authorization/</link><guid isPermaLink="false">6285c7fbd9019e19441c2476</guid><category><![CDATA[Application Security]]></category><dc:creator><![CDATA[Ben Block]]></dc:creator><pubDate>Thu, 19 May 2022 19:37:42 GMT</pubDate><content:encoded><![CDATA[<p>If you haven&apos;t already, first read the <a href="https://entr451.com/cookies/">previous post</a> about Cookies.</p><p>Authorization refers to what a user can do once they&apos;re authenticated. &#xA0;It also can include what a user sees (and also what a non-user doesn&apos;t see). &#xA0;Let&apos;s start with that. &#xA0;Let&apos;s look at that navbar again and hide the login/signup links when a user is logged-in. &#xA0;And similarly, we&apos;ll hide the current user&apos;s name if there is no current user. &#xA0;Open the <code>/app/views/layouts/application.html.erb</code> file and add some conditional logic based on the condition <code>if @current_user</code>.</p><pre><code>&lt;p&gt;
  &lt;% if @current_user %&gt;
    &lt;%= @current_user[&quot;first_name&quot;] %&gt;
  &lt;% else %&gt;
    &lt;a href=&quot;/sessions/new&quot;&gt;Login&lt;/a&gt; |
    &lt;a href=&quot;/users/new&quot;&gt;Signup&lt;/a&gt;
  &lt;% end %&gt;
&lt;/p&gt;</code></pre><p>Refresh the browser and you should no longer see the login and signup links.</p><p>To test the <code>else</code> condition, we need to logout the current user. &#xA0;To do that, let&apos;s add a <code>destroy</code> action to the sessions controller that will &quot;delete&quot; the current session. &#xA0;Typically a <code>destroy</code> action will delete a row from a table, but in this case, we just want to &quot;delete&quot; the cookie so that the browser no longer &quot;remembers&quot; that a user is logged-in. &#xA0;We can do this by adding the <code>destroy</code> action and then re-assigning the value of the cookie as <code>nil</code> which effectively will mean there is no current user.</p><pre><code>def destroy
  flash[&quot;notice&quot;] = &quot;Goodbye.&quot;
  session[&quot;user_id&quot;] = nil
  redirect_to &quot;/sessions/new&quot;
end</code></pre><p>The url we would normally use to get to this path would look something like a <code>DELETE</code> request to <code>/tacos/123</code>where <code>123</code> is the <code>id</code> of the row we want to delete. &#xA0;But with a session, there isn&apos;t an id, so this RESTful path doesn&apos;t really make sense. &#xA0;Occasionally, when the REST pattern doesn&apos;t fit, we add a custom path and tell our app where to direct the request to. &#xA0;Open up the routes file <code>/config/routes.rb</code> and add the following code at the bottom below the resources.</p><pre><code>get(&quot;/logout&quot;, {:controller =&gt; &quot;sessions&quot;, :action =&gt; &quot;destroy&quot;})</code></pre><p>This code is registering the <code>/logout</code> path as a <code>GET</code> request. &#xA0;When our application server receives that request, it will direct it to the <code>destroy</code> action within the <code>sessions</code> controller. &#xA0;While we&apos;re at it, let&apos;s add a custom path for login as well, just to keep some nice naming consistency.</p><pre><code>get(&quot;/login&quot;, {:controller =&gt; &quot;sessions&quot;, :action =&gt; &quot;new&quot;})</code></pre><p>The entire routes file now looks like this:</p><pre><code>Rails.application.routes.draw do
  resources &quot;companies&quot;
  resources &quot;contacts&quot;
  resources &quot;activities&quot;
  resources &quot;tasks&quot;
  resources &quot;users&quot;

  resources &quot;sessions&quot;
  get(&quot;/login&quot;, {:controller =&gt; &quot;sessions&quot;, :action =&gt; &quot;new&quot;})
  get(&quot;/logout&quot;, {:controller =&gt; &quot;sessions&quot;, :action =&gt; &quot;destroy&quot;})

  # Landing page (aka root route)
  # get(&quot;/&quot;, {:controller =&gt; &quot;&quot;, :action =&gt; &quot;&quot;})
end</code></pre><p>Now that the application can receive this &quot;/logout&quot; request, let&apos;s add a logout link to the navbar. &#xA0;We can also modify the login link there as well.</p><pre><code>&lt;p&gt;
  &lt;% if @current_user %&gt;
    &lt;%= @current_user[&quot;first_name&quot;] %&gt;
    &lt;a href=&quot;/logout&quot;&gt;Logout&lt;/a&gt;
  &lt;% else %&gt;
    &lt;a href=&quot;/login&quot;&gt;Login&lt;/a&gt; |
    &lt;a href=&quot;/users/new&quot;&gt;Signup&lt;/a&gt;
  &lt;% end %&gt;
&lt;/p&gt;</code></pre><p>Refresh your browser and you should see the Logout link alongside the current user&apos;s name. &#xA0;Click it and the sessions <code>destroy</code> action will reset the cookie so that the current user is &quot;logged-out&quot; and redirected to login.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/05/Screen-Shot-2022-05-18-at-2.39.12-PM.png" class="kg-image" alt loading="lazy" width="1096" height="480" srcset="https://entr451.com/content/images/size/w600/2022/05/Screen-Shot-2022-05-18-at-2.39.12-PM.png 600w, https://entr451.com/content/images/size/w1000/2022/05/Screen-Shot-2022-05-18-at-2.39.12-PM.png 1000w, https://entr451.com/content/images/2022/05/Screen-Shot-2022-05-18-at-2.39.12-PM.png 1096w" sizes="(min-width: 720px) 720px"></figure><p>Login again, and you&apos;ll be back at the companies index and see the current user&apos;s name and logout link. &#xA0;We are now &quot;authorizing&quot; what a logged-in user can see vs a logged-out user (aka &quot;visitor&quot;).</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/05/Screen-Shot-2022-05-18-at-11.22.00-PM.png" class="kg-image" alt loading="lazy" width="1126" height="458" srcset="https://entr451.com/content/images/size/w600/2022/05/Screen-Shot-2022-05-18-at-11.22.00-PM.png 600w, https://entr451.com/content/images/size/w1000/2022/05/Screen-Shot-2022-05-18-at-11.22.00-PM.png 1000w, https://entr451.com/content/images/2022/05/Screen-Shot-2022-05-18-at-11.22.00-PM.png 1126w" sizes="(min-width: 720px) 720px"></figure><h3 id="activities">Activities</h3><p>In our CRM domain model, there are <code>companies</code> and <code>contacts</code> and <code>salespeople</code> (i.e. <code>users</code>). &#xA0;There are also <code>activities</code> which are related to both <code>contacts</code> and <code>users</code>. &#xA0;This app came seeded with a couple rows in the <code>activities</code> table. &#xA0;Navigate to the &quot;Apple&quot; show page and then the &quot;Tim Cook&quot; show page. &#xA0;There you&apos;ll see a couple activities.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/05/Screen-Shot-2022-05-18-at-11.48.40-PM.png" class="kg-image" alt loading="lazy" width="1270" height="1142" srcset="https://entr451.com/content/images/size/w600/2022/05/Screen-Shot-2022-05-18-at-11.48.40-PM.png 600w, https://entr451.com/content/images/size/w1000/2022/05/Screen-Shot-2022-05-18-at-11.48.40-PM.png 1000w, https://entr451.com/content/images/2022/05/Screen-Shot-2022-05-18-at-11.48.40-PM.png 1270w" sizes="(min-width: 720px) 720px"></figure><p>Firstly, if a user is not currently logged-in, they should not be able to see or add activities. &#xA0;One could argue, they shouldn&apos;t be able to see contacts or companies either for that matter, but let&apos;s just focus on activities for now since activities are very explicitly user-related. &#xA0;</p><p>So we should hide anything in this view that only logged-in users should be able to access. &#xA0;Open up this file (<code>/app/views/contacts/show.html.erb</code>) and let&apos;s wrap the relevant code in an <code>if @current_user</code> conditional block.</p><pre><code>&lt;% if @current_user %&gt;
  &lt;p&gt;&lt;a href=&quot;/contacts/&lt;%= @contact[&quot;id&quot;] %&gt;/edit&quot;&gt;Edit Contact&lt;/a&gt;&lt;/p&gt;

  &lt;form action=&quot;/contacts/&lt;%= @contact[&quot;id&quot;] %&gt;&quot; method=&quot;post&quot;&gt;
    &lt;input type=&quot;hidden&quot; name=&quot;_method&quot; value=&quot;delete&quot;&gt;
    &lt;button&gt;Delete Contact&lt;/button&gt;
  &lt;/form&gt;

  &lt;h3&gt;Sales Activity&lt;/h3&gt;

  &lt;ul&gt;
    &lt;% for activity in @activities %&gt;
      &lt;li&gt;
        &lt;%= activity[&quot;activity_type&quot;] %&gt;
        &lt;br&gt;
        &lt;%= activity[&quot;note&quot;] %&gt;
      &lt;/li&gt;
    &lt;% end %&gt;
  &lt;/ul&gt;

  &lt;h4&gt;Log Activity:&lt;/h4&gt;

  &lt;form action=&quot;/activities&quot; method=&quot;post&quot;&gt;
    &lt;p&gt;
      &lt;select name=&quot;activity_type&quot; id=&quot;activity_type_select&quot;&gt;
        &lt;option value=&quot;call&quot;&gt;call&lt;/option&gt;
        &lt;option value=&quot;email&quot;&gt;email&lt;/option&gt;
        &lt;option value=&quot;meeting&quot;&gt;meeting&lt;/option&gt;
      &lt;/select&gt;
      with &lt;%= @contact[&quot;first_name&quot;] %&gt; &lt;%= @contact[&quot;last_name&quot;] %&gt;
    &lt;/p&gt;

    &lt;p&gt;
      &lt;label for=&quot;note_input&quot;&gt;Note&lt;/label&gt;
      &lt;textarea name=&quot;note&quot; id=&quot;note_input&quot;&gt;&lt;/textarea&gt;
    &lt;/p&gt;

    &lt;input type=&quot;hidden&quot; name=&quot;contact_id&quot; value=&quot;&lt;%= @contact[&quot;id&quot;] %&gt;&quot; id=&quot;contact_id_input&quot;&gt;

    &lt;button&gt;Submit&lt;/button&gt;
  &lt;/form&gt;
&lt;% end %&gt;</code></pre><p>Now the activity data and the new activity form will only appear for a logged-in user (and for good measure, we&apos;ll also hide the contact edit and delete links since non-users shouldn&apos;t be able to access those either). &#xA0;If you haven&apos;t done so, logout and refresh the page to confirm this is working, and then login again.</p><p>This is an improvement. &#xA0;But the activities we see here aren&apos;t our activities - as in, they&apos;re not the activities for this current user.</p><p>If you look at this data in the database, you&apos;ll notice that these activities are related to a contact (i.e. their <code>contact_id</code> column is the <code>id</code> for &quot;Tim Cook&quot;), but they are not related to a user (i.e. their <code>user_id</code> column is nil). &#xA0;Add a new activity using the form and the same will be true - the activity does not know that it&apos;s related to the current user.</p><p>Now that we have awareness of a current user, when we create a new activity, it should be assigned to this user. &#xA0;To make that change, we need to go to the code where the activity gets created. &#xA0;We could add a hidden form input and add the current user&apos;s id there, but html is easily manipulated by anyone who knows how to view source. &#xA0;So that wouldn&apos;t be very secure. &#xA0;Instead, we&apos;ll do this in the backend code that only us developers can access. &#xA0;The new activity gets created in the activities controller&apos;s <code>create</code> action. &#xA0;Let&apos;s look at that code:</p><pre><code>class ActivitiesController &lt; ApplicationController
  def create
    @activity = Activity.new
    @activity[&quot;contact_id&quot;] = params[&quot;contact_id&quot;]
    @activity[&quot;activity_type&quot;] = params[&quot;activity_type&quot;]
    @activity[&quot;note&quot;] = params[&quot;note&quot;]
    @activity.save
    redirect_to &quot;/contacts/#{@activity[&quot;contact_id&quot;]}&quot;
  end
end</code></pre><p>All of the form data is being assigned properly, but there&apos;s another column in the activities table that&apos;s not being assigned: <code>user_id</code>. &#xA0;So let&apos;s just add a line of code to assign that as the current user&apos;s <code>id</code>:</p><pre><code>def create
  @activity = Activity.new
  @activity[&quot;contact_id&quot;] = params[&quot;contact_id&quot;]
  @activity[&quot;activity_type&quot;] = params[&quot;activity_type&quot;]
  @activity[&quot;note&quot;] = params[&quot;note&quot;]
  @activity[&quot;user_id&quot;] = @current_user[&quot;id&quot;]
  @activity.save
  redirect_to &quot;/contacts/#{@activity[&quot;contact_id&quot;]}&quot;
end</code></pre><p>That&apos;s it. &#xA0;As you&apos;re seeing, with access to the <code>@current_user</code> record, we&apos;re able to build logic and add context throughout the application - it&apos;s really becoming a complete piece of software!</p><p>Go back to the activities form and submit another activity. &#xA0;You&apos;ll see it displayed in the list of activities just as the other records are, but the data in the table has changed in a very meaningful way. &#xA0;The previous rows have an empty value (i.e. <code>nil</code>) in the <code>user_id</code> column. &#xA0;But this new row has the current user&apos;s id in it. &#xA0;If you were to logout and login as another user, and then create another activity, that row would have a different value in its <code>user_id</code> column. &#xA0;Activities are now related to both contacts and users.</p><p>There&apos;s one last improvement we can make. &#xA0;Instead of displaying all of the activities here, we should only display the current user&apos;s activities. &#xA0;The activities are displayed in a loop in the contact&apos;s show view (the same file we modified above with the <code>if @current_user</code> logic).</p><pre><code>&lt;ul&gt;
  &lt;% for activity in @activities %&gt;
    &lt;li&gt;
      &lt;%= activity[&quot;activity_type&quot;] %&gt;
      &lt;br&gt;
      &lt;%= activity[&quot;note&quot;] %&gt;
    &lt;/li&gt;
  &lt;% end %&gt;
&lt;/ul&gt;</code></pre><p>Since we&apos;re in the contact&apos;s show view, the <code>@activities</code> variable must be defined in the contacts controller&apos;s <code>show</code> action:</p><pre><code>@activities = Activity.where({ &quot;contact_id&quot; =&gt; @contact[&quot;id&quot;] })</code></pre><p>This is querying the activities table for all rows where the <code>contact_id</code> column value is this contact&apos;s <code>id</code> (in the example above, that&apos;s Tim Cook&apos;s <code>id</code>). &#xA0;We can refine the query to also filter by the current user&apos;s id so that only this user&apos;s activities are returned:</p><pre><code>@activities = Activity.where({ &quot;contact_id&quot; =&gt; @contact[&quot;id&quot;], &quot;user_id&quot; =&gt; @current_user[&quot;id&quot;] })</code></pre><p>Now the SQL query will filter by <code>contact_id</code> AND <code>user_id</code>. &#xA0;Refresh the browser and you should only see the activity you created as this user. &#xA0;You can also look at the server log to see the SQL that gets executed.</p><p>There&apos;s one possible problem. &#xA0;If you logout and return to Tim Cook&apos;s page, previously we were hiding the sales activity and form, but you could still see Tim Cook&apos;s details. &#xA0;However, now you&apos;ll see an error:</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/05/Screen-Shot-2022-05-19-at-1.15.49-PM.png" class="kg-image" alt loading="lazy" width="2000" height="754" srcset="https://entr451.com/content/images/size/w600/2022/05/Screen-Shot-2022-05-19-at-1.15.49-PM.png 600w, https://entr451.com/content/images/size/w1000/2022/05/Screen-Shot-2022-05-19-at-1.15.49-PM.png 1000w, https://entr451.com/content/images/size/w1600/2022/05/Screen-Shot-2022-05-19-at-1.15.49-PM.png 1600w, https://entr451.com/content/images/2022/05/Screen-Shot-2022-05-19-at-1.15.49-PM.png 2086w" sizes="(min-width: 720px) 720px"></figure><p>The bug is due to the fact that <code>@current_user</code> is nil when logged-out. &#xA0;And you can&apos;t try to read the <code>id</code> column of nothing. &#xA0;It&apos;s a <em>possible</em> problem because maybe a logged-out user shouldn&apos;t ever be able to get to this page in the first place. &#xA0;We could conditionally check <code>if @current_user</code> in this action and simply redirect a logged-out user. &#xA0;But, to maintain the functionality that we had before, let&apos;s just account for the possibility of a logged-out user. &#xA0;The simplest way to do this is to instead use the value from the <code>session</code> hash which won&apos;t produce an error, but will simply be <code>nil</code> if there is no logged-in user. &#xA0;Here&apos;s the modified code:</p><pre><code>@activities = Activity.where({ &quot;contact_id&quot; =&gt; @contact[&quot;id&quot;], &quot;user_id&quot; =&gt; session[&quot;user_id&quot;] })</code></pre><p>And now you can refresh the page and no more error. &#xA0;Login again, and you&apos;ll only see the activities for this current user. &#xA0;Success!</p><p>Besides the concept of <code>@current_user</code> and how it gets assigned in a <code>before_action</code> method, you may have noticed that the rest of this authorization code is really no different from anything we&apos;ve done before - authorization at its core is just the use of conditionally logic, sql queries, and column assignment. &#xA0;Look at the code we used to add user context to the application:</p><ul><li><code>if @current_user</code></li><li><code>@activity[&quot;user_id&quot;] = @current_user[&quot;id&quot;]</code></li><li><code>Activity.where({ &quot;user_id&quot; =&gt; session[&quot;user_id&quot;] })</code></li></ul><p>With those tools, you have almost infinite control over the authorization logic in your applications. &#xA0;With that said, let&apos;s practice with a new resource: todos.</p><h3 id="todos">Todos</h3><p>If the canonical first app in every language is <code>&quot;Hello, world&quot;</code>, then the canonical second app is almost always a list of todos. &#xA0;So let&apos;s check that box (pun intended) for you.</p><p>This repository already has some MVC (model-view-controller) architecture for a <code>tasks</code> resource - tasks/todos, tomato/tomahto. &#xA0;There is a <code>Task</code> model and table - the <code>tasks</code> table has columns for <code>id</code>, <code>description</code>, and <code>user_id</code>. &#xA0;The routes file is exposing the tasks resource. &#xA0;There is a <code>tasks_controller.rb</code> file with <code>index</code>, <code>create</code>, and <code>destroy</code> actions - the <code>destroy</code> is used when a task is &quot;completed&quot;. &#xA0;And there is a tasks <code>index.html.erb</code> file which includes the new task form and a loop of <code>@tasks</code>.</p><p>Take a look through the code and then visit the <code>/tasks</code> path (i.e. the tasks index view) in the browser.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/05/Screen-Shot-2022-05-19-at-1.35.01-PM.png" class="kg-image" alt loading="lazy" width="1034" height="372" srcset="https://entr451.com/content/images/size/w600/2022/05/Screen-Shot-2022-05-19-at-1.35.01-PM.png 600w, https://entr451.com/content/images/size/w1000/2022/05/Screen-Shot-2022-05-19-at-1.35.01-PM.png 1000w, https://entr451.com/content/images/2022/05/Screen-Shot-2022-05-19-at-1.35.01-PM.png 1034w" sizes="(min-width: 720px) 720px"></figure><p>And try submitting a new task.</p><figure class="kg-card kg-image-card"><img src="https://entr451.com/content/images/2022/05/Screen-Shot-2022-05-19-at-1.40.07-PM.png" class="kg-image" alt loading="lazy" width="1006" height="426" srcset="https://entr451.com/content/images/size/w600/2022/05/Screen-Shot-2022-05-19-at-1.40.07-PM.png 600w, https://entr451.com/content/images/size/w1000/2022/05/Screen-Shot-2022-05-19-at-1.40.07-PM.png 1000w, https://entr451.com/content/images/2022/05/Screen-Shot-2022-05-19-at-1.40.07-PM.png 1006w" sizes="(min-width: 720px) 720px"></figure><p>This is a weird todos app as-is. &#xA0;New tasks don&apos;t have any context of a user, so if you were to ship this app, it would be a global/public todo app. &#xA0;Maybe an interesting social experiment, but not really useful for personal productivity. &#xA0;To fix this, implement the following user stories. &#xA0;Since we&apos;re still in the same repository with all of the existing authentication code, you only need to focus on authorization logic.</p><h4 id="labuser-stories">Lab - User Stories</h4><ul><li>As an anonymous user, I cannot create new tasks.</li><li>As a signed-in user, I want to create new tasks.</li><li>As a signed-in user, I want to see my tasks.</li><li>As a signed-in user, I want to be able to complete my tasks.</li></ul><p>The steps to complete these are below, but try to work through it yourself before looking.</p><h4 id="labsolution">Lab - Solution</h4><p><u>First user story:</u></p><blockquote>As an anonymous user, I cannot create new tasks.</blockquote><p>Currently, anyone can submit the new task form even if logged-out. &#xA0;We can change that by hiding the form in the index view file (<code>/app/views/tasks/index.html.erb</code>):</p><pre><code>&lt;% if @current_user %&gt;
  &lt;form action=&quot;/tasks&quot; method=&quot;post&quot;&gt;
    &lt;input type=&quot;text&quot; name=&quot;description&quot;&gt;
    &lt;button&gt;Add new task&lt;/button&gt;
  &lt;/form&gt;
&lt;% end %&gt;</code></pre><p>If we want to be helpful to the user, we can add an <code>else</code> condition that says something like &quot;You must login to add todos&quot;. &#xA0;That&apos;s up to you. &#xA0;Reload the page and the form should now be hidden. &#xA0;User story &#x2705; complete.</p><p><u>Second user story:</u></p><blockquote>As a signed-in user, I want to create new tasks.</blockquote><p>Login as a user and navigate back to the tasks index page. &#xA0;The task form is there. &#xA0;At first glance, this user story is already done. &#xA0;However, this is where these user stories are a bit too vague. &#xA0;A better user story would say something like:</p><p>&quot;As a signed-in user, I want to create new tasks, so that I can keep track of things I need to do&quot;.</p><p>Thinking like a developer now, we can interpret a bit more clearly that these tasks will need to be related <strong>in the data</strong> to the current user. &#xA0;To implement this, we need to go to the code where the task gets created, the tasks controller <code>create</code> action.</p><pre><code>  def create
    @task = Task.new
    @task[&quot;description&quot;] = params[&quot;description&quot;]
    @task.save
    redirect_to &quot;/tasks&quot;
  end</code></pre><p>Recall that a task has a <code>description</code> and a <code>user_id</code> column. &#xA0;The former is assigned based on the submitted form data. &#xA0;So we just need to assign the latter.</p><pre><code>@task[&quot;user_id&quot;] = @current_user[&quot;id&quot;]</code></pre><p>Now when the a new task gets created, it will be related to the current user so that the user can &quot;keep track of the things [they] need to do&quot;. &#xA0;User story &#x2705; complete.</p><p><u>Third user story:</u></p><blockquote>As a signed-in user, I want to see my tasks.</blockquote><p>Currently, all tasks are displayed in the view regardless of who is logged-in. &#xA0;Good thing we implemented that second user story correctly. &#xA0;Now that tasks are related to users, we can query for only the current user&apos;s tasks. &#xA0;Since the view is the tasks index view, the code we want is in the tasks controller <code>index</code> action:</p><pre><code>  def index
    @tasks = Task.all
    @task = Task.new
  end</code></pre><p>Instead of <code>Task.all</code>, we&apos;ll modify it to query by the current user&apos;s id.</p><pre><code>@tasks = Task.where({ &quot;user_id&quot; =&gt; @current_user[&quot;id&quot;] })</code></pre><p>Now if you refresh the page, you should only see tasks you created as this user. &#xA0;If you don&apos;t see any tasks, you probably haven&apos;t created any yet as this user. &#xA0;Try submitting a new task. &#xA0;To test this out thoroughly, you can logout and login as another user, create a new task, and confirm that you only see that user&apos;s tasks as well.</p><p>However, if you logout and try to view this todo app as a logged-out user, you&apos;re going to run into that same bug from before because <code>@current_user[&quot;id&quot;]</code> will raise an error when there is no current user. &#xA0;There are plenty of fixes for this bug, but the simplest is to use the <code>session</code> cookie instead:</p><pre><code>@tasks = Task.where({ &quot;user_id&quot; =&gt; session[&quot;user_id&quot;] })</code></pre><p>And now the error should go away. &#xA0;User story &#x2705; complete.</p><p><u>Fourth user story:</u></p><blockquote>As a signed-in user, I want to be able to complete my tasks.</blockquote><p>Looking at the tasks <code>index.html.erb</code> code, the complete button is part of the <code>@tasks</code> loop:</p><pre><code>&lt;%= button_to &quot;Complete&quot;, task, :method =&gt; &quot;delete&quot; %&gt;</code></pre><p>Since the <code>@tasks</code> loop is only showing the current user&apos;s tasks, then the &quot;complete&quot; button is only being displayed for this user&apos;s tasks. &#xA0;So we can probably consider this user story done. &#xA0;</p><p>However, putting on our security hats for a moment, what if we were to view the source code in the browser to see what&apos;s going on?</p><p>Here&apos;s the final html that our app is sending to the browser.</p><pre><code>&lt;form method=&quot;post&quot; action=&quot;/tasks/3&quot;&gt;
  &lt;input type=&quot;hidden&quot; name=&quot;_method&quot; value=&quot;delete&quot; autocomplete=&quot;off&quot; /&gt;
  &lt;button type=&quot;submit&quot;&gt;Complete&lt;/button&gt;
  &lt;input type=&quot;hidden&quot; name=&quot;authenticity_token&quot; value=&quot;lfNjSdpeuV27vux8x9ChX1O-MIpwMZ1O-9pdz1aSRV2frkv__2zhAlt5ZzxjrXX6wI2Q0U81fuDbMLgCeXEHiQ&quot; autocomplete=&quot;off&quot; /&gt;
&lt;/form&gt;
</code></pre><p>There&apos;s a lot of noise there and some helpful code that rails is building for us. &#xA0;But the critical code is where this form submits to which is the <code>action</code> attribute of the <code>&lt;form&gt;</code> element: <code>action=&quot;/tasks/3&quot;</code>. &#xA0;The path is to <code>/tasks/3</code> where <code>3</code> is the <code>id</code> of the task row that we want to &quot;complete&quot;. &#xA0;As a clever internet user, you may know that you can actually edit that id by opening up the Chrome developer tools and inspecting the html. &#xA0;If you change that action to <code>/tasks/1</code> and click the &quot;complete&quot; button, the request will go to the tasks controller <code>destroy</code> action, query for <code>Task.find_by({ &quot;id&quot; =&gt; params[&quot;id&quot;] })</code> and find the row where the <code>id</code> is <code>1</code> (or any other value you change it to). &#xA0;All of a sudden, a clever user can &quot;complete&quot; any task they want, even if it&apos;s not their user&apos;s task. &#xA0;Bad.</p><p>We can protect against this type of malicious behavior fairly simply in our backend code. &#xA0;Here&apos;s the <code>destroy</code> action as-is:</p><pre><code>def destroy
  @task = Task.find_by({ &quot;id&quot; =&gt; params[&quot;id&quot;] })
  @task.destroy
  redirect_to &quot;/tasks&quot;
end</code></pre><p>Let&apos;s modify it slightly by wrapping the <code>@task.destroy</code> behavior in conditional logic that checks if this task is related to the current user.</p><pre><code>def destroy
  @task = Task.find_by({ &quot;id&quot; =&gt; params[&quot;id&quot;] })
  if @task[&quot;user_id&quot;] == @current_user[&quot;id&quot;]
    @task.destroy
  end
  redirect_to &quot;/tasks&quot;
end</code></pre><p>The code <code>@task[&quot;user_id&quot;] == @current_user[&quot;id&quot;]</code> will only be true if this task&apos;s <code>user_id</code> column matches the logged-in user&apos;s <code>id</code>. &#xA0;So even if a clever user tries to modify the html, nothing will happen unless the path finds a task that they created. &#xA0;User story &#x2705; complete.</p><p>Test out the todos app now - it should behave as you&apos;d expect as a user.</p>]]></content:encoded></item></channel></rss>