Give Your Product a Leg Up

The Clever Beagle blog is a great place to sharpen up your skills, keep your inspiration high, and stay motivated when building your product. All it takes is popping in an email below to get our latest and greatest in your inbox.

Ryan Glover
Look For Simple Fixes First

Look For Simple Fixes First

Over the past few weeks, I've been focused on building out the infrastructure for a new product we'll be offering at Clever Beagle soon, Pupgrades.

Because Pupgrades will be available on a pay-per-item basis, I decided that it'd be worthwhile to integrate the marketing and checkout flow into the existing Clever Beagle app (the one we use with our mentorship customers to track work on their products).

In theory this was a great idea, but in practice there was a little hiccup: the app wasn't built with SEO in mind; all of the HTML for the app was rendered on the client only. While Google has grown more friendly to client side rendering over the years and a few hacks exist to do pre-rendering of your app so search engines can see it, those techniques all leave something to be desired.

Fortunately, Meteor—the platform the Clever Beagle app and our mentorship program is based on—added support for server side rendering (SSR) last year. Instead of relying solely on client side rendering and hacks to serve up rendered HTML to search engines, now, it's possible to actually render HTML on the server-side first.

Why does that matter? Well, that rendered HTML is what Google uses to describe and rank your sites. The more accessible and visible that HTML is, then, the better.

Having worked with SSR previously in Meteor for a tutorial on our sibling site (as well as with a few CB mentees), I knew the gist of how to get it implemented. What I didn't know, though, was how to fix one of the glaring problems when implementing it: the flash of unstyled content (FOUC).

You've no doubt seen this before. When visiting a site, for a few split seconds you see the unstyled HTML show on the page and then after a few seconds see the styled version "snap in." Kind of like this:

The evil flash of unstyled content
The evil flash of unstyled content (FOUC)

Blech. No fun. Having researched this a bit, I knew that tools like styled-components—a library that helps you write per-component CSS for React—gave you a means for compiling your CSS on the server—just before you return your HTML, you compile your stylesheets and inject them into the header. The result? No FOUC!

This is all well and good but styled-components presents a unique dilemma: it's a bit cumbersome to migrate a large application to. If you start out with styled-components, it's a wonderful idea. A migration, though, has the potential to take awhile.

Looking to launch our Pupgrades offer in a few weeks, I started to bite my nails; a move to styled-components was potentially damning to staying on schedule.

Then it dawned on me: why not try something simpler. The problem is the flash of content, not that our CSS was particularly unwieldy. While migrating to styled-components would have been one way to solve the problem, it was far from the most economical. Thinking about the problem, a previous conversation with another developer came to mind. "Just make the body invisible until the content loads!" Well, duh.

With just three lines of code, the entire problem was solved. Three lines. Here's the distilled version:

[...]

if (Meteor.isClient) import './App.scss';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = { ready: false }; // This one.
  }

  componentDidMount() {
    this.setState({ ready: true }); // This one.
  }

  render() {
    return (
      {/* This style tag below */}  
      <div className="App" style={{ visibility: this.state.ready ? 'visible' : 'hidden' }}>
        [...]
      </div>
    );
  }
}

[...]

In just five minutes of work (ten total if you consider the thinking that led to this), the problem was solved quite eloquently. No major rewrites. No significant change in process. A dirt simple, easy to "set and forget" solution.

FOUC be gone!
FOUC be gone!

When you're building your own products, always step back before committing to grandiose solutions. More often than not, there's always a far simpler, inexpensive way to solve your problem. Look for those first and only ratchet up your investment if you're certain it's necessary.

Want to get a heads up on Pupgrades when they launch?

Get on The Mailing List
Matt Michel
Using lodash to retrieve nested values

Using lodash to retrieve nested values

Cannot get property 'foo' of undefined

This is probably the most common class of error that I see in the JavaScript console, it happens when we try to access a property that doesn't exist. For example, if we have a couple of objects that look like this:

const idealObject = {
    name: {
        first: 'Scooby',
        last: 'Doo',
    },
    color: 'brown',
};

const subOptimalObject = {
    color: 'red',
};

const first1 = idealObject.name.last; // 'Doo'
const first2 = subOptimalObject.name.last; // Error: Cannot get property 'last' of undefined

We can get around this by checking for the existence of the fields we are referencing. If it exists then we return it, otherwise return the string 'n/a':

const first2 = subOptimalObject.name && subOptimalObject.name.last || 'n/a' ; 

This works but will get progressively messier with more complicated objects. This is where lodash comes in. We replace the above line with:

_.get(subOptimalObject, 'name.last', 'n/a');

Check out the lodash docs for lots of other cool time savers like this.


It Won't Be a Straight Line

​Excellent talk by Garrett Dimon, formerly of Sifter. In it, Garrett shares his story about what he learned from selling Sifter in light of a medical issue which prompted his left leg being amputated. He shares a number of anecdotes—some very humbling, heartwarming moments that are worth watching for—one of which stood out:

"Recurring revenue is really its own form of disability insurance. If you can work in recurring revenue, it can be a life changer."

Here, Garrett makes a point about how when he was unable to focus on Sifter, the recurring revenue it generated without his day-to-day involvement made a difficult experience far easier to contend with.

From one of his slides:

"Pain and discomfort are part of the process. Go right up to your limit. Push past it. Suffer a little. Then do it all over again."

A great point for anyone working on a product. Garrett makes a nod to hitting plateaus and having to have the discipline to push past them—an invaluable skill to have as the founder of a product.

Highly recommend putting this on and learning from Garrett and his experience.

Ryan Glover
Your First Game

Your First Game

Something I see a lot of people get wrong when they're building their first piece of software is they treat it like it's their only chance.

  • If they don't get the design just right, they're done.
  • If the code isn't perfect and bug free, they're goners.
  • If they don't have hundreds, or thousands, or millions of customers, there's no way they'll ever succeed.

But the thing they don't consider is how ridiculous all of that is. For contrast: consider the first anything you've done.

Was the first game of as a kid a massive success? Were you an instant-MVP, being scouted by the top teams in the league? Maybe your thing was baking cookies. Was the first batch award-winning?

Of course you weren't! And no, they tasted like crap (sorry, your parents were lying—they weren't "yummy").

Software is no different. You have no choice but to be terrible at first in order to get really, really, good. It doesn't matter if you're building an app, learning the violin, or shooting three-pointers. You're going to suck at first.

But, and this is important: if you understand this, you can be immensely successful in the long-run. If you can ignore the fact that you suck now and have some humility, the only thing standing between you and being great at what you want to do is work and time.

Shooting a thousand three-pointers in a row, carefully watching how different stances and ways you roll the ball off your hand make the ball travel through the air differently.

Playing the same sonata to the point where you never want to hear it again. Listening to variations in your playing and how little mistakes can actually become stylistic idiosyncracies that people love you for.

Building a ton of different app ideas, not to make them a success but to improve your craft and learn what it takes to actually build a product. To see what building an app that "works" feels like versus building a product that sells.

Don't look at your first game as your last. If you really want to do this, you have to be willing to accept that you may have to play hundreds or thousands of games before you're actually playing at the level you want. Mindset is everything here.

Humility from the author: I'm nowhere near where I want to be with my craft, but I show up every day and keep trying. You can do the exact same thing.

Scott Galloway on Brand Strategy

This one came across my radar from following the Gartner L2 mailing list (recommended for miscellaneous business insights). Really enjoyed this video introducing a thought process for thinking about branding. The most important part being thinking about how someone interacts with or experiences your product before, during, and after purchase, and how those moments can be used to strengthen the branding behind your product.

Ryan Glover
The Value Is in the Execution Not the Idea

The Value Is in the Execution Not the Idea

Everybody thinks they're a genius, me included.

When we get an idea—a real idea, one that we think has merit—it's easy to get affixed in its glow. This, we think, is the idea and we have to protect it like a diamond-encrusted, Fabergé egg. Not only is the idea valuable; it's delicate. Keeping it in obscurity is a way for it to retain its glow and value. Shrouded in mystery, an idea is priceless and impenetrable. In our own eyes, it retains every drop of its brilliance.

We convince ourselves that it's so valuable, too, that we dare not utter a word about it. When it comes to creating something that we deem to have economic potential, we convince ourselves that we need to either keep our mouths shut or get someone to sign an NDA. "Those sons of bitches will steal it, I tell ya! It's that good!"

Fortunately, the chances are pretty high that those sons of bitches won't steal it. The truth, which is equally warming and frightening is that ideas are pretty worthless. What really matters is someone's ability coupled with their interest and resources to execute on an idea.

By ability, we mean technical aptitude—do they have the skillset necessary not just to implement the idea but also to see its edges and nuances (read: do they have a vision)? An idea is made up of hundreds if not thousands of sub-ideas and we're assuming someone can see all of the details and have the vision to make them a reality.

By interest we mean do they even care about the idea? Is it captivating in a way that makes them want to drop everything to focus on it? Even if they start, will they actually finish it? How likely are they to follow through? It's a shame, but most folks are full of hot air and give up halfway through.

And by resources, we mean time and money. Do they have the physical hours in a day as well as the cash—whether to fund the project directly or their own life indirectly—to execute on the idea? If we stretch the meaning of resources, do they have the emotional and psychological resources to handle the trials and tribulations that are inevitable with building something?

In my eyes, this is why very few ideas even see the light of day: this trifecta of ability, interest, and resources is very difficult to come by. The likelihood that someone will successfully "steal" your idea is so infintissemally low, it's rarely worth the mental gymnastics to prevent yourself from sharing it with others in the first place.

Speaking personally, the best ideas I've had are actually the end result of conversations where I shared the idea with someone and they helped to put a twist on it. The core service of the company who's blog you're reading right now wasn't even going to be a subscription service! In retrospect, had I kept my mouth shut and executed on the exact idea I had, you may not be reading this.

To make a point, here are the top three ideas I have in mind right now:

  • A marketplace with parts to help people build their products.
  • An NPS scoring app for collecting customer feedback.
  • A tipping system for developers that leverages the command line and cryptocurrency.

What you may notice is that these already exist in one form or another. This is how much I believe in execution. If you study companies like Apple, this is the exact line of thinking behind their own success: it's not the idea, it's how well you do it.

If you start working on your idea with this attitude, you'll quickly realize that the idea has very little to do with whether or not you will be successful. Instead, it's how you execute on the idea both in the short-term and the long-term. Unless your goal is to build a crappy version of your idea: you have very little to worry about. Focus on getting it built, not about how special the idea is.

Share your idea with a mentor and start building.

Get Started
Ryan Glover
Improving Performance in Pup with MongoDB Indexes

Improving Performance in Pup with MongoDB Indexes

When you're ready to take your Pup-based app into production, one of the most important things you can do is add indexes to your MongoDB collections. An index is a way to improve how quickly MongoDB is able to search through the data in your database when performing a query. If your app has a lot of reading (e.g., an infinite scroll list of posts like on Twitter), indexes are a must-have to save you from frustrating performance headaches.

For example, in Pup, when a user logs in we redirect them to the /documents page where a list of the documents that belong to them are displayed.

Inside of the publication used to retrieve those documents, we rely on the user's ID in the Meteor.users collection (provided to us as this.userId from within our publication). Whenever a user creates a new document in Pup, their user ID is automatically set as the owner field on that document. Inside of our publication, then, we perform the following MongoDB query to retrieve the current user's documents:

/imports/api/Documents/server/publications.js
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Documents from '../Documents';

Meteor.publish('documents', function documents() {
  return Documents.find({ owner: this.userId });
});

Without an index, when MongoDB performs this query, it will perform what's known as a collection scan, looking at the entirety of every document in the Documents collection and check to see if the owner field is equal to the current user's ID. This is problematic because as our Documents collection grows, MongoDB will have to search through more and more data to find the specific documents that we're querying for.

"Indexes are special data structures [1] that store a small portion of the collection’s data set in an easy to traverse form. The index stores the value of a specific field or set of fields, ordered by the value of the field."

Indexes Documentation from MongoDB

With an index, we can create a copy of our data with just the specific fields that we're querying for, making this query far less expensive for MongoDB to perform. In other words, on a large collection, adding an index can have a serious impact on performance.

Creating an Index

In the above example, we query for documents using the owner field, so we want to create an index on that specific field. As of Pup 1.4.0, this is simplified by using the createIndex() module:

/imports/api/Documents/server/indexes.js
import createIndex from '../../../modules/server/create-index';
import Documents from '../Documents';

createIndex(Documents, { owner: 1 });

Here, we import the createIndex function from /imports/modules/server/create-index.js along with the collection that we're trying to create an index for—in this case, Documents. Next, we pass the collection instance as the first argument/value to createIndex and then an object describing the index we're trying to create. In this case, we pass { owner: 1 } to say "create an index for the owner field for all documents in the Documents collection, sorted in ascending order (a -1 value would flip this to be in descending order)."

Now, whenever we perform a query that retrieves documents using just the owner field, MongoDB will first look in its index to find the documents we're after as opposed to a performing a full collection scan. Great! But what about more complex queries? Fortunately, MongoDB can utilize multi-field indexes as well. Let's say we updated our publication code above to look at both the owner field and the title field:

/imports/api/Documents/server/publications.js
import { Meteor } from 'meteor/meteor';
import { check } from 'meteor/check';
import Documents from '../Documents';

Meteor.publish('documents', function documents() {
  return Documents.find({ owner: this.userId, title: 'Hello' });
});

Even though we've defined an index for queries on the owner field, as far as MongoDB is concerned, this query is completely different. Following our logic from earlier, our updated index would look like this:

/imports/api/Documents/server/indexes.js
import createIndex from '../../../modules/server/create-index';
import Documents from '../Documents';

createIndex(Documents, { owner: 1, title: 1 });

Keep in mind: we want to define an index for each unique type of query that we perform. So, if in one part of our code we queried with just { owner: this.userId } and in another we also queried with { owner: this.userId, title: 'Hello' }, we'd want two separate indexes, like this:

/imports/api/Documents/server/indexes.js
import createIndex from '../../../modules/server/create-index';
import Documents from '../Documents';

createIndex(Documents, { owner: 1 });
createIndex(Documents, { owner: 1, title: 1 });

Now, for queries following these patterns, MongoDB will be able to find the documents we're after much quicker than before. Though it can take a bit of work, stepping through your code to identify all of your unique queries and writing indexes for them like we see above can help to speed things up significantly.

Want to ensure you ship a solid product?

Build Your Product With a Mentor