Backbone rule learned during a JavaScript Refactoring

One of my goals for 2015 is to up my typing speed. I’ve never been that great a typer and I’m currently averaging about 75 WPM and shooting for 100 WPM by the end of the year. As part of my goal I’ve been practicing with some really great web based type tutors, games and tools. If you’re interested in playing some online typing games for free, you should definitely checkout typeracer and ztype.

In the interest of building a tool and forwarding my goal I’ve created WPM Challenge. It’s a typing challenge similar to typeracer. I learned a couple interesting things while building this little application that I’m excited to share with you.

Some background: This project is built using Rails and Backbone. The real time stuff uses Pusher and the auth is mostly omniauth gems and some stuff in the User model.

This will be a story about some javascript refactoring. If you’d like to see the before and after check these commits out.

track.js before
track.js after

My first stab at this problem used a Track backbone model, which represented the content being typed. This quickly attracted many other functions and ended up being a junk drawer of functionality, handling, joining new cars to the track, as well as about a dozen method that were delegated to an instance of WordChecker, who’s responsibility it is to manage current state of the typer as they advance through the content. At first I didn’t write any tests, in fact this wasn’t intended to be a real time collaborative typing challenge at all. At some point I decided it would be cool to race my friends and keep track of WPM for each track.

I admittedly wrote zero javascript tests at the beginning and was quite fearful of making this big refactoring. In a later post I’ll talk about how I used the teaspoon gem to get up and running writing tests for javascript pretty quickly in Rails, but for now, know that I just started adding javascript tests at the start of this refactoring.

The Problem

The problem was that the TrackDetail view and the Track model were taking on way too much responsibility. These problems manifested themselves in two ways. 1) I found this super odd code smell where the Track model was delegating more than half of its methods down to a WordChecker. I think the goal here was to keep the word checking logic abstracted away from the view as much as possible, but in the end the view was calling a handful of wordChecker methods just on the Track. IMO pretty shitty.

Smell 1: feature envy

// app/assets/javascripts/models/track.js
CodeRacer.Models.Track = Backbone.Model.extend({
  // ...
  currentWordCount: function () {
    return this.wordChecker().currentWordCount();
  },

  checkWord: function (word) {
    return this.wordChecker().checkWord(word);
  },

  checkLetter: function (letter) {
    return this.wordChecker().checkLetter(letter);
  },

  wordComplete: function (word) {
    return this.wordChecker().wordComplete(word);
  },

  backSpace: function () {
    return this.wordChecker().backSpace();
  },

  content: function () {
    return this.wordChecker().render();
  },

  moreWords: function () {
    return this.wordChecker().moreWords();
  },

  wordCount: function () {
    return this.wordChecker().wordCount();
  },

  percentComplete: function () {
    return this.wordChecker().percentComplete();
  },
  //...

Smell 2: Too Much Setup in View#initialize

2) The TrackDetail initialize method also became super gross. It was initializing a ton of objects and doing a lot of setup for the race. Check it out:

CodeRacer.Views.TrackDetail = Backbone.View.extend({
  initialize: function () {
    this.listenTo(this.model, 'sync', this.render);
    this.timer = new CodeRacer.Models.Timer();
    this.model.join(this.timer); // joins the race as the current user and sets up pusher channel
    this.carsIndex = new CodeRacer.Views.CarsIndex({
      collection: this.model.cars()
    });
    this.timerView = new CodeRacer.Views.TrackTimer({
      timer: this.timer,
      track: this.model
    });
    this.listenTo(this.timer, 'go', this.startRace);

    this.leaderBoardView = new CodeRacer.Views.LeaderBoard({
      collection: this.model.leaders()
    });
  },
  // ...

Something better

In my Rails controllers, I’ve been trying to follow the rules laid out by Sandi Metz in a Ruby Rogues episode a few months back. When attempting to keep methods short and keep only one instance variable per controller I’ve noticed that my views and controller actions are simpler, but more interestingly a lot of the logic is pushed into new ruby objects that do not fall into the Rails architecture. Some people call these service objects, call them what you want to, they’re just objects. My new found confidence in creating objects outside of the framework has led me to create two new classes in this project that do not live directly in the Backbone architecture, but provide great encapsulation of my data an business logic.

The rule I’ve discovered during this extraction: Only refer to one model or collection in a backbone view. I extracted logic from the TrackDetail view and from the Track model and put them in a class called Race. The Race class expects a track, timer, and a wordChecker and is the one thing that the view refers to. It handles working with the word checker and forwards/delegates events raised from the Track or from the Timer object. Check it out:

New Race class. Notice the dependency injection, made this especially testable 🙂

CodeRacer.Models.Race = function (track, timer, wordChecker) {
  this.track = track;
  this.timer = timer;
  this.wordChecker = wordChecker;
  this._valid = true;

  this.listenTo(track, 'change:content', function () {
    this.wordChecker.setContent(track.get('content'));
  });
  this.forwardEvents();

  track.fetch();
  track.join(timer);
};

CodeRacer.Models.Race.prototype = {
  forwardEvents: function () {
    this.listenTo(this.track, 'sync', function () {
      this.trigger('sync');
    });

    this.listenTo(this.timer, 'go', function () {
      this.trigger('go');
    });

    this.listenTo(this.wordChecker, 'over', function () {
      this.over();
    });

    this.listenTo(this.wordChecker, 'next', function () {
      this.next();
    });
  },

  over: function () {
    this.track.notify(this.wpm(), this.wordChecker.percentComplete(), true);
    this.timer.stop();
    this.trigger('over');
  },

  cars: function () {
    return this.track.cars();
  },

  leaders: function () {
    return this.track.leaders();
  },

  content: function () {
    return this.wordChecker.render();
  },

  checkWord: function (word) {
    this._valid = this.wordChecker.checkWord(word);
    return this._valid;
  },

  valid: function () {
    return this._valid;
  },

  wpm: function () {
    if (this.started() && this.timer.seconds > 1) {
      return (this.wordChecker.currentWordCount() / (this.timer.seconds / 60)).toFixed(2);
    }
    return 0;
  },

  next: function () {
    this.track.notify(this.wpm(), this.wordChecker.percentComplete());
    this.trigger('next');
  },
};

_.extend(CodeRacer.Models.Race.prototype, Backbone.Events);

TrackDetail refactored to use only one thing.

// app/assets/javascripts/views/track_detail.js
CodeRacer.Views.TrackDetail = Backbone.View.extend({
  initialize: function (options) {
    this.race = options.race;
    this.listenTo(this.race, 'sync', this.render);
    this.listenTo(this.race, 'go', this.startRace);
    this.listenTo(this.race, 'next', this.advanceWord);
    this.listenTo(this.race, 'over', this.gameOver);
    this.initializeSubviews();
  },

  advanceWord: function () {
    this.renderContent();
    this.clearInput();
  },

  initializeSubviews: function () {
    this.carsIndex = new CodeRacer.Views.CarsIndex({
      collection: this.race.cars()
    });

    this.timerView = new CodeRacer.Views.TrackTimer({
      race: this.race,
      timer: this.race.timer, // probably not necessary anymore
      track: this.model // probably not necessary anymore
    });

    this.leaderBoardView = new CodeRacer.Views.LeaderBoard({
      collection: this.race.leaders()
    });
  },
  // ...

I think the end result is much cleaner, and now that I’ve got some test coverage on WordChecker and Race, I’m much more confident in making changes. Also, moving forward and adding features, subViews to the TrackDetail view will be a breeze.

Backbone rule learned during a JavaScript Refactoring

App Landing Page for Ionic app

We’ve all been to sites that explain features of an app and entice you to visit the market place and download. If you’ve developed an app and are starting to build out a landing page, you might have googled “app landing page” and found some themes from themeforest.net. These are flashy designs that display the features of the app and expect the creator to drop in app screenshots that my slide around or transition as you move between features.

Something great about apps built with ionic framework is that you can demo the nearly full (minus device features) app right on your marketing page (not sure this is cool, legally). I’ve done that for a couple ionic apps: Pushbit and Insider AI and want to show you how.

These two pages are both being served from Rails apps, but you shouldn’t have any difficulty getting things working from your own server.

The key here is to run the ionic app in an iframe.

In Rails, there is a /public directory that contains static html. In /public I’ll create a directory called app and copy the contents of www to app.

In the html for the page, I’ll put the image of the device as the background, then an iframe pointing to the app’s index.html. The iframe should be in an iframe tag, but to comply with the blog formatting I’ve omitted the tag brackets.

<div id="phone">
  <img src="/assets/iphone6.png">
  iframe src="/app/index.html" frameBorder="0" class="screens" /iframe
</div>
App Landing Page for Ionic app

Lets do this!

Every place I’ve ever lived has had a different pace. I spent my childhood in a small mountain town in Tahoe called Kings Beach. KB has a super chill, very laid back pace. Everyone is either a tourist or in love with the mountains and lake. I would say, the area provides a nice lifestyle, especially for those interested in the out doors and relaxing.

Just before starting high school we moved to Reno, NV. Close by, but much more reasonable cost of living. The pace in Reno is definitely more upbeat than KB. More people, more hustle. There’s some budding tech but most dev jobs are for defense or gaming.

In 2012 I moved my family to San Francisco to accelerate my career, meet people and enjoy the scene as a techie. SF is MUCH faster paced than any where I’ve experienced. People pile onto MUNI and Bart heading to work or meetups. It seems that everyone wants to start a company, and everyone definitely drinks coffee like they’ve co-founded 3. The cost of living in the bay is so high, it’s hustle or move.

Many people like me have recently moved to the bay for tech in some way or another. It feels like ‘the homeland’ of tech. Some complain about gentrification, which I don’t really have an opinion about. It is exciting to think that the iPhone I’m composing this post on, the iOS, the app and keyboard, the fitbit on my wrist, we’re all created by BUILDERS, Engineers, Devs, Designers just a few miles from me. That feeling is super motivating and empowering.

Today is the day. A friend of mine just told me a story: Running across the Golden Gate Bridge. On one of the pillars is a placard with the names of the engineers who contributed to the construction. Those men and women have their name in history. At the time they were masters of their craft, in a place and time where the Golden Gate Bridge was necessary and they did it. Today is the day for US engineers to put our name in the record books. Today is the day to build incredible things that will help billions of people.

The impact we can make is profound and lasting. Go out and change the world! One line of code at a time 🙂

ggbridge

Lets do this!

ES6 Model layer for Angular.js

Many of the so called MV* frameworks have some explicit support for a model layer. When working in backbone.js, the model layer is very clearly defined in Backbone.Model and Backbone.Collection.

Instances of Backbone.Model and Backbone.Collection interact with a rest api using $.ajax. In the world of backbone there is a lot of buy in to the evented architecture. When a model or collection finishes fetching, the instance publishes an event which listeners can then act on. They have clear #save, #create, #fetch methods that hit the server and update the model accordingly.

Since working with angular I’ve explored a few solutions (Restangular and $resource) for managing the model layer, but was left wanting. I feel like the designers of angular wanted to be a bit more flexible at the model layer and as a result didn’t build in much on top of plain-old-javascript-objects (POJO).

As a result Angular has a component called Service which when injected into a function new’s up an instance of the Service object which might contain your custom logic for interacting with a backend API or with local storage.

I found myself repeating a lot of logic for each service and essentially rewriting much of the Backbone.Model class in my angular services. For example:

myApp.factory('Workouts', function ($http, $q, loc) {
    function url(id) {
      if (id) {
        return loc.apiBase + '/workouts/' + id;
      }
      return loc.apiBase + '/workouts';
    }

    function Workout(attrs) {
      this.id = attrs.id;
      this.completed_date = attrs.completed_date;
      this.workout_sets = attrs.workout_sets;
    }

    return {
      all: function () {
        var dfd = $q.defer();
        $http.get(url()).then(function (resp) {
          var workouts = _.map(resp.data, function (w) {
            return new Workout(w);
          });
          dfd.resolve(workouts);
        }, function (resp, status) {
          $rootScope.$broadcast('event:auth-loginRequired', status);
          dfd.reject(resp.data);
        });
        return dfd.promise;
      },
      get: function (id) {
        var dfd = $q.defer();
        $http.get(url(id)).then(function (resp) {
          dfd.resolve(resp.data);
        }, function (resp) {
          dfd.reject(resp.data);
        });
        return dfd.promise;
      }
    };
  });

The all method here was essentially the same across all services in the system and only differed slightly in cases where I was massaging the incoming data.

@shawndromat pointed me towards a pattern where using a Model factory might help by returning a constructor function that I could use for each of my Model layer classes.

Here’s a simplified (without caching) version of my Model factory:

  myApp.factory('Model', function ($http, $q, $window, loc) {
    return function (options) {
      var path = options.path;
      var url = loc.apiBase + path;

      class Model {
        constructor(attrs) {
          this.setup.apply(this, arguments);
          attrs = attrs || {};
          this.attributes = {};
          this.set(this.parse(attrs));
          this.initialize.apply(this, arguments);
        }

        get id() {
          return this.attributes.id;
        }

        initialize() {
        }

        setup() {
        }

        set(attrs) {
          for(var attr in attrs) {
            this.attributes[attr] = attrs[attr];
          }
          return this;
        }

        get(key) {
          return this.attributes[key];
        }

        parse(response) {
          return response;
        }

        save() {
          if(this.id) {
            return this.update();
          } else {
            return this.create();
          }
        }

        update() {
          var dfd = $q.defer();
          $http
            .put(this.url(), this.attributes)
            .then((response) => {
              this.set(this.parse(response.data));
              dfd.resolve(this);
            }, (response) => {
              dfd.reject(this);
            });
          return dfd.promise;
        }

        create() {
          var dfd = $q.defer();
          $http
            .post(this.url(), this.attributes)
            .then((response) => {
              this.set(this.parse(response.data));
              dfd.resolve(this);
            }, (response) => {
              dfd.reject(this);
            });
          return dfd.promise;
        }

        url() {
          if(this.id) {
            return url + '/' + this.id;
          } else {
            return url;
          }
        }
      }

      Model.url = () => { return url; };

      var _all = [];

      Model.all = function () {
        $http.get(url, { cache: true }).then((response) => {
          _.each(response.data, (data) => {
            if(!_.any(_all, (m) => {
              return m.id === data.id;
            })) {
              _all.push(new Model(data));
            }
          });
        });

        return _all;
      };

      Model.create = function (attributes) {
        var model = new Model(attributes);
        _all.push(model);
        return model.save();
      };

      return Model;
    };
  });

By injecting the Model class into other factories I can create constructor functions that override extend business logic, as well as extend interactions with the backend.

  myApp.factory('Workout', function (Model, WorkoutSet) {
    var Workout = Model({ path: '/workouts' });

    Workout.prototype.setup = function () {
      this.workout_sets = [];
    };

    Workout.prototype.parse = function (response) {
      if(response.workout_sets) {
        console.log('response has workout_sets');
        this.workout_sets = _.map(response.workout_sets, (s) => {
          return new WorkoutSet(s);
        });
        delete response.workout_sets;
      }
      return response;
    };

    return Workout;
  });

I would love to hear your comments and suggestions regarding this implementation. 🙂

ES6 Model layer for Angular.js

Towers of Hanoi in Scala

Here’s a rough solution here: https://gist.github.com/w1zeman1p/5bd2f6b0400d96f201da if you want to fork and play around.

I love building little games when first playing with new programming languages. For Scala I decided to write up this little game of Towers of Hanoi.

The most difficult part was definitely moving disks from one tower to another. Because Scala is both object oriented and functional, I could have stored the state of the towers with disks as mutable variables and modified these collections when moving a disk from one tower to another. However, I’m trying really hard to embrace the immutable data structure and functional side of Scala. Check out my Move method:

  def Move(from: Int, to: Int, towers: List[List[Int]]): List[List[Int]] = {
    if(!CanMove(from, to, towers)) {
      println("Can't move there")
      return towers
    }
 
    val disk = towers(from).head
    var i = 0;
    towers.foldLeft(List[List[Int]]())((r, l) =>
      if (i == from) {
        i += 1;
        r ::: List(l.tail);
      } else if (i == to) {
        i+= 1;
        r ::: List(List(disk) ::: l);
      } else {
        i+= 1;
        r ::: List(l);
      }
    )
  }

It starts off with a guard clause returning the same towers state if the attempted move was illegal. Then I, store off the value of the disk `towers(from).head` so that I can use that to construct a new list later. Then I use #foldLeft on the towers List to construct a new immutable List[List[Int]] that has the updated state with from towers top disk removed and to towers disk added.

I’m not sure this is the best solution, and I’m super new to functional programming, so please if you have any suggestions please hit me up!

Towers of Hanoi in Scala

Getting started: Ionic + ES6

For some context: When working on front end stuff, I’ve always written pure javascript with the exception of a few one off coffee script fixes. I’m super stoked about ES6 and have been converting many of my js heavy projects to use some of the ES6 syntax. Most recently I’ve been working on an app called Pushbit for competing with friends for weekly pushup count. It’s written using the ionic framework and I’ve built much of it using some cool new ES6 syntax and would love to share my build process.

Here’s how I got started (follow along on github: w1zeman1p/es6demo)

ionic start es6demo
cd es6demo

We’ll need to transpile our es6 code into es5 code for it to work on all current browsers including the phones. I’ve chosen to create a directory called `jssrc` at the root level of the app to store all my javascript code. The transpiled file will end up in www/js.

mkdir jssrc
mv www/js/* jssrc/

Luckily, ionic comes with a nice gulpfile that we can add to. I like gulp, and incase you enjoy a different flavor of build system you should checkout Addy Osmani’s list of https://github.com/addyosmani/es6-tools es6 tools. The idea here is that we want to some npm modules to read our es6 code and output a single concatenated es5 javascript file. We’ll use 3 new npm modules which you might need to install using these commands:

npm install gulp-traceur
npm install gulp-sourcemaps
npm install gulp-watch
npm install gulp-concat

Here’s a snippet to get you started:

//gulpfile.js
var gulp = require('gulp'),
  concat = require('gulp-concat'),
  sourcemaps = require('gulp-sourcemaps'),
  traceur = require('gulp-traceur'),
  watch = require('gulp-watch');

var paths = {
  scripts: ['./jssrc/**/*.js']
};

gulp.task('scripts', function () {
  return gulp.src(paths.scripts)
    .pipe(sourcemaps.init())
    .pipe(traceur())
    .pipe(concat('all.js'))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('./www/js'));
});

gulp.task('default', ['scripts']);

gulp.task('watch', function () {
  gulp.watch(paths.scripts, ['scripts']);
});

Lets go through this and talk about each piece.

First we need to require the modules we’ll use for transpiling. I’ve tried gulp-traceur and gulp-6to5 which are both gulp packages that overlay Traceur and 6to5 respectively.  Traceur is a project out of google and seems to have the most traction at the moment. It also was the tool that worked best for me. gulp-sourcemaps is used to build a sourcemap file that can be used by the browser during debugging to show you the original code, rather than the transpiled es5. gulp-watch is handy for constantly running your gulp task when your es5 files change.

sourcemaps = require('gulp-sourcemaps'),
traceur = require('gulp-traceur'),

I’ve added `scripts: [‘./jssrc/**/*.js’]` to the given `paths` variable.

The `scripts` task will be used to convert our scripts. First it reads the files in `paths.scripts`, then initializes sourcemaps, then runs the files through traceur, then concatenates them into a file called `all.js`, then drops that file into www/js.

gulp.task('scripts', function () {
  return gulp.src(paths.scripts)
    .pipe(sourcemaps.init())
    .pipe(traceur())
    .pipe(concat('all.js'))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('./www/js'));
});

Now that our gulpfile is all good to go we can run `gulp` and `gulp watch` which will start listening to changes in our javascript files.

gulp
gulp watch

Lets checkout the www/index.html file and make sure we’re including the new transpiles `all.js` file.

There is an autogenerated section that looks like this:

    <!-- your app's js -->
    <script src="js/app.js"></script>
    <script src="js/controllers.js"></script>
    <script src="js/services.js"></script>

Update that to reference `all.js`.

    <!-- your app's js -->
    <script src="js/all.js"></script>

We also need to add in a few special files referenced here: https://code.google.com/p/traceur-compiler/wiki/GettingStarted that will allow us to run traceur compiled js files in the browser.

Add these scripts to your index.html head:

  <script src="https://traceur-compiler.googlecode.com/git/bin/traceur.js"></script>
  <script src="https://traceur-compiler.googlecode.com/git/src/bootstrap.js"></script>

Lets fire up the server and see the result.

ionic serve

See the default app in the browser? Cool!

Lets get to writing some ES6 to test it out. Open up jssrc/app.js. First I like to go through and convert all anonymous function arguments to the () => {} syntax. something like this:

// ... OLD
angular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])
  .run(function($ionicPlatform) {
    $ionicPlatform.ready(function() {

// ... NEW!
angular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])
  .run(($ionicPlatform) => {
    $ionicPlatform.ready(() => {

Refresh the page and all should be well.

Lets try something a little more interesting. Add the following snippet at the bottom of `jssrc/app.js`:

class Snowman {
  constructor (name) {
    this.name = name;
  }

  sayHi() {
    console.log("Hi! I'm " + this.name + " and I like warm hugs.");
  }
}

var olaf = new Snowman('Olaf');
olaf.sayHi();

Open the dev console (cmd+opt+i), then refresh and observe the message from the instance of our es6 class.

That should get you started. In a following post I’ll show you how I built out my angular model layer with influences from my backbone.js experience.

Getting started: Ionic + ES6

Editor battle + Productivity hack!

Over my decade of development I’ve used many different text editors. In the very beginning, I didn’t know better and used notepad.exe. At my first paid programming gig in 2005 we used Sun Solaris machines and I was thrown into the deep end with a book on Perl and a book on Vi. I became proficient enough to get things done in VI, but was mostly lost. That same year I was introduced to Visual Studios. Later for school I used notepad++, Dev-C++ and, as infrequently as possible, nano.

Most of my development between 2007 and 2012 was done on windows based machines writing code for the web in Visual Studios. In 2012 I bought a Mac that would become my primary development machine. In the process of switching over to Mac I’ve experimented with a handful of editors an eventually settled on one.

In my experience Visual Studios is hands down the most powerful editor out there. It is incredibly feature rich. When I use VS I feel like I’m only ever actually using about .5% of what it can really do. I’ve used XCode for some iOS development and I would categorize XCode and Visual Studios together as full IDE’s. XCode is no where near as featureific as Visual Studios, but still has far more features than I’ll ever consider using.

I spent about 4 months working with Textmate 2 and found it to be straight forward and useful. I liked some of the integrations allowing me to run code directly from the editor. I also liked the powerful built in snippet library provided.

After Textmate I considered Sublime text for 1 month. It has very similar features to Textmate and the way I used them was identical except for subtle differences in snippets.

After using Sublime for a month I attempted to use Vim as my primary editor for a month. At first I hated it. I didn’t really understand the “modal” editing and was using the arrow keys in insert mode most of the time. It seemed like a more frustrating version of Sublime or Textmate.

About a month after starting to use Vim I received an offer to try the Atom beta. I used atom for about 2 weeks before deciding that it too was extremely similar to Textmate and Sublime.

There must be a reason that developers prefer Emacs and Vim, I thought. (I opened emacs once and spent 10 min figuring out how to properly close it, maybe one day I’ll give her another solid try). After some research I realized I was using Vim all wrong. I was implementing every anti-pattern in the book: staying in insert mode, using arrow keys to navigate, holding down backspace to delete a word or line. I learned a few rules of thumb that took my editing to the next level with Vim:

  1. Spend as little time as possible in `insert` mode.
  2. Don’t use arrow keys, prefer h, j, k, l.
  3. Don’t hold down any key for any reason, ever.
  4. Navigate with numbers.
  5. Compose motions and operators.

I’m constantly trying to get faster. Sharpening my tools. I recommend you try Vim and try to follow the above rules. The best tip for getting more efficient: Turn off key repeat!

By turning off key repeat at your operating system level, you’ll be forced to learn all the keyboard shortcuts for every other application you use. Turning off key repeat for a couple weeks drastically increased my overall productivity.

Editor battle + Productivity hack!

Suck at reading? Watch or listen instead

One of my biggest weaknesses is reading speed. I must read something a few times before extracting meaning. Recently, I’ve embraced this and know that if I want to learn something I should just find the same content in a different medium.

While in school, I compensated for my lack of reading comprehension by paying extremely close attention during lectures. I avoided any classes that required heavy reading and took as many STEM classes as possible. In college, I claimed to hate subjects like History, Humanities and English. Looking back I know it was mostly just fear of failing.

People often ask if I’ve read certain books and my answer is usually “I don’t know how to read.” I’ll explain that rather than reading a book I’ll spend some time finding a video screencast, podcast or audiobook that covers the same subject. Today, educational tech content is so available it’s awesome.

Wait, aren’t there way more books than podcasts and videos? Great question! There are certainly more written content than audio/video content about web development. That said, it’s unreasonable to assume that someone would read all the books. It’s also fair to say there is a lot of overlap in many of the books. I’ve been following my listen/watch focused learning strategy for about 4 years now and I’ve never run out of content (and I listen to podcasts at 1.5x).

Great resources for watching web development content:

Great resources for listening to web development content:

Suck at reading? Watch or listen instead

Goals

It’s New Years resolution time again! It’s healthy to constantly be setting and achieving goals. IMO goals are best set when you need something to strive towards, not necessarily Jan 1st. The new year does provide a nice explicit opportunity for everyone to review and refine their goals. Here are a few of mine:

2016

Simplify

  • January – No TV (including youtube/netflix/hulu/streaming/video)
  • February – No Facebook
  • March – No Alcohol
  • April – Only buy food/gas (not things, no clothes, toys, tools, games, furniture etc.)
  • May – Run every single day
  • June – Swim every single day
  • July -No Coffee
  • August – No Sugar
  • September – No eating out
  • October – No Meat
  • November – Wake up and get out of bed before 6AM every day
  • December – Give away something or donate money every day

Improve

  • Read / Listen To 30 books.
  • Publish 24 blog posts.

——————-

2015

Earn 1000 points on stackoverflow. (made it to 556, count this as a fail)

Type 100 WPM in 3 races on typeracer. (top speed in the 90s, not 100, but huge improvement, count this as a fail also)

âś“ Publish 12 blog posts.

Drink only water (yes no coffee, no beer, no milk, just water).

âś“ Give a talk at a meetup.

âś“ Launch Insider AI in the app store.

Additions (March 7):

âś“ Record a screencast.

Goals