Noisy Neighbor Perfect is a verb, not a noun.

Idea To App Part 2

Introduction

We were pretty meta in part one of this series – running through a typical thought process and common techniques for turning ideas into something tangible… including how to come up with ideas in the first place, thinking about workflow once you have one, and mocking a UI that lets us start visualizing a Minimum Viable Product).

This is where things start to get fun! We can finally delve deeper into code (just in case CSS can’t actually be called code, we’ll start a bit of JavaScript as well). As we said in Part 1, there’s a lot of ground to cover… Just in case you forgot, here’s the planned breakdown:

Let’s get started!

Slight Detour: Dev Environment

In Part 1 I promised to share details on the dev environment we’ll be using for this project. The first thing you need to check out is the GitHub repo. Go browse that now if you’d like.. I’ll wait. When you’re done, clone it so you can follow along:

$ mkdir -p ~/src; cd ~/src
$ git clone https://github.com/deadlysyn/chowchow.git

Back? Cool. This has been documented by others far better than I ever could, so we’ll keep it brief… If you have questions, there are lots of great references available for Docker in general (one of the most amazingly well-documented projects around!), as well as specific guides for Node.js development using Docker…like these, just to share a few:

Compared to those, ours is quite simple…but we have a couple things key things in common. Here’s our Dockerfile:

FROM node:latest

RUN mkdir -p /app
WORKDIR /app

COPY package.json /app/
RUN npm install
RUN npm install -g nodemon

EXPOSE 3000

CMD [ "npm", "start" ]

Key things to note:

  • We just pull the latest Node.js image as a starting point (you might want to lock this down to a specific version for a production app)
  • We create /app within the container as our working directory (more on this later)
  • We copy package.json from the current directory (e.g. our cloned GitHub repo) and install all project dependencies with npm install
  • We carefully put dependency bits toward the top of the Dockerfile to maximize cache hits
  • We EXPOSE port 3000 within the container (more on this later)

Next we use what may be the world’s tiniest Makefile to make building and running our image easier (isn’t it amazing how useful old tools can be?):

DOCKER=/usr/local/bin/docker
IMG=chowchow

build:
	$(DOCKER) build . -t $(IMG)

run:
	$(DOCKER) run --rm -v $(PWD):/app -p 3000:3000 \
		-e IP="0.0.0.0" \
		-e API_KEY="${API_KEY}" $(IMG)

This lets us make build to build an image from our Dockerfile, and make run to bring up our app in the container on our machine. Note how we use -p to map port 3000 on our machine to port 3000 in the container. This means you can browse localhost:3000 to access the application during development.

IP=0.0.0.0 just gets the application listening on all available network interfaces (if you simply bind to 127.0.0.1 aka loopback, you won’t be able to access the application from outside the container). API_KEY deserves more explanation, but I want to save that for Part 3: Backend and APIs so we don’t muddy the waters too much here. The big takeaway for now is that you can pass environment variables (defined in your shell when running Docker) into the container to control application behavior.

The --rm avoids leaving old versions of our container around (we want to rebuild each time we make a change), and -v maps our current directory into the container’s /app we created in the Dockerfile.

The last piece of this puzzle is understanding the start command in our package.json. Let’s look at that:

{
  "name": "chowchow",
  "version": "1.0.0",
  "description": "find food fast",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon app.js"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/deadlysyn/chowchow.git"
  },
  "author": "Mike Hoskins",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/deadlysyn/chowchow/issues"
  },
  "homepage": "https://github.com/deadlysyn/chowchow#readme",
  "dependencies": {
    "body-parser": "^1.18.2",
    "ejs": "^2.5.7",
    "express": "^4.16.2",
    "express-session": "^1.15.6",
    "memorystore": "^1.6.0"
  }
}

When you first npm init a new project, there will be no start command. We’ve added "start": "nodemon app.js" which causes the npm start command in our Dockerfile to run our application under supervision of nodemon (If you haven’t heard of nodemon, see here and here). This causes the application to restart anytime we change JavaScript source within the project so we can easily preview changes.

For the most part you can ignore the rest of the fields for now, but if you haven’t worked with package.json before and want a deep-dive, check out the official documentation.

Project Hierarchy

It always helps to know how a project is laid out so you can follow along, and we’ll delve deeper into this in the next part of the series where we focus on Node.js and Express particulars.

For now, since this is a small project, you can just browse the repository to get a feel for things. The main thing is awareness of what a view is (loosely, a template rendered when users access specified URIs or routes served by our application), where those live (in the aptly named views directory), and where assets like CSS reside (inside the public directory). Here’s a simplified tree view of the project, with only areas we’ll work with in this part of the series:

.
├── Dockerfile
├── Makefile
├── app.js
├── package.json
├── public
│   ├── css
│   │   ├── main.css
│   ├── js
│   │   ├── bowser-191.min.js
│   │   ├── fontawesome-all-5.js
│   │   ├── home.js
│   │   ├── list.js
│   │   └── random.js
│   └── media
│       ├── chowchow.png
│       ├── favicon.png
└── views
    ├── catchall.ejs
    ├── home.ejs
    ├── list.ejs
    ├── partials
    │   ├── footer.ejs
    │   └── header.ejs
    └── random.ejs

Our First Refactor

In Part 1 we discussed how I started with Bootstrap when mocking the UI because it was familiar. The bootcamp I went through leveraged it heavily, it’s one of the most popular CSS/JS frameworks, and it’s well-documented and easy to use… but for such a simple app, it was a huge dependency to pull in.

I also found myself fighting the framework. The main requirement we have is a responsive design. Responsive design is largely about ensuring your application works well on a variety of screen sizes (desktop, mobile, etc.).

Responsive web apps use technologies like media queries and viewport to make sure that their UIs will fit any form factor: desktop, mobile, tablet, or whatever comes next. -MDN

Since we are building a web application on a journey toward native mobile (where our app would be most useful), responsiveness is key. While Bootstrap provides responsiveness, it’s achievable with native CSS as well. It felt a bit silly to pull in Bootstrap for what boiled down to a single row and column. I found myself with something like this in each view:

<div class="container">
    <div class="row">
        <div class="col">
        </div>
    </div>
</div>

I decided to rip out the framework and use this as an opportunity to jump on the CSS Grid bandwagon. Nothing against Bootstrap, but there is a time and a place for frameworks. I also simplified the interface quite a bit, removing another dependency (css-toggle-switch) which is amazing in itself but felt too heavyweight for our needs.

Dependencies are a way of life (who wants to re-invent the wheel), but it’s important to be mindful of what you pull into your project. Make judgement calls on whether the added complexity makes sense in your context.

In the end, it turned into a bit more work than originally thought… mostly because I had started using things like special formatting and classes to define buttons and other UI elements. What started as I’ll just rip out a few divs turned into quite a journey, but the end result was a responsive UI, fewer dependencies, FAR less CSS users are forced to download (a whopping 96.43% less CSS to be exact!), and a lot of learning. :-)

Getting Stylish

As mentioned, we’re going to use CSS Grid to layout our simple app. Be warned, I’m learning with you and not a CSS master by any means. I’m sure everything I show you can be done more efficiently – if you spot something, unicast me or share in comments. I’ve refactored things to reduce duplication in some places, but know it could be cleaner.

We’ll also be using another CSS layout mechanism called Flexbox. Before going further, take time to review the following resources so it’s easier to follow along:

You also want to get familiar with your browser’s developer tools. In Firefox, you can simply hit F12 to toggle the developer tools. The DOM and Style Inspector (the Inspector tab along the top menu bar of the development tools window) and JavaScript Console will be invaluable friends during your web development journey.

Inspector in Action

The Inspector has a lot of useful features, the screenshot highlights a few I use regularly:

  • Responsive design mode lets you mimic a variety of screen sizes and bandwidths
  • You can select the small pointer icon to focus on elements you click
  • For grid layouts, you can click the small grid icon beside display: grid to show an outline of the grid (very useful!)
  • You can shift-click color swatches to see alternate formats like rgb and hsl

Before we dive into the restyle… let’s look at the original UI we mocked in Part 1:

ChowChow UI 0.1

Pretty hideous, huh? Well, I felt pretty good about my design to start – after all, it was my brainchild… but looking closer it had a lot of shortcomings (other than the 90’s-flashback punch-out effect I felt so clever devising).

Fight the urge to get attached to your designs. In the spirit of Agile/Lean, be courageous – experiment and change often!

Look and feel aside, it simply wasn’t responsive…and didn’t work at all on smaller screens. Even if it had looked amazing, it didn’t meet our primary requirement (finding decent food quickly while traveling with a mobile device). Loading the app in the Inspector’s Responsive Design Mode with a resolution of 320 x 480 was the final nail in the coffin.

Aww so sad...  :-(

Let’s see how we can improve the mobile experience…

Defining our Views

In Part 1 we defined a workflow consisting of three views… the home view (or index page) which allowed users to modify purposefully-minimal (e.g. not cumbersome on mobile) inputs, a random view to display information about a randomly selected restaurant, and finally a list view which allows users to browse additional choices if the random selection is not up to par. Here’s an artist’s rendition (hopefully you’ve caught on to the inherent snark):

Getting Sketchy

Let’s walk through the HTML and CSS to build each of these in a responsive manner… Keep in mind that while I am trying to separate this series into sections for convenience, back-end bits inevitably bleed into front-end. You will see a bit of EJS template detail and even helper functions as we work through the markup… I’ll explain important bits, but we’ll also go deeper into all that in the next part of this series.

Common Elements

Before we get into specific views, I want to share some common elements all views will leverage. There’s more we could do, but the idea is to factor out anything we can as reusable classes to reduce duplication in our CSS. I want to share this now since it will avoid confusion later when we reference some of these in our markup:

/*
 * common elements
 */

.button {
    align-items: center;
    box-sizing: border-box;
    color: #d32323;
    display: flex;
    font-size: 4vh;
    font-weight: 700;
    justify-content: center;
    letter-spacing: 2px;
    text-align: center;
}

.button:hover {
    box-shadow: inset 0px 0.1em 0.1em rgba(0, 0, 0, 0.4),
                inset 0px -0.1em 0.1em rgba(0, 0, 0, 0.4),
                inset 0.1em 0px 0.1em rgba(0, 0, 0, 0.4),
                inset -0.1em 0px 0.1em rgba(0, 0, 0, 0.4);
    color: #000;
    transition: all 0.25s;
}

.footer {
    grid-area: footer;
    opacity: 0.7;
}

.text-shadow {
    text-shadow: 0px 2px 4px rgba(211, 35, 35, 0.7),
                 0px -2px 4px rgba(211, 35, 35, 0.7),
                 2px 0px 4px rgba(211, 35, 35, 0.7),
                 -2px 0px 4px rgba(211, 35, 35, 0.7);
}

Nothing special here, we simply define a bit of custom CSS to get back the concept of a button we lost when we ditched Bootstrap and define the footer which will be a common element on every page.

Note the use of the transition property to make the hover effect feel a bit better (to me at least). Also note the use of a relative font-size based on display size (using the vh unit which we’ll discuss later). That’s key to getting the responsiveness we need. grid-area won’t make sense just yet, read on!

Home

The home view gives our users a starting point. In the next part of this series we’ll talk about details of defining and rendering the view in the Express framework, here we want to focus on the style. The first thing we want to do is use CSS Grid to define a layout we can map our UI elements onto:

#home {
  display: grid;
  grid-template-columns: auto;
  grid-template-rows: repeat(4, auto) 5vh;
  grid-template-areas: "find" "price" "drive" "donate" "footer";
}

Be sure to review the CSS Grid Tutorial shared above so this makes sense… I’ve simply defined an id we can use in our HTML which defines a grid consisting of five rows and a single column. I then name each of the rows for easy reference later. You might be more familiar with another format for grid-template-areas, something like:

grid-template-areas:
    "find"
    "price"
    "drive"
    "donate"
    "footer";

While useful for visualizing multi-column layouts, it seems a bit unwieldy for simple pages like this where we only have a single column so I merged everything onto one line. Either way works just fine. Which do you prefer?

Why do this in an id? Because we will have a specific grid layout per view, and only need to use each layout once (a requirement for CSS ids vs classes like Bootstrap’s row and column which can be re-used).

OK, great…so how do we use this? While refactoring, I got over-zealous (I might regret it later) and decided to eliminate as many extraneous wrappers as possible. I didn’t want the equivalent of <div class="container"></div>, so chose to map the grid definitions to my <body> elements. Here’s what we see in views/home.ejs:

<% include partials/header %>
<body id="home">
  ...

I like the way this effectively uses the body as the container, but it could be less flexible based on your requirements. Just keep in mind that this is just one way to define our grid, and not even one I thought up myself…I just found it novel. Do you, or is it setting off alarm bells? :-)

Now that we’ve got a grid, and it’s mapped onto our page… let’s add some content. We do that by assigning elements to grid areas. Our simplified home view is just four buttons (find food, adjust price range, toggle walk/drive, or make a donation) which should fill the screen and be responsive:

/*
 * home view styling
 */

#find {
    background-color: rgba(255, 0, 0, 0.4);
    grid-area: find;
    height: 21vh;
    max-height: 180px;
    width: 100%;
}

#price {
    background-color: rgba(255, 125, 0, 0.4);
    grid-area: price;
    height: 21vh;
    max-height: 180px;
    width: 100%;
}

#drive {
    background-color: rgba(255, 255, 0, 0.4);
    grid-area: drive;
    height: 21vh;
    max-height: 180px;
    width: 100%;
}

#donate {
    background-color: rgba(125, 255, 125, 0.4);
    grid-area: donate;
    height: 21vh;
    max-height: 180px;
    width: 100%;
}

We use grid-area to map elements onto our grid, and relative sizes for height and width to get the desired responsiveness (vh stands for view height). This mostly gets the desired behavior on the platforms I can easily test (Chrome, Firefox and Safari on MacOS, as well as mobile Safari on an iPhone 7), but is almost certainly not the best way to do it (finding all the neato ways designs break on various devices is part of the fun of being a web developer).

When I first tested this, I tweaked the heights to look just right in developer tools at 320 x 480. When I tested on the iPhone, it was close but didn’t quite fit the screen… Not a major surprise given resolution differences, but don’t assume every device is happy just because something looks good in a simulator. Be aware it can take some patience and a bit of trickery to find a good middle ground that mostly works on all devices. :-)

Let’s look at the home view’s markup:

<% include partials/header %>
<body id="home">
    <script defer src="/js/home.js"></script>
    <form action="/random" method="post" id="form">
        <input type="hidden" name="price" value="$$">
        <input type="hidden" name="drive" value="true">
        <input type="hidden" name="latitude" value="0" min="0" step="Any" id="latitude" required>
        <input type="hidden" name="longitude" value="0" min="0" step="Any" id="longitude" required>
    </form>
    <div class="button" id="find">
        <span id="loading">
            <i class="fas fa-spinner fa-pulse fa-lg" aria-hidden="true"></i>
        </span>
    </div>
    <div class="button" id="price">
        <i class="fas fa-dollar-sign fa-lg" aria-hidden="true"></i>
        <i class="fas fa-dollar-sign fa-lg" aria-hidden="true"></i>
    </div>
    <div class="button" id="drive">
        <i class="fas fa-dot-circle fa-lg"></i>
    </div>
    <div class="button" id="donate">
        GIVE <i class="fas fa-heart fa-lg" aria-hidden="true"></i> FOOD
    </div>
<% include partials/footer %>

The price and drive inputs are controlled by JavaScript associated with the divs which have similarly-named ids. We’ll look at more of that code in a future part of this series, or you can check it out in GitHub. As we’ll see below, we auto-fill the latitude and longitude inputs using results we get back from geolocation. When the user submits the form, these will be passed along to Yelp’s API. A future feature could be allowing the user to enter a zip code if location is unavailable.

Overall, we can see how removing Bootstrap has really simplified our layout. Our markup now neatly corresponds to design elements. No more extraneous containers, rows or columns let alone class names like col-xs-12 which are not at all obvious unless you’ve spent time with the documentation!

Those weird <% %> tags signify EJS (Embedded JavaScript templates)… The small amount of EJS here is pretty clear, we simply include a header and a footer to avoid having to type common bits like <html></html> in every view. Where you see things like <%= var %>, we’re simply including a variable from our application in the template. One thing we need to understand is a bit of magic included in views/partials/header.ejs:

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, height=device-height, initial-scale=1.0, maximum-scale=1.0">
        <title>chowchow</title>
        <link rel="icon" type="image/png" href="/media/chowchow.png">
        <link rel="stylesheet" href="/css/main.css">
        <script src="/js/bowser-191.min.js"></script>
        <script defer src="/js/fontawesome-all-5.js"></script>
    </head>

A key part of our responsive design is the viewport we define via a meta tag (which should be the first meta tag in your head after charset). This allows the UI to scale based on the target device’s screen-size. If you want to learn more about that, check out the MDN documentation, as well as this oldie but goodie.

This all works reasonably well on a small screen, but looks a bit odd in a larger desktop browser. To fix that, we’ll need to leverage CSS media queries a few places in our design. You could get as fancy as you like here, I simply add some spacing and give our buttons a different shape on larger screens:

@media screen and (min-width: 600px) and (min-height: 800px) {
    html {
        padding-top: 5vh;
    }

    body > div {
        border-radius: 10px 40px;
        margin-top: 1vh;
    }
}

With that, we have something that looks decent on small and large screen sizes:

Media queries adjust our layout based on display size.

We’ve covered a lot of ground in this section, but luckily for us many of these concepts will be reused in each view. This will save us a lot of time as we work through the remaining sections.

Random

In views/random.ejs we’ll use CSS grid for our layout again…but define a completely different grid suited to our needs. The main feature will be a large content area, followed by a few buttons (route, call, home, list). Going further, we’ll re-define the grid based on screen size for an optimal layout. Here’s the random view’s CSS:

/*
 * random view styling
 */

#random {
    display: grid;
    grid-template-columns: auto;
    grid-template-rows: repeat(4, auto) 5vh;
    grid-template-areas:
        "content content"
        "route route"
        "call call"
        "back list"
        "footer footer";
}

@media screen and (min-width: 600px) and (min-height: 800px) {
    #random {
        grid-template-rows: repeat(5, auto) 5vh;
        grid-template-areas:
            "content"
            "route"
            "call"
            "back"
            "list"
            "footer";
    }
}

#random-content {
    grid-area: content;
    height: 60vh;
    max-height: 350px;
    overflow: auto;
    width: 100%;
}

#random-biz {
    background-color: rgba(0, 0, 0, 0.4);
    color: #fff;
    font-weight: 700;
    line-height: 0.5em;
    overflow: hidden;
    padding: 1em 0;
    width: 100%;
}

#js-random-route {
    background-color: rgba(255, 0, 0, 0.4);
    grid-area: route;
    height: 11.66vh;
    max-height: 100px;
    width: 100%;
}

#js-random-call {
    background-color: rgba(255, 125, 0, 0.4);
    grid-area: call;
    height: 11.66vh;
    max-height: 100px;
    width: 100%;
}

#js-random-home {
    background-color: rgba(255, 255, 0, 0.4);
    grid-area: back;
    height: 11.66vh;
    max-height: 100px;
    width: 100%;
}

#js-random-list {
    background-color: rgba(125, 255, 125, 0.4);
    grid-area: list;
    height: 11.66vh;
    max-height: 100px;
    width: 100%;
}

(Psst…I see duplication sneaking into our code. Make a note on the refactor list!)

One thing worth noting is the js-* naming convention for some of the elements. That’s a signal those elements are tied to JavaScript (which we’ll get to later). In a project of this size it’s not a big deal, but will make maintenance easier as our codebase grows. It’s another fine idea I didn’t have to come up with myself! Here’s the markup:

<% include partials/header %>
<body id="random">
    <script defer src="/js/random.js"></script>
    <% if (biz.image_url) { %>
    <div id="random-content" style="background-image: url('<%= biz.image_url %>'); background-position: center; background-size: cover">
    <% } else { %>
    <div id="random-content" style="border: 1px solid #000">
    <% } %>
        <div id="random-biz" class="text-shadow">
            <p><%= shorten(biz.name) %></p>
            <p><%= biz.location.display_address[0] %></p>
            <p><%= parseFloat(biz.distance / 1609.344).toFixed(1) %> miles</p>
            <p><a href="<%= biz.url %>"><%- stars(biz.rating) %></a> (<%= biz.review_count %> reviews)</p>
        </div>
    </div>
    <div class="button" id="js-random-route">
        <i class="fas fa-map-marker-alt fa-lg"></i>
    </div>
    <div class="button" id="js-random-call">
        <i class="fas fa-phone fa-lg"></i>
    </div>
    <div class="button" id="js-random-home">
        <i class="fas fa-home fa-lg"></i>
    </div>
    <div class="button" id="js-random-list">
        <i class="fas fa-th-list fa-lg"></i>
    </div>
    <script>
    window.onload = function() {
        updateMapURL("<%= biz.location.display_address %>")
        updatePhone("<%= biz.phone %>")
    }
    </script>
<% include partials/footer %>

We’re starting to leverage EJS more and more… working around the fact that biz.image_url may or may not exist (sometimes a business doesn’t have an image defined), so we avoid render errors by wrapping associated output in a conditional. We also use a helper function (more on those later) and parseFloat along with toFixed to convert meters to miles.

On the last few lines, we take the biz object’s (a Yelp API search result) address and phone number to pass into a couple JavaScript functions we’ve created to generate the correct links for routing to or calling selected businesses. Since there is only one result displayed on this page, we use the window.onload event to set everything up when the view is rendered. Be sure to check out updateMapURL and updatePhone in public/js/random.js.

Overall, this is still a fairly clean view and achieves the desired responsiveness:

Random View

List

Last but not least, we want to let the user browse additional choices when the randomly-selected restaurant is unsatisfactory. Enter the list view. What happens behind the scenes is our app takes the top five restaurants near the user (perhaps less, if there are not actually five, adequately-ranked, open choices nearby!), displays a random choice, and then saves the remaining choices for later use if the user wishes to browse through them (we don’t need to hit the API again).

By now CSS Grid is old-hat, here’s our styling for the list view:

/*
 * list view styling
 */

#list {
    display: grid;
    grid-template-columns: auto;
    grid-template-rows: auto auto 5vh;
    grid-template-areas: "content" "home" "footer";
}

#list-content {
    align-content: center;
    display: flex;
    flex-wrap: wrap;
    grid-area: content;
    height: 70vh;
    max-height: 700px;
    width: 100%;
}

#js-list-home {
    background-color: rgba(255, 255, 0, 0.4);
    grid-area: home;
    height: 15vh;
    max-height: 100px;
    width: 100%;
}

.list-item {
    color: #fff;
    display: flex;
    font-weight: 700;
    height: 17vh;
    justify-content: center;
    max-height: 150px;
    width: 100%;
}

.list-item-route {
    background-color: rgba(0, 125, 0, 0.4);
    display: flex;
    height: 100%;
    width: 15%;
}

.list-item-content {
    align-content: center;
    display: flex;
    flex-wrap: wrap;
    width: 70%;
}

.list-item-content > div {
    display: flex;
    justify-content: center;
    width: 100%;
}

.list-item-call {
    align-items: center;
    background-color: rgba(0, 0, 125, 0.4);
    display: flex;
    height: 100%;
    justify-content: center;
    width: 15%;
}

@media screen and (min-width: 600px) and (min-height: 800px) {
    .list-item {
        border-radius: 10px 40px;
    }

    .list-item-route {
        border-radius: 10px 0 0 40px;
    }

    .list-item-call {
        border-radius: 0 40px 10px 0;
    }
}

This is still evolving. Simple things like the color of our buttons should be consistent for good UX (in my opinion), but some of the colors we chose in our initial views don’t look good when directly overlaid on our background images. I’m still tweaking this. :-) Also note the slightly different syntax for grid-template-rows. auto auto 5vh was simply shorter in this case, so I didn’t use repeat.

The main thing to note is how we are mixing the familiar grid layout with display: flex. This lets us easily map higher-level containers onto the grid, then neatly arrange the list item components (route button, business detail, call button) within those containers. Nothing groundbreaking, but keep in mind you can mix and match layout styles as needed.

Here’s our markup:

<% include partials/header %>
<body id="list">
    <script defer src="/js/list.js"></script>
    <div id="list-content">
        <% results.forEach(function(biz) { %>
        <% if (biz.image_url) { %>
        <div class="list-item" style="background-image: url('<%= biz.image_url %>'); background-position: center; background-size: cover">
        <% } else { %>
        <div class="list-item" style="background-color: rgba(0, 0, 0, 0.5);">
        <% } %>
            <div class="button list-item-route" onclick='route("<%= biz.location.display_address %>");return false'>
                <i class="fas fa-map-marker-alt fa-fw fa-lg"></i>
            </div>
            <div class="list-item-content text-shadow">
                <div><%= shorten(biz.name, 20) %> (<%= parseFloat(biz.distance / 1609.344).toFixed(1) %>mi)</div>
                <div><a href="<%= biz.url %>"><%- stars(biz.rating) %></a></div>
            </div>
            <div class="button list-item-call" onclick='call("<%= biz.phone %>");return false'>
                <i class="fas fa-phone fa-fw fa-lg"></i>
            </div>
        </div>
        <% }) %>
    </div>
    <div class="button" id="js-list-home">
        <i class="fas fa-home fa-lg"></i>
    </div>
<% include partials/footer %>

We do a similar trick as seen in the random view to feed business details to route and call functions, but use onclick since we’ll build up a list of many results. Check those out over in public/js/list.js.

Don’t let the pursuit of perfection make you afraid to ship anything! Ship and iterate.

By now, you’ve likely noticed some styling sneaking into our markup (things like the background-color on the list-item div). This is an anti-pattern we’d like to avoid, but we’ll just make a note to refactor later. This was an easy way to get started.

List View

Client-Side JavaScript

Phew, that was quite a tour! We are almost ready for a break so you can digest all this a bit more. Better yet, clone the repo and play with it yourself! Before we part, I want to start our journey into the world of JavaScript… aside from the bits of EJS you’ve already seen, we actually need some client-side code to make our app useful.

First, we need to be able to obtain the user’s location so we can search nearby. Second, we need a way to know what platform our app is running on so we can do things like plot routes in native map applications vs using the browser. Last but not least, since we’ve already had them shoved in our face a few times while working through the views, let’s go over the template helper functions (shorten and stars) just to see what they do.

Geolocation

Luckily for me, geolocation in browsers has matured a lot since I last experimented. For our needs we can simply leverage navigator.geolocation, wrap that in a conditional to ensure location is enabled (if not, redirect to a view which gives a helpful message), and provide a couple callbacks to define behavior on success and failure.

Peeking inside public/js/home.js, we see:

if (!navigator.geolocation || !navigator.cookieEnabled) {
    location.href = '/nolocation'
} else {
    navigator.geolocation.getCurrentPosition(geoSuccess, geoError, geoOpts)
}

function geoSuccess(position) {
    latitude.value = position.coords.latitude
    longitude.value = position.coords.longitude
    loading.innerHTML = 'FIND <i class="fas fa-utensils fa-lg" aria-hidden="true"></i> FOOD'
}

function geoError(error) {
    loading.innerHTML = '<p style="font-size: 0.7rem">Unable to retrieve location:<br> "' + error.message + '"</p>'
}

It’s not strictly required, but we also define a geoOpts object to configure geolocation behavior:

var geoOpts = {
        enableHighAccuracy: true,
        maximumAge: 300000,
        timeout: 20000
    }

For more details on geolocation, be sure to check out the MDN docs which give useful examples and go over available options. It starts to feel like 80% of our time as developers will be split across Google and MDN. :-)

When the home view is loaded, this ensures geolocation is enabled, attempts to get a high accuracy location result, displays a spinning icon while waiting, updates some form elements on the page with the latitude and longitude if successful (we’ll go over feeding that into the Yelp API later), otherwise displays a useful error message. Pretty cool, huh?

Platform Detection

When the user clicks or taps a route button, we want to use the native map application vs opening everything in the browser. This should give a richer user experience, and also gets us closer to feeling like a mobile app. To do that, we need two things…

First, we include bowser to help us detect what platform the end user is running (this adds a new dependency, but one that meets a specific requirement and avoids re-inventing a rather complicated wheel).

Second, we use a bit of Google-engineered knowledge to manipulate the URL assigned to the route buttons in our app based on detected platform. Let’s put these together:

function updateMapURL(address) {
    let maps = 'https://maps.google.com?q='

    if (bowser.iphone) {
        maps = 'maps:q='
    } else if (bowser.android) {
        maps = 'geo:0,0?q='
    }

    route.addEventListener("click", function() {
        location.href = maps + encodeURIComponent(address)
    })
}

As we saw above, we receive address from the rendered views (random and list do similar things), and encodeURIComponent ensures it’s in valid URI format.

Template Helpers

In the EJS templates above, I’m sure you noticed references to the shorten and stars functions. We make these available in our templates by attaching them to app.locals, an Express feature allowing us to leverage the power of custom functions within our templates.

These let us shorten the length of strings used in our templates (some can get quite long causing misbehavior on small screens) and represent ratings as graphical stars. Note the use of a default value for shorten’s len argument, that way things mostly work as expected if we forget to pass a second argument:

/*
 * template helpers
 */

// truncate long strings; only add ellipsis on truncation
app.locals.shorten = function(str, len = 30) {
    if (str.length > len) {
        str = str.substring(0,len).trim() + '...'
    }
    return str
}

// convert rating number to svg stars courtesy of fontawesome.io
app.locals.stars = function(num) {
    let stars = ''
    let style = 'style="color: #FFD700"' // "gold"

    for (let i = 0; i < num-1; i++) {
        stars += '<i class="fas fa-star fa-lg" ' + style + '></i>'
    }

    if (num%1 != 0) {
        stars += '<i class="fas fa-star-half fa-lg" ' + style + '></i>'
    }

    return stars
}

Final Thoughts

With that, we’ve come to the end of Part 2… If you’ve read this far, your tenacity is commendable. :-) Factoring out Bootstrap was a bit more work than anticipated, but with only a few hundred lines of custom CSS (a significant reduction) we were able to build a responsive UI that is functional on a variety of devices. In exchange for our effort, we’ve got fewer dependencies to worry about and greatly simplified markup.

We saw that media queries are a powerful tool in our quest for responsive design, but just scratched the surface. As it exists, we’ve only tested a small number of potential device types and window sizes. There is still a lot of room for improvement, but at least we’re headed in the right direction!

We still have a sort of hybrid web app vs a true mobile application, but it’s a journey with plenty of milestones ahead… Also, while we accomplished a lot, we just built an interface – how does it actually do real work? Next time around we’ll dive into the back-end and talk about integrating with Yelp’s API. Their API is so complete and well-documented it will be a simple case, but we’ll get to touch on some common themes which will carry over when building more complex applications interacting with multiple back-end services.

In case you haven’t already figured it out, the SVG background pattern we’re using comes from heropatterns.com (which I came across while listening to the Full Stack Radio podcast). The icons we leverage for our app’s buttons come from Font Awesome 5.

Continue to the next part of this series…

Idea To App Part 1

Introduction

Happy New Year! 2018 is here, and for the first blog post of the new year I wanted to start by exploring the creative process and one possible technology stack (Node.js, Express, React) behind turning an idea into an app. Since we need to cover a lot of ground to get from start to finish, this is going to be my first multi-part series… Here are the currently planned sections:

Since there are so many great How To articles, I want to start by giving you context so you understand where this one fits… I am not a professional developer, though my background is CSCE and I work in technology (SRE). I am new at building modern web applications, new at working with Node and Express (an online bootcamp and a few months of tinkering), still learning React (via another online course), and have yet to actually publish a mobile app.

There will undoubtedly be imperfections and room for optimization in everything you read here – keep in mind this series is about sharing the overall process with an absolute beginner vs something that will help seasoned professionals. If you are a pro and have advice, please share comments or unicast me. I am happy to share credit and adjust content as needed so future readers have the most helpful experience possible!

Ideation

No matter how fun your tech stack, it’s hard to produce something useful if you don’t start with a good idea… Wikipedia defines ideation as the generation of new ideas. This sounds obvious and easy until you find yourself brainstorming and hitting a brick wall!

Ideation is the creative process of generating, developing, and communicating new ideas, where an idea is understood as a basic element of thought that can be either visual, concrete, or abstract. -Wikipedia

There are schools of thought around how to generate new ideas, one of my favorites is design thinking. One technique is using the double diamond approach. The key concept is using both divergent and convergent thinking to generate many possible solutions within a problem space. This is 180 degrees from a common mistake – jumping straight to a solution – which can often blind you to related but potentially more interesting (or valuable) problems to solve.

Finding Our Idea

If we were a team brainstorming from a blank slate, double diamond in conjunction with customer conversations would be a natural starting point… for the sake of brevity, I already have an idea to offer up. Before getting into the what of my idea, how did I come up with the why?

Very few people or companies can clearly articulate WHY they do WHAT they do…people don’t buy WHAT you do, they buy WHY you do it. -Simon Sinek

Equal parts education and inspiration, Simon Sinek’s Start with Why: How Great Leaders Inspire Everyone to Take Action gives lots of insight into the power of understanding why… as you use double diamond or other techniques to generate ideas, those with the strongest why’s will often resonate the loudest – can you identify with the use case(s), do you feel excited about using the idea in day-to-day life (not just the fun of developing it)?

Necessity is Your Friend

As frequent travelers and foodies, my wife and I were early adopters of the now ubiquitous food review site Yelp. As the site grew in popularity, more businesses and reviews were added. While good in other ways, information overload began setting in – what started as a way to quickly find good food began to get bogged down.

A scenario played itself out repeatedly… we would be in a new city, usually just off a flight and starved to death, then leverage Yelp to find some chow. Most of the time we would struggle to narrow down cuisine, try to improve our chances by prioritizing places with better reviews, and often end up deadlocked in endless opines over a handful of places that all seemed equally pleasant.

Wouldn’t it be nice if there was an app that could optimize this scenario for speed? Ideally allowing minimal user input (so it’s not cumbersome on small screens), and abstracting away all the typical things we care about (good reviews, open now, etc.)?

Birth of an App

Fleshing it out a bit more, I envisioned something like this:

  • The app would need to be aware of user location (search nearby)
  • The main feature should be “good food in a single click” (optimize Time To Food)
  • Minimal user input, should be optional (walk or drive? cheap or expensive? etc.)
  • Present random choice from top-rated options that are open
  • Allow other choices if random choice is not satisfactory
  • Make it easy to call or route to choices

It’s amazing how the imagination runs wild as you start to think through a new idea. I can almost see the workflow… One other thing I wanted to tie in was some sort of potential for social good. I wasn’t sure how yet, but I wanted it to be easy for anyone using the app to help feed others through charitable donations.

As programmers, we have a magic power, and with great power comes great responsibility. We need to think about the good we can do with this ability. -Aaron Swartz

Okay, so what to call it? While not something to get hung up on, apps need a name, logo, and social profile if they have any chance of being found useful. While not impossible to change, settling on a high level brand for your app early will mean less re-work and confusion later. My first unoriginal idea was simply calling it Hungry, but partially to avoid annoying my friends, and partially at the urging of my wife (“It’s cuter!”), opted for ChowChow as the name (unfortunately chowchow.io was taken, I am soooo bummed).

Depending on what you’re building, a logo and social presence could require real investment… Since this is a simple app with no financial motivation, I cribbed a logo from iconarchive.com (it’s available in several sizes, and will double as a good favicon), created a GitHub repo, and moved along.

Building a Mental Model

Now that we have our why and what, it’s time to think about how. Don’t bog yourself down unnecessarily (we’ll get into implementation detail later), but do you understand the big pieces of your vision at a high level? Is it just stitching together readily available APIs? Is there a lot of client-side logic, or is it mostly back-end? Are there known patterns you can re-use to solve parts of the problem, or will you need to create a lot from scratch?

For me, there were a few big questions:

  • How will we get the user’s location?
  • Does Yelp have an API?
    • If so, how do I access it?
    • What information is available?
  • Once a business is found, what’s the best way to plot a route?
  • From a UX perspective, how can we allow unobtrusive user input?
  • For charitable donations, can I do that over an API?

If I could answer those, it would increase my confidence the idea was realistic… we’ll answer each of these as we work through the series, but be aware that I took time to define and reflect on these questions. You should for your idea as well.

Technology is notorious for engrossing people so much that they don’t always focus on balance… -Paul Allen

Like so many things in life, it is about balance… don’t get stuck in analysis paralysis. However, a little extra time spent upfront will avoid the frustration of getting bogged down later by letting you focus on clearly defined pieces of the problem and ensuring you don’t head in an entirely wrong direction architecturally. That said, if you do find yourself in a bog… don’t panic. We’ve all been in a similar place. Take notes, find a better way, learn from it, share your experience!

Defining the Workflow

At this point, I found myself thinking a lot about workflow. My goal was to hone existing skills (Node, Express by building a web-based version of the app. Later (as I improve in React, and figure out Reactive-Native), I want to produce a mobile version.

I had a few concerns:

  • What should the “happy path” actually look like?
  • How would I build a web app requiring minimal refactoring for mobile?
  • How could I ensure any controls were easy to use on small screens?

I began working through the first with ye olde pen and paper. Whiteboards are great too. The point is really to step back from the keyboard for a moment and brainstorm the user experience. You could mock this out in your favorite graphics editor, but I prefer a medium where you can quickly erase and rework. This consumed a few pages for me, including several that got tossed, but the picture shows how informal and messy it can be…anything to solidify the idea of what you are trying to build.

A Messy Sketch

You can see simple sketches of the main app screens with some zoom detail, and notes on must haves vs roadmap features. With this, I was able to visually step through the path a typical user would follow. This is a starting point that will be continuously refined, but helps the idea start to feel tangible. Sketches of individual screens, elements, arrows/flow between screens, dialogs or other callouts – anything is fair game, but use several pages to ensure they are legible. Nothing is worse than forgetting what your awesome diagram actually meant a few days later. For larger apps, a single sheet of paper might represent a page, so you can tape them to the wall, rearrange as needed, and visualize the big picture.

Throughout brainstorming you will have countless ideas…write them down, and organize by must haves vs roadmap. Holding items in your head risks forgetting them, and clutters mental space best focused on developing what must come next.

With a happy path in mind, I started thinking more about the last two items since they are intricately related… While the back-end would continue to evolve, I wanted to avoid constantly refactoring the UI. The way I wanted to avoid major refactoring was by building a web UI that mimicked the mobile experience as much as possible. I made some hard choices about UI dimensions, content (nothing extraneous) and controls (everything has to work easily on a small touch screen).

I mostly worried about smaller screens because I knew the most useful place for the app would be on mobile devices… This isn’t an enterprise app with a need to seamlessly support every known platform, but I did want a smooth experience whether in a modern browser or mobile device. With the above in mind, I decided the best way to really tackle this was to ship something fast and start testing on real mobile devices. Sure beats worrying!

Mocking the UI

At this point I was ready to move past workflow and really start defining look and feel. Based on your skillset, you might prefer to work bottom up (focus on the back-end first), but I’ve always found a top down process to be more natural. With this approach, we start by generating key UI elements, then wrap code around them.

I used the git repo while working through this part (laying things out where I thought they would naturally live in production), but want to share the project skeleton and GitHub repo in the next article when we setup our Docker development environment. Hopefully that’s not too confusing. You might not even have a repo ready at this point, aren’t really writing code (just focusing on presentation via HTML and CSS), and directory structure could even change as you figure out what components to use and how to stitch things together.

Home Sweet Home

For me, the obvious focus was the index page…the literal starting point. If I could get this halfway decent, it could serve as a template for the rest of the app (while content will change on each screen, we want general UI consistency). It also offered an opportunity to tackle one of the key challenges, figuring out what user input would be allowed and control types that would be easy to navigate on small screens with touch controls.

A lot could be said here, but since I was developing for the web at this point simple HTML and the CSS box model were my tools of choice. I started by simply laying out a few divs and experimenting with size and position. A common trick I find useful at this point is giving all elements a hideous border (usually something like border: 1px dashed red) to make subtleties for things like floats and overflows easier to see. While useful, be aware these extra pixels will take up space in your design.

CSS Box Model

While working around browser bugs or platform inconsistencies can be infuriating (we’ll touch on that in a later part of this series), I find initial UI prototyping rewarding. By now you’ve got a solid idea, and get to slap Legos together until they match your mental picture. It takes patience, but the satisfying part is instant feedback as you add HTML, adjust CSS, reload, repeat… make small changes, and verify often. Goes well with coffee. :-)

While working through the layout, make friends with your browser’s native web developer tools (cmd-alt-i for Mac users). This is not only useful for Javascript debugging in the console (we’ll use that later!), but also identifying styling discrepancies, specificity conflicts, or other oddities via the element inspector.

Element Inspector

Don’t spend so much time on the UI you never get the back-end working. It sounds silly, but this is an easy black hole to get stuck in. You will almost always think of small tweaks, so remember the Pareto Principle – the average user will only touch ~20% of your UI. Put another way, users will derive 80% of their value from 20% of your development. If you hope to make the 20% of your code users actually care about 80% better, you need to ship sooner so tweaks can be based on feedback.

You will almost always think of small tweaks, so remember the Pareto Principle – the average user will only touch ~20% of your UI.

At this point I made a mistake that wasn’t the end of the world, but might be useful to share… Since I wanted a mobile-friendly, responsive design I jumped to a familiar framework (Bootstrap 4). After all, I’d just gone through a class where we used it heavily, my own website uses it, and it is both mobile-first and full-featured.

Full-featured was the problem… this isn’t anti-Bootstrap, it’s a great framework. This also isn’t a Bootstrap problem, it’s a general concern with frameworks. There’s a time and a place. About halfway through this app I realized what I’d done: I’d pulled an 800-pound dependency into my project when all I needed was CSS Grid. Sometimes it’s not obvious, but as I worked through the rest of the UI, I found myself using framework elements, overriding styles, replacing elements with completely custom versions since the resulting CSS was shorter, then wondering if I was committing sacrilege by not only using native components – it shouldn’t be this hard, I was fighting the framework. Oh well, lesson learned. Add one to the roadmap list!

Yes, that's EJS, forgive me. :-)

Now that I had a handful of responsive divs laid out, I wanted to apply some color and texture. Since I’m not a graphic designer by trade, I jumped over to uigradients.com (grabbed a background gradient for my container div) and heropatterns.com (grabbed a conveniently themed SVG fill pattern). While browsing free resources, I ran what CSS I had through autoprefixer. For UI icons, I pulled in Font Awesome 5.

Last but not least, after a bit of research, I decided simple toggle switches would be aesthetically pleasing and easy to use on small screens. I came across a CSS-native implementation and decided to give it a try. We’ll go over importing and using css-toggle-switch in the next article of this series.

With basic HTML, CSS, and a handful of free utilities I had the skeleton of a UI… here’s a teaser of the home screen we’ll get to build next time:

ChowChow Home Screen

Conclusion

Starting only with a simple desire to bolster existing skills by building an app, we considered user needs (in this case our own) to identify a valuable idea (find our why). Finding an idea was easier for us since it was based on personal experience, but it’s worth noting it resonated because it was based on an understood problem…a place we as programmers could leverage technology to meet a need. Finding such opportunities generally requires talking to and/or observing your target users.

With our shiny new idea in hand, we fleshed out how it could work at a high level. Next, we took a moment to think about the major components of our app and did research to understand if our idea was feasible. Finally, we hammered out a sensible workflow based on what we wanted the app to do, and did simple mocks in HTML and CSS…now we have something to start wrapping code around!

That was quite a journey for an introduction… but it should lay a nice foundation for the next article in the series where we will go deeper into the UI design and address other client-side concerns like location and user agent detection. We’ll also devote a small section to project setup (sharing our directory structure to make following code examples easier), and discuss the simple Docker-based development environment.

I hope you’ll follow the series as our simple app idea continues to evolve…

Thanks for reading!

Continue to Part 2…

Learning From Apple

I’m one of many in the industry to note that Apple had a pretty bad time last week… while it’s not the first time they’ve had security flaws in their products (for the record, I’d be more suspicious of products that “never” find any flaws – are they just not used much, or hiding something?), the embarrassment came from the nature of the more recent flaws and, more importantly to me, the interactions between a couple of those flaws.

I want to reflect on this experience to ask a rhetorical question, and promote a particular practice I’ve been diving into over the last couple years. However, before going there, I want to state a few things so I don’t just come off like an Apple basher.

  • I’ve used Apple in some form for over a decade, having owned a Mac Classic, Mac Pro workstation, a handful of MacBooks, and converting from Android to iPhone (mostly because of the fractured marketplace and inconsistent update model for Android devices).
  • I’m a UNIX geek at heart (many years saw me running BSD variants on PC hardware), and the love child of BSD, Mach and NeXT producing what we now lovingly call Darwin, OS X or macOS combined with Apple’s expertise at hardware engineering produced what I consider the almost-perfect developer laptop (the only way it could be better is with fully open hardware, but this is an industry-wide vs Apple problem).
  • In general, I trust and respect Apple’s commitment to security and privacy. Anyone not living under a rock in recent years has seen this in action. They are usually great at fixing issues quickly, and doing so consistently across a diverse product line.

With that out of the way, there have been several incidents which are out of character for a company of Apple’s size… These are even more embarrassing given their commitment to security, and the fact their increasing market share clearly implies any issues they encounter have a broad impact on the global community. Here’s a brief tour:

  • While typically fast, Apple has occasionally taken years (yes, YEARS) to fix highly-publicized, “critical” vulnerabilities reported by researches. Consider the 3+ year MTTR for the (in)famous finfisher trojan.
  • More than an anomaly, careful study of Apple’s patch history reveals a startling trend… while there is a seemingly constant flow of “updates” to my iDevices, Krebs on Security found that on average 91 days elapsed between the date that a security researcher alerted Apple to an unpatched flaw and the date Apple shipped a patch!
  • Timing aside, the very nature of the most recent vulnerabilities has been shocking. The most surprising was the root login vulnerability which was the easiest root exploit I’ve ever seen.
  • OK, OK…but apple only took 18 hours vs 3+ years to fix that one. Phew. Unfortunately, anyone who applied the fix without already having installed macOS 10.13.1 had the fix reverted when they upgraded. Others reported, even when running 10.13.1, the fix didn’t work until reboot. Oh, and there was no indication a reboot was required… and the update broke file sharing. Yeesh.
  • Other notable vulnerabilities this year include nasty password stealing and giving away your passwords for free.
  • Beyond this short list, the increasing reliance on biometrics (despite security expert’s consistent warnings about the down-sides) raises privacy concerns. While Apple has assured us the technology is mature enough to be trusted and our immutable biometric signatures are safe by investing in Secure Enclaves and Differential Privacy, recent media coverage says otherwise.

With a few minutes in your favorite search engine, you’ll find a lot more to be worried about. I’ve been trying very hard to write blog posts rather than academic theses so I’ll wrap up with a statement (because hey, this is my blog where I get to be opinionated) and the promised question…

While the above is certainly worrisome, having vulnerabilities isn’t the problem. I expect any innovative and heavily used product to have lots of vulnerabilities found. The issue is response time and effectiveness of patches. If either of these suffer, users are at serious risk. If Apple is truly committed to security and privacy, they need to do better on both fronts. I think it’s time Apple pause feature development for a moment, do a quick retrospective on recent events, and devote more time to improving security and stability. Think Snow Leopard (a OS X release dedicated to stability and optimization). While it’s easy to say features trump everything else, and technical debt should just be paid down as you go… security is a much-touted feature (not just an after-thought!), and it’s clear the debt has been accumulating. We need more Snow Leopards.

My question is… does Apple practice TDD (Test Driven Development)? Unfortunately, I’ve fallen out of regular contact with the few folks I know working for Apple today so my direct questions have gone unanswered. I wasn’t as thorough as I’d like, but some comments around the Internet imply use of TDD is quite limited if practiced at all. I highly respect Terry, and the fact that post is as much his opinion as Apple’s, but also disagree with the comments “TDD is more applicable when there are standards bodies driving adherence to international standards” and “BDD is pretty useless.” I think both of these show the need for better understanding of TDD.

In his seminal work on TDD, Test Driven Development By Example, Kent Beck talks about the “no time for testing death spiral” when pressured to deliver features – the more stress you feel, the less testing you do. The less testing you do, the more errors you make. The more errors you make, the more stress you feel.

Some have mixed feelings over TDD (debates often get more emotional than fact based), and while nothing is a panacea, I honestly don’t see how a company of Apple’s size, trying to innovate at an increasingly frantic pace, with a ballooning user base ensuring greater impact from software defects, can operate sanely without strict adherence to TDD principles.

As a loyal customer and security advocate concerned over recent events, I sincerely hope best practices like TDD and pair programming are embraced across Apple’s engineering department. With most of my professional life and PII housed on Apple devices in one form or another, my future quality of life depends on it.