Tuesday, March 4, 2014

Group By In Plain Ruby

I had to work with this code recently, and it just did not feel right:

class FindsEmployeesByLocations
  def self.from(employee_locations=[])
    locations = Hash.new
    employee_locations.each do |location|

      if !locations.key?(location.city)
        locations[location.city] = Array.new;
      end

      locations[location.city].push(location);
    end

    return locations
  end
end
A few things stood out:
  1. There is a locations hash
  2. The each iterator adds items to the hash based on a condition
  3. Returns the locations hash

Declaring a collection and adding items to it by iterating over the source collection is an infection that people with imperative languages have. With a little bit of functional thinking I was certain this code can be simplified.

Just to be on the safe side I added specs around this before I refactored it:

describe User do
  ...

  context "when the employee locations collection is empty" do
    specify "employees by locations is empty" do
      expect(FindsEmployeesByLocations.from([])).to be_empty
    end
  end

  context "when there are 3 employees with 2 locations" do
    subject { FindsEmployeesByLocations.from(employee_locations) }

    it "has only 2 locations" do
      expect(subject.keys.count).to eq(2)
    end

    specify "first locations has 2 users" do
      expect(subject.keys.first).to eq("Chicago")
      chicago_location = subject["Chicago"]

      expect(chicago_location.count).to eq(2)
      expect(chicago_location.map(&:user).map(&:name)).to eq(%w(John Kate))
    end

    specify "last locations has 1 user" do
      expect(subject.keys.last).to eq("New York")
      new_york_location = subject["New York"]

      expect(new_york_location.count).to eq(1)
      expect(new_york_location.first.user.name).to eq("James")
    end

  end
end
Everything passed, I was ready to play with the code.

First I thought I need to use the reduce method. I tried that, but it did not work. Then I started to think about what this piece of code is doing. I realized it's grouping entities based on keys. I was certain Ruby has a group_by method and I was right: there it was.

All that code can be replaced with one beautiful line:

class FindsEmployeesByLocations
  def self.from(employee_locations=[])
    employee_locations.group_by(&:city)
  end
end
The method, or even this class is unneeded for calling something as simple and elegant as Ruby's group_by method.

Be skeptical when you see an empty collection followed by an iterator. If you look closely, you will see that the code can be simpler by using one of Ruby's enumerable functions.

You can find the entire example in this gist.

Saturday, February 22, 2014

Learning Clojure One Failing Test at a Time

I learn a new language just like I discover a new algorithm in a language I already know: one failing test at a time. The advantage of discovering a language this way is deeper learning. If I read something in a book, try it in the REPL, I forget it by next week. However, writing a failing test first, describing what a function should do, I realized my learning is deeper.

I also like to keep around things I try as a reference. Once I type it in the REPL, unless I save my session somehow, the information goes away. I am unhappy when I make a mistake on the third line in the REPL, and I have to start the whole thing over. I also caught myself looking up things in tests I tried a few weeks ago.

I can try this in the REPL:

user => (conj [1 2 3] 4 5)
  [1 2 3 4 5]

Or I could fire up a new project using Leiningen with lein new learning, add a new test touch test/learning/vector_tests.clj and do something similar in a test:
(ns learning.vector-test
  (:require [clojure.test :refer :all]))

(deftest a-vector-test
  (testing "conjoining elements to a vector"
    (is (= [1 2 3 4 5] (conj [1 2 3] 4 5)))))
I run the test and everything works:
lein test learning.vector-test
Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

The next thing I try is removing the last element from a vector. The Clojure pop function does just that. Here is what I add to my test:

 ..
  (testing "removing the last element from a vector"
    (is (= [1 2 3] (pop [1 2 3 4])))))

You see where this is going. It's a bit more typing, but I'm building up my own reference guide as I learn new things. I also get a deeper knowledge faster by practicing this way.

Why Clojure’s own test framework does not have a colored output is beyond me. I am not ready to send a pull request for it just yet. But if you are as bothered as I am to look at a non-colored test reporter, you need to install difftest as a Leiningen plugin. You need to create or edit your ~/.lein/profiles.clj file as it's described here.

Once you successfully installed the plugin, you go from this:

To that:

The REPL is great for trying short things quickly. As soon as my example reaches the second line I question myself if I really should continue it here or just put the code in a test. I don't enjoy using the REPL as an editor, but as a tool where I can try things, it works!

Thursday, December 26, 2013

Test Driving the FizzBuzz Kata In Clojure

I've been enjoying my journey down the path of functional languages. I started with Clojure, than looked at Erlang, and now I am back at Clojure again. This post does not try to explain why you should look at functional languages, I assume you passed that since you're reading this blog post.

The books will teach you the basic concepts. Those ideas vanish fast unless I practice them with a project or some kind of challenge. Programming quizzes or coding puzzles are a good way to exercise a new language. I usually start with the simple ones, and I work my way towards the more complex projects. The FizzBuzz kata is on the easier side, I was even asked to code it on a white board not too long ago.

 

Here is how I solved the FizzBuzz kata in Clojure test driving it with its own test-is tool. I describe every changes I made, feel free to follow along by typing in the way I did it.

I'll assume you have Clojure and Leiningen installed on your computer. Getting these tools is simple thanks to Homebrew on OSX. Please do a Google search if you don't have them already installed and you're using an OS other than OSX. I am using Clojure 1.5.1 version at the time of this writing.

I generated a skeleton project with Leiningen:lein new app fizzbuzz. The directory structure had a src and a test directory. lein test runs the tests and lein run executes the program.

I put all my code in the src/fizzbuzz/core.clj and in the test/fizzbuzz/core_test.clj files. I write code in Vim, and I use a vertical split to look at the test and program files in the same window. I usually have the test on the left, and the code under test on the right hand side.

Here is the first failing test I wrote:

(ns fizzbuzz.core-test
  (:require [clojure.test :refer :all]
            [fizzbuzz.core :refer :all]))

(deftest fizz-buzz-printer-test
  (testing "with 1"
    (is (= "1\n" (fizz-buzz-printer 1)))))
I provided the skeleton for the fizz-buzz-printer function this way:
(ns fizzbuzz.core
  (:gen-class))

(defn fizz-buzz-printer [limit])

(defn -main
  "I don't do a whole lot ... yet."
  [& args]
  ;; work around dangerous default behaviour in Clojure
  (alter-var-root #'*read-eval* (constantly false))
  (println "Hello, World!"))
When I ran the test with lein test, this is the error I had:

FAIL in (fizz-buzz-printer-test) (core_test.clj:7)
with 1
expected: (= "1\n" (fizz-buzz-printer 1))
  actual:
 (not (= 1
 nil))

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
Tests failed.

The simplest thing that could possibly work is this:

(ns fizzbuzz.core
  (:gen-class))

(defn fizz-buzz-printer [limit]
  "1\n")

...
Please note that I omitted the -main function as it's not changed yet. I ran the tests, and it all passed:

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

Great! Now I am on to the next test:

...

(deftest fizz-buzz-printer-test
  (testing "with 1"
    (is (= "1\n" (fizz-buzz-printer 1))))
  (testing "with 2"
    (is (= "1\n2\n" (fizz-buzz-printer 2)))))
The following code change made the test pass:
...

(defn fizz-buzz-printer [limit]
  (if (= 2 limit)
    "1\n2\n"
    "1\n"))

...
A conditional won't serve me long to print out numbers. I refactored the function this way:
...

(defn fizz-buzz-printer
  ([output current limit]
    (let [product (str output current "\n")]
      (if (< current limit)
        (fizz-buzz-printer product (+ 1 current) limit)
        product)))
  ([limit]
    (fizz-buzz-printer "" 1 limit)))

...
This might need a little explanation. I chose recursion instead of using a loop. The fizz-buzz-printer function is overloaded: it accepts 1 (the limit) or 3 arguments (the output, current value and limit). The formatted string is captured in the product value. If the current value in the cycle is less than the limit I call the same function but the current value is incremented by one, and if not, I know I reached the limit and just return the formatted string - the product.

With this code I should be able to print the numbers on the screen followed by a line break. I wrote a quick - temporary - test to verify that (see the last assert):

...

(deftest fizz-buzz-printer-test
  (testing "with 1"
    (is (= "1\n" (fizz-buzz-printer 1))))
  (testing "with 2"
    (is (= "1\n2\n" (fizz-buzz-printer 2))))
  (testing "with 4"
    (is (= "1\n2\n3\n4\n" (fizz-buzz-printer 4)))))

I was ready for the fun part of this kata! For every number divisible by 3 I had to print out the word: "Fizz".
First I wrote the failing test:

...

(deftest fizz-buzz-printer-test
  (testing "with 1"
    (is (= "1\n" (fizz-buzz-printer 1))))
  (testing "with 2"
    (is (= "1\n2\n" (fizz-buzz-printer 2))))
  (testing "from 1-3"
    (is (= "1\n2\nFizz\n" (fizz-buzz-printer 3)))))
The error was obvious:

FAIL in (fizz-buzz-printer-test) (core_test.clj:11)
from 1-3
expected: (= "1\n2\nFizz\n" (fizz-buzz-printer 3))
actual: (not (= "1\n2\nFizz\n" "1\n2\n3\n"))

Ran 1 tests containing 3 assertions.
1 failures, 0 errors.
Tests failed.

I was expecting the word Fizz, but I was getting the number 3. That logic was missing. Here is how I added that:

...

(defn fizz-buzz-printer
  ([output current limit]
    (let [product (if (= 3 current)
                    (str output "Fizz\n")
                    (str output current "\n"))]
      (if (< current limit)
        (fizz-buzz-printer product (+ 1 current) limit)
        product)))
  ([limit]
    (fizz-buzz-printer "" 1 limit)))

...
I felt generating the formatted string - the product value - put way too much logic in the let function. I extracted out that logic into its own function called format-output:
...

(defn- format-output [output current]
  (if (= 3 current)
    (str output "Fizz\n")
    (str output current "\n")))

(defn fizz-buzz-printer
  ([output current limit]
    (let [product (format-output output current)]
      (if (< current limit)
        (fizz-buzz-printer product (+ 1 current) limit)
        product)))
  ([limit]
    (fizz-buzz-printer "" 1 limit)))

...
The function format-output is private, it's only visible within its own namespace thanks to declaring it with defn-.
The increased complexity in the let function triggered the "extract function" refactoring. I felt the format-output function should be responsible for deciding if it has to print the number or the words "Fizz", "Buzz" or "FizzBuzz".

The next test was simple, I wanted to make sure the logic worked from zero to four. I did not have to modify the code for it:

...

(deftest fizz-buzz-printer-test
  (testing "with 1"
    (is (= "1\n" (fizz-buzz-printer 1))))
  (testing "with 2"
    (is (= "1\n2\n" (fizz-buzz-printer 2))))
  (testing "from 1-3"
    (is (= "1\n2\nFizz\n" (fizz-buzz-printer 3))))
  (testing "from 1-4"
    (is (= "1\n2\nFizz\n4\n" (fizz-buzz-printer 4)))))
I reached a point with my tests where I had to make my code a bit easier to test. I did not want to write tests that compares more than 3 numbers from the fizz-buzz-printer. I felt 3 is a large enough set to verify the boundaries. I added a new test for this:
...

(deftest fizz-buzz-printer-test
  (testing "with 1"
    (is (= "1\n" (fizz-buzz-printer 1))))
  (testing "with 2"
    (is (= "1\n2\n" (fizz-buzz-printer 2))))
  (testing "from 1-3"
    (is (= "1\n2\nFizz\n" (fizz-buzz-printer 3))))
  (testing "from 1-4"
    (is (= "1\n2\nFizz\n4\n" (fizz-buzz-printer 4))))
  (testing "from 3-4"
    (is (= "Fizz\n4\n" (fizz-buzz-printer 3 4)))))
Since I did not have the function override for 2 arguments, the test failed:

ERROR in (fizz-buzz-printer-test) (AFn.java:437)
from 3-4
expected: (= "Fizz\n4\n" (fizz-buzz-printer 3 4))
  actual: clojure.lang.ArityException: Wrong number of args (2) passed to: core$fizz-buzz-printer

I added the function override which looked almost identical to the function with one argument:

...

(defn fizz-buzz-printer
  ([output current limit]
    (let [product (format-output output current)]
      (if (< current limit)
        (fizz-buzz-printer product (+ 1 current) limit)
        product)))
  ([start limit]
   (fizz-buzz-printer "" start limit)) ; added function with 2 arguments
  ([limit]
    (fizz-buzz-printer "" 1 limit)))

...
All the tests passed. However, this code had duplication: the unary function (method with one argument) initialized the output just like the binary function. I decided to remove this duplication by changing the unary function to call the binary function that calls the ternary one.
...

(defn fizz-buzz-printer
  ([output current limit]
    (let [product (format-output output current)]
      (if (< current limit)
        (fizz-buzz-printer product (+ 1 current) limit)
        product)))
  ([start limit]
   (fizz-buzz-printer "" start limit))
  ([limit]
    (fizz-buzz-printer 1 limit))) ; does not initializes the output

...
I was now ready to zoom into the ranges where the program had to produce the different words instead of numbers. The next test did just that:
...

(deftest fizz-buzz-printer-test
  (testing "with 1"
    (is (= "1\n" (fizz-buzz-printer 1))))
  (testing "with 2"
    (is (= "1\n2\n" (fizz-buzz-printer 2))))
  (testing "from 1-3"
    (is (= "1\n2\nFizz\n" (fizz-buzz-printer 3))))
  (testing "from 1-4"
    (is (= "1\n2\nFizz\n4\n" (fizz-buzz-printer 4))))
  (testing "from 3-4"
    (is (= "Fizz\n4\n" (fizz-buzz-printer 3 4))))
  (testing "from 4-5"
    (is (= "4\nBuzz\n" (fizz-buzz-printer 4 5)))))
I did the simplest thing that could possibly work: I used a nested conditional:
...

(defn- format-output [output current]
  (if (= 3 current)
    (str output "Fizz\n")
    (if (= 5 current) ; introduced this nested conditional
      (str output "Buzz\n")
      (str output current "\n"))))

...
Everything passed again, but this code was ugly. Here is how I used cond to refactor it:
...

(defn- format-output [output current]
  (cond
    (= 3 current) (str output "Fizz\n")
    (= 5 current) (str output "Buzz\n")
    :else (str output current "\n")))

...
My next test covered the range from 4-6, where the word "Fizz" should be printed out for the number 6.
...

(deftest fizz-buzz-printer-test
  (testing "with 1"
    (is (= "1\n" (fizz-buzz-printer 1))))
  (testing "with 2"
    (is (= "1\n2\n" (fizz-buzz-printer 2))))
  (testing "from 1-3"
    (is (= "1\n2\nFizz\n" (fizz-buzz-printer 3))))
  (testing "from 1-4"
    (is (= "1\n2\nFizz\n4\n" (fizz-buzz-printer 4))))
  (testing "from 3-4"
    (is (= "Fizz\n4\n" (fizz-buzz-printer 3 4))))
  (testing "from 4-5"
    (is (= "4\nBuzz\n" (fizz-buzz-printer 4 5))))
  (testing "from 4-6"
    (is (= "4\nBuzz\nFizz\n" (fizz-buzz-printer 4 6)))))
This test failed again as I was only checking for the number three and not for numbers divisible by three without a remainder. I changed the format-output function to use the modulus operator:
...

(defn- format-output [output current]
  (cond
    (= 0 (mod current 3)) (str output "Fizz\n")
    (= 5 current) (str output "Buzz\n")
    :else (str output current "\n")))

...
All the tests now passed again.

I suspected the same error at number 10, therefore my next test was around it (between 9 and 11):

...

(deftest fizz-buzz-printer-test
  (testing "with 1"
    (is (= "1\n" (fizz-buzz-printer 1))))
  (testing "with 2"
    (is (= "1\n2\n" (fizz-buzz-printer 2))))
  (testing "from 1-3"
    (is (= "1\n2\nFizz\n" (fizz-buzz-printer 3))))
  (testing "from 1-4"
    (is (= "1\n2\nFizz\n4\n" (fizz-buzz-printer 4))))
  (testing "from 3-4"
    (is (= "Fizz\n4\n" (fizz-buzz-printer 3 4))))
  (testing "from 4-5"
    (is (= "4\nBuzz\n" (fizz-buzz-printer 4 5))))
  (testing "from 4-6"
    (is (= "4\nBuzz\nFizz\n" (fizz-buzz-printer 4 6))))
  (testing "from 9-11"
    (is (= "Fizz\nBuzz\n11\n" (fizz-buzz-printer 9 11)))))
The test failed, I received 10 instead of "Buzz", I adjusted the second conditional to use the modulus function as well:
...

(defn- format-output [output current]
  (cond
    (= 0 (mod current 3)) (str output "Fizz\n")
    (= 0 (mod current 5)) (str output "Buzz\n")
    :else (str output current "\n")))

...
This was great! Now I had to write code for one more scenario: for the numbers divisible by 5 and 3 I had to print the word "FizzBuzz" instead of "Fizz" or "Buzz". My last test did just that:
...

(deftest fizz-buzz-printer-test
  (testing "with 1"
    (is (= "1\n" (fizz-buzz-printer 1))))
  (testing "with 2"
    (is (= "1\n2\n" (fizz-buzz-printer 2))))
  (testing "from 1-3"
    (is (= "1\n2\nFizz\n" (fizz-buzz-printer 3))))
  (testing "from 1-4"
    (is (= "1\n2\nFizz\n4\n" (fizz-buzz-printer 4))))
  (testing "from 3-4"
    (is (= "Fizz\n4\n" (fizz-buzz-printer 3 4))))
  (testing "from 4-5"
    (is (= "4\nBuzz\n" (fizz-buzz-printer 4 5))))
  (testing "from 4-6"
    (is (= "4\nBuzz\nFizz\n" (fizz-buzz-printer 4 6))))
  (testing "from 9-11"
    (is (= "Fizz\nBuzz\n11\n" (fizz-buzz-printer 9 11))))
  (testing "from 14-16"
    (is (= "14\nFizzBuzz\n16\n" (fizz-buzz-printer 14 16)))))
The error was what I expected: the program only printed out the word "Fizz" instead of "FizzBuzz". Adding the modulus check for 15 made this last test to pass:
...

(defn- format-output [output current]
  (cond
    (= 0 (mod current 15)) (str output "FizzBuzz\n")
    (= 0 (mod current 3)) (str output "Fizz\n")
    (= 0 (mod current 5)) (str output "Buzz\n")
    :else (str output current "\n")))

...
All my tests passed!

Ran 1 tests containing 9 assertions.
0 failures, 0 errors.

I also wanted to see the program in action by running it with leain run. Here is how I changed the main function:

...

(defn -main
  [& args]
  ;; work around dangerous default behaviour in Clojure
  (alter-var-root #'*read-eval* (constantly false))
  (print (fizz-buzz-printer 100)))

And that's it! Please take a look at the final solution in this Gist.
Hope you had as much fun reading this as I had preparing it.

Happy New Year everyone!

Wednesday, November 20, 2013

Reporting in Rails - with Rails Admin and ...

Understanding the data is very important for a business. "How many active customers do I have right now?" "How many did we have last week?" "What are they buying?" "What they are not buying?" "Why?" I could go on and on with these questions that businesses would like to get answered.

I was shadowing one of our customer service folks back in March when someone from sales stopped by and started talking about how great it would be if they could easily export all the candidates for a job and where they applied from. I knew what they were asking for is in the database, I just had to find a way to extract that information.

Our nightly job takes a snapshot of the production database and restores it in our QA environment. This data is good enough for our data mining purposes. The data analyzing tool I was looking for did not have to be very efficient as we can easily restart the QA servers if a long running query would bring it down.

The tool phpPgAdmin works great, but I wanted to find something more lightweight with Rails and nothing turned up at that time. A few years ago I took a look at the Rails Admin project, I figured that might work for us. I generated a skeleton Rails app, migrated all the DataMapper models to ActiveRecord ones and introduced it to the team shortly after.

They loved it!

The product team was able to see the database tables with the data stored in them without an abstracted reporting layer on top of it. Exporting data as a CSV file was also simple, users used Excel to analyze it further.

The story could end here, but it does not. As the users of Rails Admin wanted to use the tool for intricate queries, we bumped into roadblocks: when the association is more complex between tables, Rails Admin won't work out of the box. I remembered the days when I used phpMyAdmin and how that tool provided a SQL editor tool where I was able to do anything with the data through DML.

We needed something similar and I came up with this tool:

The users have to put the SQL script in the query textarea, click on the "Run it" button and the result in CSV is pushed back to the client. Since most of the data analysis happens in Excel anyway, I did not bother building a paginated view for the result. I added basic error checking that reports Postgres errors back to the client. I don't restrict this tool for read-only database access as only our product team is using it. If they figure out how to delete the users table through this SQL tool, we can restore the production snapshot in a few minutes.

This is what the logic looks like for the SQL controller:

require 'csv'

class SqlController < ApplicationController
  before_filter :authenticate_user!

  def editor
    @query = nil
  end

  def run
    @query = params[:query]

    begin
      results = ActiveRecord::Base.connection.execute(@query)
    rescue ActiveRecord::StatementInvalid => ex
      flash.now[:error] = ex.to_s
      render 'editor' and return
    end

    if results.count == 0
      flash.now[:notice] = "No records were found."
      render 'editor' and return
    end

    send_data prepare_csv(results),
              type: 'text/csv; charset=iso-8859-1; header=present',
              disposition: 'attachment;filename=exported_data.csv'
  end

  private

  def prepare_csv(result, options = {})
    CSV.generate(options) do |csv|
      keys = result.first.keys
      csv << keys
      result.each do |row|
        csv << row.values
      end
    end
  end

end

We came up with a couple of SQL queries and stored them in a Google Doc. The process is as simple as coping and pasting the query from that Google Doc, changing the where clause for their needs and running the script. I highlighted the data that can be changed in the queries (like date ranges or record IDs) and showed them how it can be adjusted before the query runs.

The product team just can not be happier! We got to the point where this SQL query editor is going to be the primary data retrieval tool. Rails Admin can do the same with simple and straightforward queries but selecting the tables and fields through check boxes is more time consuming than copying a SQL script into the textarea and clicking the button.

Would we ever give this tool to our customers? Of course not! It will always remain a well secured internal tool. But this will be an easy way to figure out what kind of additional reports most of our users want and we will build those into our product as we go. Until then, they can just contact us, and we'll send them the data they need in a few minutes. Win!

Tuesday, September 10, 2013

The Broken Light Switch

Ever since I read the book "The Design of Everyday Things" I notice awkward objects that have not bothered me before.
What is wrong with this light switch?

I know the wall plate is broken. I'll replace it. It costs about 50 cents and I'll take care of it albeit I don't own this property

OK, I tell you. The problem with this light switch is that the toggle on the left side, closer to the door, should turn on the main light in our master bathroom. It doesn't. That one turns on the light in the shower cabinet and the other on the right side turns on the main light which is totally the opposite the way I'd expect it to work. Whenever I enter the bathroom I have to think about which toggle to use: the one on the left or the other on the right hand side?

I asked my 5 year old son this afternoon to grab something from the bathroom. I turned back from my desk and watched what he was going to do. He walked into the bathroom, switched on the toggle closer to the door which turned on the shower light. He noticed that's not what he wanted, quickly turned it off and switched on the other one on the right hand side. This is the point where I paused for a few seconds. A 5 year old's mind is basically working with instincts, he hasn't had much chance to reprogram his brain. And yet he still used the toggle closer to the door first thinking that is the one for the main light.

I wish I could use his help to do user acceptance or API testing. If something is not obvious for a 5 year old then it should be further simplified. Like how Steve Jobs wanted to ask a 4th grader to write a manual for the Macintosh back in 1983.

Monday, August 26, 2013

Me and My (2013 or v2) Nexus 7

The TL;DR Version

Pros of the Nexus 7 (2013):

  • High resolution - 324 ppi which is great for reading books
  • Shape - it's narrower than the iPad mini, I can easily hold it with one hand
  • Price - less expensive than other tablets on the market
  • No additional software - comes with pure OS installed, no need to uninstall crapware
Cons:
  • Plastic back - I just like Apple's more expensive casing
  • Rough headphones jack - I had trouble using it the first time
  • Unpolished OS - starting from the square buttons to "no command" errors during the OS update

The Nexus 7

I had about 30 minutes drive to my former employer last year. I used this time listening to audio books as I talked about it in my talk at Railsconf 2013. After moving to Chicago my commute changed from driving to riding the train. This was a great opportunity to make progress with my ever growing list of books to read.

I have a subscription to Safari Books Online. It's rather pricey, but you get access to a large number of software engineering titles including unreleased "rough cuts". It also has an offline bookbag feature as well, where I can save 3 books on my device and read it without Internet connection.

I seriously considered buying an iPad mini as that size was more manageable to me than the full size - 10" - iPad. The only reason I did not buy it before was the lack of retina on the iPad mini. And then Google's Nexus 7 (version 2 or 2013) came out one Wednesday. While the iPad mini offered only 162 pixels per inch (ppi), the Nexus 7 offered twice as much: 324 ppi. As I needed this device for reading the higher resolution was the selling point for me. I ordered the device through Amazon and after a few days the USPS guy handed it over to me when I bumped into him outside the house.

Apple cares a lot about the packaging and your first experience with the product, Asus does not. The device has a plastic back cover and its charger is an unattractive black plastic cube. I turned it on, set up the Wifi password and within a few minutes I was on my way. The first app I installed was - of course - Safari Books Online app which worked neatly.

My first initial impression was that the screen was beautiful with the retina display but the device felt cheap and the Android 4.3 isn't as polished as iOS. The lack of rounded corners on the screen keyboard buttons was a surprise to me.
I tried to plug in my headphones into the jack and it did not go in all the way. A pressed a bit, gently, still no luck. I googled this "feature" and bumped into a video on YouTube where a guy had the exact same problem and he recommended using more force. I did that and it worked. I had a few Apple products but I never had this kind of experience with their product. They all just worked.

A few days later Android asked for an OS update. I went with it and when the device rebooted I received a "no command", meaningless error message. Sure the Nexus 7 costs less than the iPad mini but I was a bit worried what else I will find later.

After all the initial quirks, this device is great for reading books and I use it every time I travel to and from the city.

Monday, July 8, 2013

Trust and Discipline are the Core Ingredients of Remote Work

I've just wrapped up seven weeks of remote work from Europe where I worked from several cities from Hungary, Austria and Germany.
Just think about for a second how fortunate we - software engineers - are compared to dentists, assembly line workers or plumbers: all we need is a computer with decent Internet connection.

Unfortunately, airfare is expensive to Europe for an entire family. Whenever we go we try to stay as long as we can to justify the cost with a longer stay. I've had trips with three-four weeks, but this seven week stretch was the longest.

I am pretty sure my team was a bit nervous about my trip before I left, but they trusted me. They knew that I'll get the job done even when I am not on the same continent and they also knew that I'll try to make this as seamless as possible. I did. I started working late in the afternoon and wrapped up work at 11 pm or midnight which overlapped with the work hours of my team in Chicago.

Here is what worked:

  • Phone calls through Gmail - free calls launched from my browser
  • Skype - we used these rarely but worked fairly well
  • Join.me was a good tool to quickly share our screens, however the delay was annoying sometimes
  • Tmux was excellent for sharing the console when I worked with our system admin on server issues
  • Chat

What did not work so well:

  • Conference calls - sometimes the quality of sound was bad. Really bad. There was one time when I had to listen only with muted mic and "talk" through chat
  • Calling cell phones from Gmail had audio quality problems

I am not saying it was easy to log in and focus on work after a great morning and lunch with my family on the Old Continent. But just like running, self discipline helped. And when I noticed my attention had waned, I used pomodoro sessions to keep me focused for 25 minutes at a time.