<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Joseph Moore</title>
    <description>The latest articles on DEV Community by Joseph Moore (@thatjoemoore).</description>
    <link>https://dev.to/thatjoemoore</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4001%2Fpic-258.jpg</url>
      <title>DEV Community: Joseph Moore</title>
      <link>https://dev.to/thatjoemoore</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thatjoemoore"/>
    <language>en</language>
    <item>
      <title>Database Views Don't Really Exist</title>
      <dc:creator>Joseph Moore</dc:creator>
      <pubDate>Thu, 09 Aug 2018 03:10:04 +0000</pubDate>
      <link>https://dev.to/thatjoemoore/database-views-dont-exist-4ho7</link>
      <guid>https://dev.to/thatjoemoore/database-views-dont-exist-4ho7</guid>
      <description>&lt;p&gt;I keep running into a misconception about database views, even when talking to fairly experienced developers. People seem to be under the impression that, in a relational database, a view is something that actually exists. They don't!&lt;/p&gt;

&lt;p&gt;Now, let me explain. Obviously, when you create a view, SOMETHING exists. But many developers seem to misunderstand what it is that exists. Most often, I encounter this misconception when trying to address performance problems - "Hey, if your join is so bad and slow, why don't you just create a view! That'll make your SQL faster, with no joins or anything!"&lt;/p&gt;

&lt;p&gt;When I say that the view doesn't exist, I mean that, at any given time, there is no data stored that says "here's what's in this view!" A view is not a table that gets constantly updated when the underlying tables get updated.&lt;/p&gt;

&lt;h1&gt;
  
  
  So what is a view, really?
&lt;/h1&gt;

&lt;p&gt;A view is a stored query.  When you create a database view, the database stores the SQL you gave it.  Then, when you come along and query that view, the database takes the stored view query, adds in the extras from the query against the view, and executes it.  That's it!  Essentially, a view is just an automatic query rewriter, letting you write SQL which is simpler than what you would otherwise write. It's kind of like writing a function to hide a bunch of reusable nasty code. That code still gets executed, it just gets hidden behind a simpler, reusable facade.&lt;/p&gt;

&lt;h1&gt;
  
  
  Examples, please!
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjmaob35tiqlvb8n31zl2.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjmaob35tiqlvb8n31zl2.gif" alt="Money, Please! from Parks and Recreation" width="245" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's say you have two shiny tables, Cars and Drivers, which have a many-to-many relationship:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbj0zcfprmm1thze2uigi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbj0zcfprmm1thze2uigi.png" alt="Cars and Drivers have a many-to-many relationship" width="624" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;People can drive many cars, and one car can be driven by many people.&lt;/p&gt;

&lt;p&gt;Now, let's say I need to be able to query for the name of everyone who drives a blue Toyota.  What might my SQL look like?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;distinct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;car_drivers&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;
&lt;span class="k"&gt;left&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;car&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt;
&lt;span class="k"&gt;left&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'blue'&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'toyota'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(This isn't the most performant version of this query, but we'll get to that in a minute.)&lt;/p&gt;

&lt;p&gt;While this is a trivial example, it's still a lot of stuff to get right. In a real-world example, those joins can get out of hand quickly.&lt;/p&gt;

&lt;p&gt;So, let's make a view, so our queries can be simpler!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;view&lt;/span&gt; &lt;span class="n"&gt;cars_and_drivers_vw&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;birthday&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;car_drivers&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;
&lt;span class="k"&gt;left&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;car&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt;
&lt;span class="k"&gt;left&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, my query becomes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;distinct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cars_and_drivers_vw&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'blue'&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'toyota'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice! Simple! Pretty!&lt;/p&gt;

&lt;h2&gt;
  
  
  But what's really happening?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbph9i5apkjz999xgp043.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbph9i5apkjz999xgp043.gif" alt="Don't Look" width="329" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's what happens when we set all of this up in an Oracle database and ask Oracle to show it's execution plan for our simple query:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fen37fjtsa3s68dol3w6g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fen37fjtsa3s68dol3w6g.png" alt="Oracle execution plan - I'm truly sorry I can't write a good alt-text for this" width="800" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don't know what you're looking at, never fear!  That's what I'm here for!&lt;/p&gt;

&lt;p&gt;What this shows us is that Oracle is taking this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;distinct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cars_and_drivers_vw&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'blue'&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'toyota'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and turning it into this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;distinct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;car_drivers&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;
&lt;span class="k"&gt;left&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;car&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt;
&lt;span class="k"&gt;left&lt;/span&gt; &lt;span class="k"&gt;join&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'blue'&lt;/span&gt; &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'toyota'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Hey, that looks familiar!)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Nice!&lt;/p&gt;

&lt;h1&gt;
  
  
  Stop Telling Me Views are Faster!
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5n1a0n4uqcncu118un4a.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5n1a0n4uqcncu118un4a.gif" alt="Barney Stinson - Stop It!" width="374" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we come to the thing that made me write this post. &lt;strong&gt;Views do not improve performance!&lt;/strong&gt; In one of my team's more complex applications, we have some very large tables with complicated relationships and even more complicated queries that need to be performed on them. Every time we run into a slow query, someone suggests that we can solve the slowness with a view.  By creating a view that does all of our joins for us, we can magically make our query faster!  However, if you realize that a view is just a fancy query re-writer, not a persistent data structure, you know that this is not true.  Using a view will not be any faster than the raw query; in fact, it could easily make things worse. The answer to performance problems is not a view; it is often a process of trial and error: finding different queries that yield the same results, analyzing query execution plans, and finding good indexes to help the database query things more efficiently.&lt;/p&gt;

&lt;p&gt;As an example of a query that might be more efficient than the slow one we've been using, here's a query that is equivalent to our first query, but may be more efficient over a large data set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;driver&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="k"&gt;exists&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;car_drivers&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt; 
    &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;driver_id&lt;/span&gt; 
    &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;exists&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;car&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
        &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vin&lt;/span&gt; 
        &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'blue'&lt;/span&gt; 
        &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;make&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'toyota'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  What about materialized views?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjxranxkl9s1ji6jeb5j.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjxranxkl9s1ji6jeb5j.gif" alt="Star Trek Beaming In" width="245" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You just had to bring those up, didn't you.&lt;/p&gt;

&lt;p&gt;Materialized Views are a concept which exists in Oracle and in Postgres (and some other, smaller engines). The term is really a misnomer, and Oracle has replaced the name 'Materialized View' with 'Snapshot', as it is a more accurate description of what is happening.&lt;/p&gt;

&lt;p&gt;When you create a &lt;del&gt;materialized view&lt;/del&gt; snapshot, the database runs the query you specified immediately, then stores the results. The important thing is that, as the original tables change, the contents of the materialized view do &lt;strong&gt;not&lt;/strong&gt; change. You have to manually refresh the view whenever you want to update the contents, which can be a slow operation. So, materialized views are fast, but they rapidly become stale. They're mostly useful for non-real-time analytics and analysis, where you would periodically refresh the view, then perform lots of queries on it.&lt;/p&gt;

&lt;p&gt;The exception to this (since there always is one in SQL databases) is Microsoft SQL Server, which allows you to create an 'indexed view,' which is like an Oracle snapshot, except that updates to the underlying table are reflected in the view. However, this comes with limitations to the queries which can be used to construct the view.&lt;/p&gt;

&lt;h1&gt;
  
  
  Epilogue
&lt;/h1&gt;

&lt;p&gt;So, obviously, we didn't use a view to solve our recent performance problems. In fact, the initial version of the application used views for these queries, and we got rid of them because they actually made many of our queries much slower. We also can't solve our specific problem with indexes because the most complex parts of the queries are split across two tables 🤦.&lt;/p&gt;

&lt;p&gt;Instead, we leveraged some mechanisms we'd already built into the app to implement a small bit of &lt;a href="https://martinfowler.com/bliki/CQRS.html" rel="noopener noreferrer"&gt;CQRS&lt;/a&gt; - we created a small table that keeps the important, hard-to-optimize parts of our common queries. We already had hooks in our DB layer that allowed us to run additional updates as side effects to the updates on our main tables, so we took advantage of that to make sure that our query table is always up-to-date. This allowed us to add some very nice indexes, and one of our queries went from taking about 20 seconds to taking about 0.15 seconds.&lt;/p&gt;

</description>
      <category>database</category>
    </item>
    <item>
      <title>Bringing Order to Web Design Chaos (with Web Components)</title>
      <dc:creator>Joseph Moore</dc:creator>
      <pubDate>Fri, 09 Feb 2018 05:43:39 +0000</pubDate>
      <link>https://dev.to/thatjoemoore/bringing-order-to-web-design-chaos--3fhb</link>
      <guid>https://dev.to/thatjoemoore/bringing-order-to-web-design-chaos--3fhb</guid>
      <description>&lt;h2&gt;
  
  
  Or, how I learned to stop worrying and love Web Components
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;In my maturation as a Software Engineer, I've found that stories of the problems and solutions that other engineers have encountered to be invaluable tools for improving and learning. So, I'm going to start sharing some of my code stories. This is the first of (hopefully) many, and it is not a short one.  Cross-posted from &lt;a href="https://www.thatjoemoore.com/posts/2018-01/byu-theme-components/" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is the story of an interesting engineering effort I've participated in over the last year. It's been a really fun side project for me (~20 hours a week at the start, ~5 hours a week since then), which has allowed me to experiment with some fun new technologies and implement some cool stuff. It centers on an effort by a large, cross-disciplinary team to bring a simple, unified theme to all of our many, many web properties that can share code between any CMS or frontend framework.&lt;/p&gt;

&lt;h1&gt;
  
  
  Where We Started
&lt;/h1&gt;

&lt;p&gt;I work for &lt;a href="https://www.byu.edu" rel="noopener noreferrer"&gt;Brigham Young University&lt;/a&gt;, a large, private University.&lt;/p&gt;

&lt;p&gt;Universities are a unique environment. In a university like ours, you have a number of large colleges and schools, all of which have a lot of freedom to do what they want. Historically, one way in which this freedom has manifested itself is in Web design, which has lead to some very...inconsistent designs.&lt;/p&gt;

&lt;p&gt;For example, this time last year, a designer I work with looked at the home pages of all of our top-level colleges and schools, and found that they all had completely different designs. They used different logos, layouts, shades of our school colors (navy blue and white), and, in one extreme case, didn't use our school colors at all. Many of these pages were loosely based on a design that had been published in 2013. This particular design was never well documented  and the reference implementation wasn't portable across platforms, so we saw a lot of divergence and changes as time went on.&lt;/p&gt;

&lt;p&gt;I don't mean to shame any particular schools, because most of their designs actually looked quite nice, but here's a &lt;del&gt;horrifying&lt;/del&gt; fun picture to show what I'm talking about:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.thatjoemoore.com%2Fposts%2F2018-01%2Fbyu-theme-components%2Fbyu-sites.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.thatjoemoore.com%2Fposts%2F2018-01%2Fbyu-theme-components%2Fbyu-sites.png" alt="Selection of inconsistent BYU pages"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After hiring an outside firm to conduct an audit of our web presence, it was decided that it was time to try and bring some brand uniformity to the University. An initiative was launched in our web community to design and implement a simple, modern, unobtrusive standard theme and to encourage developers and designers across campus to converge on this theme.&lt;/p&gt;

&lt;p&gt;I'm not going to talk about the design part, because I'm not a designer. While I was asked to attend the design meetings, it was as an engineer who would be asked to develop a reference implementation of the theme. I should note, however, that it was decided to only require a small header (including search and user sign-in), simple navigation, and a footer. Everything else was up to individual sites to design, though we've also been working on providing them with off-the-shelf styles and components so that every site doesn't have to redesign common elements.&lt;/p&gt;

&lt;h1&gt;
  
  
  Our Goals
&lt;/h1&gt;

&lt;p&gt;Once our design group hammered out a design that everyone could work with, it was time for our engineering group to implement things. While the design group worked, we had one department that stepped up in a big way and developed a Drupal module that allowed them to rapidly iterate on the design across several of their sites, so the designers could get feedback on  what worked in the real world and what didn't. However, for long-term use, we decided that we wanted a solution that gave us three things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Cross-platform&lt;/li&gt;
&lt;li&gt;[ ] Simple&lt;/li&gt;
&lt;li&gt;[ ] Fixable&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cross-Platform
&lt;/h2&gt;

&lt;p&gt;We have a wide variety of web hosting platforms across campus, ranging from simple static sites, to CMSes like Drupal and Wordpress, to complex single-page apps written in Angular, React, and other frameworks. We needed a solution that would allow a small team to make our theme work consistently across all platforms. Ideally, this would mean having one single codebase for everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simple
&lt;/h2&gt;

&lt;p&gt;We have a wide variety of skill sets on campus, ranging from seasoned, 30-year veterans to CS students who got a part-time job maintaining a departmental site. We needed a solution that was simple and easy to consume, even if the developer doesn't understand everything that's going on - if you can't reliably copy and paste from examples, it would be too complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fixable
&lt;/h2&gt;

&lt;p&gt;One of the problems we had run into in the past was that, as the theme was improved, only new sites would adopt the improvements. Because every site kept their own copy of the theme code, there was no good way to distribute updates and fixes.&lt;/p&gt;

&lt;h1&gt;
  
  
  Web Components to the Rescue
&lt;/h1&gt;

&lt;p&gt;A few of us in the group had been playing with Web Components, and the v1 version of the specifications had just been implemented in Chrome. After looking at several proposals on how to reach our goals, we decided that Web Components would give us a lot of what we were looking for.&lt;/p&gt;

&lt;p&gt;Web Components solved a lot of our problems. Because they're a platform-level primitive, we didn't have to worry as much about integration - they can (theoretically) integrate with anything that can work with the DOM.&lt;/p&gt;

&lt;p&gt;To put this in perspective, it took a couple of student programmers who hadn't seen our code before about 30 minutes to implement a Wordpress theme that was based on our Web Components. My favorite demonstration of the ease with which we could integrate came when I was doing a presentation about the theme to the campus organization I work for. As a live demo, I took one of our really old, nasty pages and retrofitted it to use the new theme, right there in front of everyone. It took about 10 minutes. Granted, I had practiced this beforehand, but it was still pretty fun to show just how easy it could be.&lt;/p&gt;

&lt;p&gt;Beyond the ability to write them out to the DOM, Shadow DOM gave us some really nice style isolation, so we could be certain that none of the site's styles could accidentally mess up our themed components, and none of our internal styles would leak out to the outside world.&lt;/p&gt;

&lt;p&gt;There have been some hiccups on the way, but our CMSes have had a pretty easy integration story, and we've got Single-Page Apps in production that are using React (16+), Angular (2+), VueJS, and others. There are a handful of SaaS tools doing some truly awful things to the DOM that we haven't gotten working yet, but for the most part, it's been pretty uneventful.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal Check In:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] Cross-platform&lt;/li&gt;
&lt;li&gt;[ ] Simple&lt;/li&gt;
&lt;li&gt;[ ] Fixable&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Web Components also promised to solve some of our complexity problems. If you're familiar with Bootstrap, you know how, in order to use their styles, you have to structure your markup in just the right way, with lots of nested &lt;code&gt;div&lt;/code&gt;s and special classes. It's really easy to get lost in all of that structure, especially since the design we were given had some very large layout differences between mobile and desktop views. The concept of slots in Shadow DOM gave us a really easy way to hide the markup complexity from the consumer, while still allowing them to plug in all of their site-specific things.&lt;/p&gt;

&lt;p&gt;(If you're unfamiliar with Shadow DOM, slots are a lot like doing component composition with &lt;code&gt;children&lt;/code&gt; in React or &lt;code&gt;&amp;lt;ng-content&amp;gt;&lt;/code&gt; in Angular)&lt;/p&gt;

&lt;p&gt;Our final header markup looks something like this:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;del&gt;(Note that the fonts aren't the actual fonts, as those can only be loaded on *.byu.edu sites. Open the full codepen to see the desktop view.)&lt;/del&gt; This isn't true anymore - we swapped the fonts, and I didn't have to change the codepen at all! #winning&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/ThatJoeMoore/embed/jZyXwb?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;At first glance, there's a lot going on here. However, most of the structural complexity is hidden, and only the things that are specific to a given site are included.&lt;/p&gt;

&lt;p&gt;To give you an idea of how much structure has been left out, here's what this looks like after being rendered by the Shady DOM polyfill, which combines all of the content into one DOM tree:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;byu-header&lt;/span&gt; &lt;span class="na"&gt;mobile-max-width=&lt;/span&gt;&lt;span class="s"&gt;"1023px"&lt;/span&gt; &lt;span class="na"&gt;max-width=&lt;/span&gt;&lt;span class="s"&gt;"1200px"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-component-rendered"&lt;/span&gt; &lt;span class="na"&gt;left-align=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"header"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-header-root"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-header-content needs-width-setting stretches"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-header-primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-logo"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"home-url"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"home-url"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://byu.edu/"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-logo"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"BYU"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.byu.edu/shared-icons/latest/logos/monogram-white.svg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-header-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"site-title"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"some-site.byu.edu"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Site Title&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"secondary"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-header-secondary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-header-user"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;byu-user-info&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"user"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-component-rendered"&lt;/span&gt; &lt;span class="na"&gt;has-user=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-user-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"no-user slot-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"user-info-image"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"User Icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text slot-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"login"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#login"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign In&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"has-user"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"name slot-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"user-name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Joe Student&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"user-info-image"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"User Icon"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"logout slot-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"logout"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#logout"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign Out&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/byu-user-info&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-header-search"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;byu-search&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"submit-form"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-component-rendered"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"search-form"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"search-container"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"search.php"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"__byu-search-selected-input"&lt;/span&gt; &lt;span class="na"&gt;placeholder=&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt; &lt;span class="na"&gt;title=&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"search-button"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                                &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"search-icon"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.byu.edu/shared-icons/latest/fontawesome/search-white.svg"&lt;/span&gt;
                                    &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Run Search"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/byu-search&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"menu-outer-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"menu-inner-wrapper slot-wrapper needs-width-setting"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"max-width: 1200px;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;byu-menu&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"nav"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"byu-component-rendered"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"outer-nav slot-container needs-width-setting"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#link1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Link 1&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#link2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Link 2&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#link3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Link 3&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"#link4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Link 4&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/byu-menu&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/byu-header&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've actually simplified that a little. It's not pretty, and Web Components give us this nice little wrapper behind which we can hide all of this complexity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal Check In:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] Cross-platform&lt;/li&gt;
&lt;li&gt;[x] Simple&lt;/li&gt;
&lt;li&gt;[ ] Fixable&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Dynamic Distribution
&lt;/h1&gt;

&lt;p&gt;(This is the part I'm really proud of)&lt;/p&gt;

&lt;p&gt;Once we had a working set of components, we needed some way to distribute them to developers on campus. We considered using some sort of package manager, like NPM, but we quickly determined that it would not fit our needs. While it would give us a simple way to push out updates, there would be no way to guarantee that sites would see the update. We have sites that will go years without seeing an update, and, in the event of an important bug, we wanted to be able to have even those sites get the update. In addition, with the variety of platforms we needed to support, it would be hard to converge on a single package manager that would be easy for everyone to use.&lt;/p&gt;

&lt;p&gt;The solution we converged on was to build a central CDN and have all sites load resources from it. This has turned out to be a great idea. We built a process that could respond to webhook notifications from Github and push code changes to the CDN. My office has been aggressively pursuing a move to Amazon Web Services, so we build the CDN using Amazon S3, Cloudfront, and CodeBuild.&lt;/p&gt;

&lt;p&gt;Our CDN's secret sauce is its assembler. When we make any pushes to any of our Github repositories, an AWS Lambda function receives a webhook notification and kicks off a build running on AWS CodeBuild. We use CodeBuild because a) we don't have to run any servers for it and b) it allows us to execute basically anything that can run in a docker container. The assembler looks at a list of repositories that are included in the CDN, then uses the Github API to find all of the branches and tags that have changed or been created since the last assembler run. It pulls down the necessary files, then copies the required files into our S3 bucket.&lt;/p&gt;

&lt;p&gt;In order to provide automatic updates, we include the concept of 'aliases' in the CDN. Aliases use semver parsing of Git tags to create aliases like &lt;code&gt;latest&lt;/code&gt; (always the highest version number), &lt;code&gt;1.x.x&lt;/code&gt; (gets all minor and bugfix releases), and &lt;code&gt;1.1.x&lt;/code&gt; (gets all bugfix releases for the 1.1 major release). We also include an &lt;code&gt;unstable&lt;/code&gt; alias, which points to whatever is currently on master. Users can also stick to a specific release by providing an explicit version number. This allows us to provide automatic updates to all most sites, while giving more control to those who desire it.&lt;/p&gt;

&lt;p&gt;When a consumer wants to use something from our CDN, they construct a URL like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://cdn.byu.edu/{library name}/{version or alias}/{file name}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, the URL for the main Javascript for the always up-to-date version our theme components is &lt;code&gt;https://cdn.byu.edu/byu-theme-components/latest/byu-theme-components.min.js&lt;/code&gt;. Getting the current latest version and turning off auto-updates would have a URL of &lt;code&gt;https://cdn.byu.edu/byu-theme-components/1.2.3/byu-theme-components.min.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We want to maximize the cacheability of all of our assets, but this can complicate the rapid rollout of new releases. We've found a good compromise by assuming that tags will never change (which is usually true). So, we serve anything coming from a specific tag with cache headers that indicate that it is cacheable forever (&lt;code&gt;Cache-Control: immutable, public, max-age=31557600, s-maxage=31557600&lt;/code&gt;). Then, when a user requests an alias (like &lt;code&gt;latest&lt;/code&gt;), we send back a 302 redirect to the specific tag and set a cache expiry on the redirect to 1 hour. This gives us a good balance between having updates roll out often and keeping useful things in the user's cache. The redirects happen quickly enough and transfer little enough data to the user that they don't add much overhead, and we keep the vast majority of our assets cached in the browser forever (well, a year in most browsers, but that might as well be forever, right? I mean, it's longer than the lifetime of most Javascript frameworks).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal Check In:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[x] Cross-platform&lt;/li&gt;
&lt;li&gt;[x] Simple&lt;/li&gt;
&lt;li&gt;[x] Fixable&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Additional Challenges
&lt;/h1&gt;

&lt;p&gt;There are always problems that you don't foresee going into a project (or even some that you foresee, but decide not to tackle until later). Here are some of the ones we faced.&lt;/p&gt;

&lt;h2&gt;
  
  
  Opting-out of a Framework
&lt;/h2&gt;

&lt;p&gt;When we started working on this a year ago, &lt;a href="https://www.polymer-project.org/" rel="noopener noreferrer"&gt;Polymer 2&lt;/a&gt; was still a few months from being released, and we weren't enamoured with Polymer's insistence on using the never-going-to-be-implemented HTML Imports specification. &lt;a href="http://skatejs.netlify.com/" rel="noopener noreferrer"&gt;SkateJS&lt;/a&gt; was an option, but we didn't really like the way Skate was handling templates.&lt;/p&gt;

&lt;p&gt;Faced with these options, we decided early on that we didn't really need a Web Component framework. Our components were going to be fairly static - stamp the DOM once, allow for a few small dynamic things, and be done with it. So, we thought, it wouldn't be that terrible to use the raw Custom Element APIs, augmented with a small collection of helper functions for doing common things (like stamping a template to the Shadow DOM).&lt;/p&gt;

&lt;p&gt;If we were starting this project now, I think we would be using some form of framework, probably &lt;del&gt;Polymer 3 (even though it's not released yet) or SkateJS 5 (with its nice new pluggable renderers)&lt;/del&gt; LitElement. While our setup isn't THAT complicated, we do still re-invent the wheel in a lot of ways.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-904902463588839424-826" src="https://platform.twitter.com/embed/Tweet.html?id=904902463588839424"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-904902463588839424-826');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=904902463588839424&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Singing the Load-Time Blues
&lt;/h2&gt;

&lt;p&gt;That still left the problem of loading our elements. With the HTML Imports spec, you define your elements in an HTML file, which contains your markup, your styles, and your JS code, all in once nice happy file. You pop a &lt;code&gt;&amp;lt;link rel="import"&amp;gt;&lt;/code&gt; into your page, and your components magically load everything they need.&lt;/p&gt;

&lt;p&gt;However, we have a lot of skittish developers on campus, and we didn't want to introduce many new concepts to them (Yes, there are some who would get scared off by something as simple as a new type of &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag). We wanted to be able to stick with simple &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags for loading. You add into that the fact that nobody but Chrome wanted to implement HTML Imports, and things were looking pretty dismal for the Polymer 1 and 2 way of loading things. (Sidenote: I still long for a &lt;a href="https://github.com/w3c/webcomponents/issues/645" rel="noopener noreferrer"&gt;better solution&lt;/a&gt; to importing HTML than just 'stick it all in Javascript').&lt;/p&gt;

&lt;p&gt;We also wanted our development experience to be nice. If we couldn't just bring in an HTML document with embedded CSS and JS, we wanted to be able to keep our CSS, JS, and HTML in separate files, but still have them all be imported with one simple script tag.&lt;/p&gt;

&lt;p&gt;First, we wrote a simple Gulp module that would assemble things for us, but it was finnicky and relied on comments that were formatted in juuust the right way. That's when we discovered Webpack, which solved this problem for us. Now, we could have arbitrary dependency graphs, use modules from NPM, and just generally get things done without having to write our own bundler. That has worked really well for us and, as a result, importing our theme component bundle is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.byu.edu/byu-theme-components/latest/byu-theme-components.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Styling the Unstyleable
&lt;/h2&gt;

&lt;p&gt;Shadow DOM is great for keeping our template CSS contained, but what about styling the pieces that the consumer hands us in our slots? The whole point of this project is the styling!&lt;/p&gt;

&lt;p&gt;Shadow DOM does provide an answer for styling slotted content, but it's a limited one. I can make use of the &lt;code&gt;::slotted&lt;/code&gt; pseudo-element to style them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;style&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;.my-slot-wrapper&lt;/span&gt; &lt;span class="nd"&gt;::slotted&lt;/span&gt;&lt;span class="o"&gt;(*)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;navy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-slot-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;slot&amp;gt;&amp;lt;/slot&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll notice that &lt;code&gt;::slotted&lt;/code&gt; takes in another selector. That selector can be any CSS selector, with one limitation: it can only be one level deep. You can't have a selector like &lt;code&gt;::slotted(div &amp;gt; a)&lt;/code&gt;, it can only be one level deep.&lt;/p&gt;

&lt;p&gt;Most of the time, this limitation isn't a problem, but we quickly ran into two major issues: styling links and styling inputs.&lt;/p&gt;

&lt;p&gt;As soon as we published a beta version of our theme components, we saw people adding links to their site titles that were nested inside of &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;byu-header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;slot=&lt;/span&gt;&lt;span class="s"&gt;"site-title"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"my-site.byu.edu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;My Site&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/byu-header&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Normally, nested text elements wouldn't cause any problems for us - we set &lt;code&gt;color&lt;/code&gt;, &lt;code&gt;font-size&lt;/code&gt;, and &lt;code&gt;font-family&lt;/code&gt; on the parent element, and it cascades down. Links, however, are special. Every user-agent stylesheet I've seen applies their own &lt;code&gt;color&lt;/code&gt; and &lt;code&gt;text-decoration&lt;/code&gt; to links, and doesn't inherit those styles from the parent. If the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; is the element with the &lt;code&gt;slot&lt;/code&gt; attribute, this isn't a problem - the styles get directly applied to it. However, when the link is nested inside another element, we can't style it from within the Shadow DOM.&lt;/p&gt;

&lt;p&gt;The other problem we ran into was styling search inputs. We were aiming for compatibility across a wide range of frameworks and CMSes, and each one has their own ways of wrapping inputs that we couldn't get rid of. However, we needed to make sure that their inputs got styled properly. One solution could have been to present our own styled input and use Javascript to keep the two up-to-date with each other, but we felt that this approach had a lot of potential to confuse developers. For example, it would make implementing something like an autocomplete popup very, very difficult.&lt;/p&gt;

&lt;p&gt;So, we needed to be able to have this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;byu-search&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"my-cms-wrapper"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/byu-search&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;get styled just like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;byu-search&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"search"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/byu-search&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The solution to both of these problems turned out to be something we were already familiar with: external CSS stylesheets. We added a stylesheet (we called it the 'extra' styles) containing special selectors to handle these and other corner cases, and we have developers include both the stylesheet and the Javascript in their pages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.byu.edu/byu-theme-components/latest/byu-theme-components.min.css"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;async&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.byu.edu/byu-theme-components/latest/byu-theme-components.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Developers were already used to including pairs of CSS and Javascript files (like with jQuery UI), so we weren't introducing anything new to them.&lt;/p&gt;

&lt;p&gt;Because we were using custom element names, the selectors in the 'extra' stylesheet can be very simple, yet specific:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;byu-header&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;slot&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"site-title"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;text-decoration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While we try to avoid selectors that are more than two layers deep, having the outer two layers (&lt;code&gt;byu-header &amp;gt; [slot="site-title"]&lt;/code&gt;) gives us pretty good scoping, so our styles don't bleed over and affect the styles on the page.  There's still the possibility that some stylesheet later on the page will do this something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;green&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We decided that, since we can't solve everything, we won't try to solve this problem. After all, this is just an instance of CSS behaving as expected, so developers should be able to figure out why the site title is suddenly green all on their own (well, with the help of their browser's devtools, at least).&lt;/p&gt;

&lt;h2&gt;
  
  
  &amp;lt;blink&amp;gt; Considered Harmful &lt;em&gt;(Flash of Unstyled Content)&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;One of the pitfalls to using JS to render parts of a UI is the Flash of Unstyled Content. This is a common problem across frontend frameworks and isn't specific to Web Components, though I hope that proposals like &lt;a href="https://github.com/w3c/webcomponents/blob/gh-pages/proposals/Declarative-Shadow-DOM.md" rel="noopener noreferrer"&gt;Declarative Shadow DOM&lt;/a&gt; can help reduce the impact of it when using Web Components.&lt;/p&gt;

&lt;p&gt;Typically, the strategy for overcoming this is to embed some CSS in the initial HTML that will only apply until the framework has loaded and starts rendering things. We don't have a good way to have people embed styles and still preserve our goal of easily pushing out updates, so we've compromised and embed the Flash of Unstyled Content styles in our 'extra' styles. This CSS file is much smaller and parses much quicker than our JS, so we can get a very good experience from it. Once the components have stamped their template, they apply the &lt;code&gt;byu-component-rendered&lt;/code&gt; class to themselves, so all we need to do in our styles is look for components that don't have the class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;byu-header&lt;/span&gt;&lt;span class="nd"&gt;:not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;.byu-component-rendered&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;/* nasty fallback styles here */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since our CSS gets resolved synchronously, this allows our pages to look almost-right while we wait for the JS to finish its work. This leads to practically seamless page transitions, especially once the browser has cached the CSS.&lt;/p&gt;

&lt;h1&gt;
  
  
  Future Work
&lt;/h1&gt;

&lt;p&gt;One thing we want to add in the future is client-side analytics. While we can gather a fair amount of information from our server logs, we can't see some critical things, like how many sites are using different configuration options and component combinations. We're looking at using &lt;code&gt;navigator.sendBeacon&lt;/code&gt; to send light-weight analytics back to our servers with information like the build of the code that has been loaded, which components actually get used, different features of each component that are being used, etc. We don't want to rely on an external analytics site, because we don't want to add the concerns about load time and privacy that come with them to all of our sites. If individual sites want to use a full-featured analytics tool, they'll have that option without anything we do interfering with that.&lt;/p&gt;

&lt;p&gt;Another thing we want to add is automated tests.  There aren't a lot of logical tests we can do, since there isn't a lot of logic in our components, but we are really looking forward to adding tests that use visual diffing tools like &lt;a href="https://github.com/mapbox/pixelmatch" rel="noopener noreferrer"&gt;Pixelmatch&lt;/a&gt; to detect when we've made changes that alter the styling of our elements. The goal will be to have a step in our CDN assembler that runs the tests and doesn't deploy the new code if they fail.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;This has been a really fun project to work on, forcing us to tackle a lot of interesting challenges. I'm really proud of the solutions we've come up with, as well as the way that a group of developers from different departments and of different skill levels all came together to come up with a solution that works well for everyone. While our code isn't perfect, and we haven't really had a chance to bring it all in line with a single style, I feel like we've done a pretty good job.&lt;/p&gt;

&lt;p&gt;For those who would like to take a look at our code, it is all part of the &lt;a href="https://github.com/byuweb/" rel="noopener noreferrer"&gt;BYU Web Github Organization&lt;/a&gt; and is licensed with Apache 2.0.&lt;/p&gt;

&lt;p&gt;A few quick links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/byuweb/byu-theme-components" rel="noopener noreferrer"&gt;Our main theme components&lt;/a&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://2017-components-demo.cdn.byu.edu/" rel="noopener noreferrer"&gt;A gallery of the theme components&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;a href="https://github.com/byuweb/web-cdn" rel="noopener noreferrer"&gt;Our CDN source&lt;/a&gt;&lt;/li&gt;

&lt;li&gt;

&lt;a href="https://github.com/byuweb/byu2017_d7" rel="noopener noreferrer"&gt;Drupal&lt;/a&gt; and &lt;a href="https://github.com/byuweb/WordPress" rel="noopener noreferrer"&gt;Wordpress&lt;/a&gt; CMS themes that leverage the Web components.&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>webcomponents</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Emojifying my Bash Prompt (and why you should too)</title>
      <dc:creator>Joseph Moore</dc:creator>
      <pubDate>Sat, 29 Jul 2017 02:11:45 +0000</pubDate>
      <link>https://dev.to/thatjoemoore/emojifying-my-bash-prompt</link>
      <guid>https://dev.to/thatjoemoore/emojifying-my-bash-prompt</guid>
      <description>&lt;p&gt;&lt;strong&gt;Warning: Dumb Stuff ahead&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This post contains some Dumb Stuff. If you take yourself too seriously, you may want to go find something a little less whimsical to read ðŸ˜.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;/warning&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's face it - development work can sometimes be hard on the psyche. When the unrealistic project deadlines approaching or the mountains of technical debt are looming, sometimes a small little pop of happiness can get you through the day. One of the things that keeps me sane is finding little ways to give myself small bursts of joy throughout the day. That's why, when I got a new laptop a few weeks ago, I decided to play around for a bit and find a fun way to solve some problems.  How, do you ask?  By emojifying my bash prompt.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why, oh why, would you do such a dumb thing?
&lt;/h1&gt;

&lt;p&gt;Well, it all started with two different problems I'd noticed on my old laptop; minor little annoyances that were never worth taking the time to solve when everything else was working. I have a personal rule that, once I get a new laptop, I a few days to tweak and configure it, and then my setup is frozen for at least a few months.  This helps me incrementally improve my setup, but prevents me from falling into an endless cycle of tweaking and testing and never getting any actual work done.  I've seen developers get so lost in configuring their workstation that they're basically useless for a month, and I never want to be That Guy.&lt;/p&gt;

&lt;h1&gt;
  
  
  A Very Fancy ðŸŽ© Prompt
&lt;/h1&gt;

&lt;p&gt;I've always had a thing for customizing my bash prompt; I never like the default. My last few workstations and all of my servers have had basically the same prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hostname:/current/directory/here
 -&amp;gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is, obviously, nothing special, but customizing my prompt was one of the first things I figured out how to do in Bash, and it still gives me a little burst of joy to remember the feeling of triumph when I finally got it to work how I wanted.&lt;/p&gt;

&lt;p&gt;A couple of laptops later, I further improved this prompt to detect when I was in a subdirectory of my home directory, and replace my home path with &lt;code&gt;~&lt;/code&gt;.  Yet another little burst of joy when I see it.&lt;/p&gt;

&lt;p&gt;And now, I was setting up a new laptop, and I'd gotten bored with my prompt.  First, I thought about adding colors to it, but I quickly got bored with that - it wasn't any more exciting than a plain prompt.  Then, while installing some packages using &lt;code&gt;brew&lt;/code&gt;, I noticed the little beer emoji that pops up when you successfully install something, and I got to thinking - what if I played with Emoji? Lately, whenever I need to come up with a trivial demo or example, I lean towards doing something dumb with emoji, because it makes me laugh to treat something as dumb as emoji seriously (just look at the &lt;a href="https://github.com/ThatJoeMoore/byu-fancy-component" rel="noopener noreferrer"&gt;output&lt;/a&gt; of a yeoman generator I created a while back to demonstrate some concepts to my team).&lt;/p&gt;

&lt;p&gt;So, how to make my prompt Fancy?&lt;/p&gt;

&lt;p&gt;Well, here's how:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkc06hcaujkf90urzie1w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkc06hcaujkf90urzie1w.png" alt="A Fancy Prompt" width="394" height="188"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I really didn't do much - I just replaced my &lt;code&gt;~&lt;/code&gt; with a ðŸ  and my little ASCII arrow with a â©. And yet, those little emoji make my day a bit brighter each time I see them.&lt;/p&gt;

&lt;p&gt;You'll notice that I added another emoji - on this laptop, I'm keeping all of my work-related code under &lt;code&gt;~/work&lt;/code&gt;, and so I decided to replace that path with an office building ðŸ¢. Why?  Well, why not? I might eventually add more such replacements, but I've already exhausted my self-enforced tweaking time. Perhaps I'll make &lt;code&gt;~/Documents&lt;/code&gt; be ðŸ“ and &lt;code&gt;/tmp&lt;/code&gt; â³. I can definitely think of a few project directories that could be replaced by ðŸ’©. But that's a problem for another laptop.&lt;/p&gt;

&lt;p&gt;So, my prompt setup isn't too complicated at this point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
fancyprompt&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo

  &lt;/span&gt;&lt;span class="nv"&gt;where&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;
  &lt;span class="nv"&gt;home&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;
  &lt;span class="nv"&gt;work&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$home&lt;/span&gt;&lt;span class="s2"&gt;/work"&lt;/span&gt;

  &lt;span class="nv"&gt;where&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;where&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$work&lt;/span&gt;&lt;span class="p"&gt;/ðŸ¢ &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;where&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;where&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;$home&lt;/span&gt;&lt;span class="p"&gt;/ðŸ  &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="nv"&gt;PS1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'$where
â©  '&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;PROMPT_COMMAND&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fancyprompt

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;em&gt;But can it get fancier?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Of course!&lt;/p&gt;

&lt;h1&gt;
  
  
  Keeping me up-to-date
&lt;/h1&gt;

&lt;p&gt;Once again, while installing all of my normal &lt;code&gt;brew&lt;/code&gt; packages, I got to thinking about one of the problems I run into with Homebrew: outdated packages. I'll let so many updates accumulate without ever doing anything about them that, when I do need to update one, I end up with a &lt;code&gt;brew upgrade&lt;/code&gt; running for half an hour, and I don't like that. So, could I find a fun way to solve this problem?  Well, would I be here telling you about it if I hadn't? I don't think so!&lt;/p&gt;

&lt;p&gt;The idea I settled on was to enhance my already-fancy prompt my making it tell me when there were brew updates to install:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5z8zny0fg8huhu48yfej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5z8zny0fg8huhu48yfej.png" alt="Brew Updates" width="454" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My initial implementation looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
fancyprompt&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

  brew update
  &lt;span class="nv"&gt;outdated&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;brew outdated &lt;span class="nt"&gt;--quiet&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'[:space:'&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$outdated&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
          &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;1B[104m&lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;1B[97mðŸº You have &lt;/span&gt;&lt;span class="nv"&gt;$outdated&lt;/span&gt;&lt;span class="s2"&gt; brew update(s).ðŸº  &lt;/span&gt;&lt;span class="se"&gt;\x&lt;/span&gt;&lt;span class="s2"&gt;1B[0m"&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;

  ...the rest of my prompt...
&lt;span class="o"&gt;}&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;This worked...slowly.  &lt;code&gt;brew update&lt;/code&gt; is very slow, as it has to do a lot of work, including a fair amount of network IO.  So, I moved the &lt;code&gt;brew update&lt;/code&gt; into a cron job that ran daily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/sbin:/usr/local/bin

@daily brew update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Who uses cron on their workstation? I do, apparently!)&lt;/p&gt;

&lt;p&gt;Now, I could remove the &lt;code&gt;brew update&lt;/code&gt; from my prompt.  My prompt was then...less slow.  As it turns out, &lt;code&gt;brew outdated&lt;/code&gt; was pretty slow too, taking about 500ms to execute.  Knowing exactly nothing about what's actually happening, I choose to cast the need to fire up a Ruby process to run brew as my red herring.  Regardless, 500ms is way. too. slow.  It felt like I was dying every time I typed a command and waited for my prompt to come back.  So I had to get crazier.&lt;/p&gt;

&lt;p&gt;I solved the speed problem by moving the &lt;code&gt;brew outdated&lt;/code&gt; check to my cron job too, writing the output to a little file in &lt;code&gt;/tmp&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@daily brew update &amp;amp;&amp;amp; brew outdated --quiet | wc -l | tr -d '[:space:'] &amp;gt; /tmp/.brew-outdated
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I had a file that contained a count of my outdated brew packages. All I had to do was read it in my prompt function, which is very quick!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    outdated=0
    if [ -f "/tmp/.brew-outdated" ]; then
      outdated=`cat /tmp/.brew-outdated`
    fi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hooray! Now, I get to happy little beer emoji and a nice message whenever I have brew updates.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;But can we go fancier?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Never fear, dear reader, we can!&lt;/p&gt;

&lt;h1&gt;
  
  
  Update ALL THE PACKAGES
&lt;/h1&gt;

&lt;p&gt;Turns out, I have a bunch more package managers to keep up to date. I do some web dev, so of course, I have a bunch of npm packages, and I have to have pip installed, because a bunch of our deployment tooling is in Python. So, can I extend this to get updates for those?&lt;/p&gt;

&lt;p&gt;But of course!&lt;/p&gt;

&lt;p&gt;I ended up creating a few extra functions to handle this for me, and wrote a script to check for updates:&lt;br&gt;
&lt;/p&gt;

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

check_brew&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo &lt;/span&gt;checking brew
  brew update
  brew outdated &lt;span class="nt"&gt;--quiet&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'[:space:]'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/.brew-outdated
&lt;span class="o"&gt;}&lt;/span&gt;

check_npm&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo &lt;/span&gt;checking npm
  npm outdated &lt;span class="nt"&gt;--global&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'[:space:]'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/.npm-outdated
&lt;span class="o"&gt;}&lt;/span&gt;

check_pip&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;echo &lt;/span&gt;checking pip2
  pip2 list &lt;span class="nt"&gt;--outdated&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;legacy | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'[:space:]'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/.pip2-outdated
  &lt;span class="nb"&gt;echo &lt;/span&gt;checking pip3
  pip3 list &lt;span class="nt"&gt;--outdated&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;legacy | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; | &lt;span class="nb"&gt;tr&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'[:space:]'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/.pip3-outdated
&lt;span class="o"&gt;}&lt;/span&gt;

check_brew
check_npm
check_pip


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

&lt;/div&gt;



&lt;p&gt;Each of these writes to a file named &lt;code&gt;/tmp/.(toolname)-outdated&lt;/code&gt;. I then have some functions in my bash profile that, given a tool name and an emoji, will check the appropriate file and show a message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa29ectuo2jq6tvwgs9bb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa29ectuo2jq6tvwgs9bb.png" alt="A bunch of updates" width="468" height="230"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
_count_updates() {
  type=$1
  f=/tmp/.$type-outdated
  upd=0
  if [ -f "$f" ]; then
    upd=`cat $f`
  fi
  echo $upd
}

_has_updates() {
  type=$1

  cnt=`_count_updates $type`
  if [ "$cnt" -gt "0" ]; then
          return 0
  fi
  return 1
}

_update_prompt() {
  type=$1
  icon=$2

  upd=`_count_updates $type`
  if [ "$upd" -gt 0 ]; then
    echo -e "\x1B[104m\x1B[97m$icon You have $upd $1 update(s).$icon \x1B[0m"
  fi
}

# configure my multi-line prompt
fancyprompt() {
  echo

  _update_prompt brew ðŸº
  _update_prompt npm ðŸ“¦
  _update_prompt pip2 ðŸ
  _update_prompt pip3 ðŸ

  where=$PWD
  home=$HOME
  work="$home/work"

  where="${where/$work/ðŸ¢ }"
  where="${where/$home/ðŸ  }"

  PS1='$where
â©  '
}


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

&lt;/div&gt;



&lt;p&gt;I then decided that remembering the right commands to update everything was a pain, mostly because Pip is weird, so I created a function in my bash profile that knows how to do it for me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
update_all&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;_has_updates brew&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
          &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Updating Brew"&lt;/span&gt;
          brew upgrade
  &lt;span class="k"&gt;fi

  if &lt;/span&gt;_has_updates npm&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
          &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Updating NPM"&lt;/span&gt;
          npm update &lt;span class="nt"&gt;-g&lt;/span&gt;
  &lt;span class="k"&gt;fi

  if &lt;/span&gt;_has_updates pip2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
          &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Updating Pip2"&lt;/span&gt;
          pip2 list &lt;span class="nt"&gt;--outdated&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;freeze | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 1 | xargs &lt;span class="nt"&gt;-n1&lt;/span&gt; pip2 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt;
          &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Running pip update again, because that's necessary"&lt;/span&gt;
          pip2 list &lt;span class="nt"&gt;--outdated&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;freeze | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 1 | xargs &lt;span class="nt"&gt;-n1&lt;/span&gt; pip2 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt;
  &lt;span class="k"&gt;fi

  if &lt;/span&gt;_has_updates pip3&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
          &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Updating Pip3"&lt;/span&gt;
          pip3 list &lt;span class="nt"&gt;--outdated&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;freeze | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 1 | xargs &lt;span class="nt"&gt;-n1&lt;/span&gt; pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt;
          &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Running pip update again, because that's necessary"&lt;/span&gt;
          pip3 list &lt;span class="nt"&gt;--outdated&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;freeze | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; 1 | xargs &lt;span class="nt"&gt;-n1&lt;/span&gt; pip3 &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-U&lt;/span&gt;
  &lt;span class="k"&gt;fi

 &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Finshed updating"&lt;/span&gt;
 ~/bin/fancy-prompt/outdated-packages.sh
&lt;span class="o"&gt;}&lt;/span&gt;


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

&lt;/div&gt;



&lt;p&gt;I ended up having to run pip2 and pip3 run their updates twice, because it almost always would end up with more packages that needed updating.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;FANCIER!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No.  I spent about an hour a day for a week tweaking this, and I've exhausted my setup time.  In the future, I could enhance this to make it easier to add more package managers, or to swap them out and make it run on Linux.  That, however, is a job for my next laptop.&lt;/p&gt;

&lt;h1&gt;
  
  
  Next Steps
&lt;/h1&gt;

&lt;p&gt;If you want, you can download my scripts from (Github)[&lt;a href="https://github.com/ThatJoeMoore/fancy-prompt" rel="noopener noreferrer"&gt;https://github.com/ThatJoeMoore/fancy-prompt&lt;/a&gt;]. I don't promise any kind of help or support, but I might make future tweaks.&lt;/p&gt;

&lt;p&gt;I also suggest that you take a look at &lt;a href="https://github.com/magicmonty/bash-git-prompt" rel="noopener noreferrer"&gt;bash-git-prompt&lt;/a&gt;. It makes your git repos delightful ðŸ˜.  I've even developed an emojified theme for it, because of course I did.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusions, and maybe some Deep Thoughts
&lt;/h1&gt;

&lt;p&gt;I realize that this is a completely ridiculous first post here, but I do actually have a reason for writing such a ridiculous post.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Do you regret spending so much time on something so trivial?&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nope. Even after almost a month, seeing these little emoji makes me happy.  Perhaps the shine will wear off eventually, but until then, this was totally worth it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;But WHY!?!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because it makes me happy.&lt;/p&gt;

&lt;p&gt;So now we get to the deep part of my post.&lt;/p&gt;

&lt;p&gt;Being a developer can be stressful. Often, there's a lot riding on us, and it usually doesn't come with much glory or recognition beyond our immediate team members.  You hand a client a beautiful program on a silver platter, and they complain that it's not platinum and diamond-encrusted.  We work late hours, we lose sleep to keep apps running, we consume far too much pizza (stop trying to make sure that my project team can still be fed with two pizzas!).&lt;/p&gt;

&lt;p&gt;We all need to find ways to take care of ourselves, physically, mentally, and emotionally. Some may take their lunch break to exercise. Some may get happiness from having a nice standing desk, or a collection of Lego Star Wars sets, or by having pictures of their kids on their desk. For me, it involves emojifying my bash prompt.&lt;/p&gt;

&lt;p&gt;If you just want to be a developer, sure, go ahead, skip the little things. But I think most of us also want to be a Human Being, and that involves more than code.  If my experience shows me anything, it's that the best code comes from those who take the time to be a well-rounded person, and who find ways to make themselves happy throughout the day. To mangle a line from Mary Poppins, "A spoonful of sugar helps the developer stay sane."&lt;/p&gt;

&lt;p&gt;And that's why, sometimes, I spend too long playing with something ridiculous, like emojifying my prompt.  I do more than that to keep myself happy, but these little things can help keep the happy going throughout the occasional long day. Find the dumb things that work for you, and do them.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>emoji</category>
      <category>life</category>
    </item>
    <item>
      <title>Hi, I'm Joseph Moore</title>
      <dc:creator>Joseph Moore</dc:creator>
      <pubDate>Thu, 23 Feb 2017 20:12:36 +0000</pubDate>
      <link>https://dev.to/thatjoemoore/hi-im-joseph-moore</link>
      <guid>https://dev.to/thatjoemoore/hi-im-joseph-moore</guid>
      <description>&lt;p&gt;I have been coding for 10 years.&lt;/p&gt;

&lt;p&gt;You can find me on Twitter as &lt;a href="https://twitter.com/ThatJoeMoore" rel="noopener noreferrer"&gt;@ThatJoeMoore&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I live in Provo, Utah.&lt;/p&gt;

&lt;p&gt;I work for Brigham Young University&lt;/p&gt;

&lt;p&gt;I mostly program in these languages: Java and Javascript (browser and node).&lt;/p&gt;

&lt;p&gt;I am currently learning more about functional and reactive programming. I dream of the day when I can convince my team to use a functional language like Clojure or Haskell.&lt;/p&gt;

&lt;p&gt;Nice to meet you.&lt;/p&gt;

</description>
      <category>introduction</category>
    </item>
  </channel>
</rss>
