Showing posts with label programming. Show all posts
Showing posts with label programming. Show all posts

Saturday, January 23, 2010

Prototype analog to jQuery's $(document).ready

I have a lot of experience with jQuery, but less with Prototype. Recently, I needed to add some event handlers to some elements in a Ruby on Rails app I'm building. I searched for how to do the equivalent of jQuery's $(document).ready() function in Prototype so that I could add the handlers after the document loaded, but most of the guides I found were out of date (I'm running Prototype 1.6.0.3, and I don't know which version these guides were for, but they all made my Javascript console angry).

Eventually, I was able to piece it together after digging through the depths of the Prototype API documentation. It's actually very simple:


document.observe('dom:loaded', function(){
// do yo thang...
});


Wrap whatever you're doing with that, and it won't be run until the document is loaded.

Wednesday, January 13, 2010

Installing Ruby on Rails 2.3+ plugins from github

I've been banging my head against this wall for quite awhile now, and I just finally figured out the answer. Like I've done in other posts, I'll just post what worked for me, and hopefully it'll help other people.

I'm running Ruby 1.9 and Ruby on Rails 2.3.3 on Snow Leopard. I've been trying to install plugins (specifically, Authlogic and Carmen) for a couple days now using the following two commands (as taken from the main github pages):

script/plugin install git://github.com/binarylogic/authlogic.git
script/plugin install git://github.com/jim/carmen.git


In return, I received the following errors:
Plugin not found: ["git://github.com/binarylogic/authlogic.git"]
Plugin not found: ["git://github.com/jim/carmen.git"]


After a lot of poking around, it turns out you need to make two changes in order for this to work on Rails 2.3 or higher: change the git:// at the beginning of each URL to http://, and add a trailing slash to the end of each URL. So instead, run these:

script/plugin install http://github.com/binarylogic/authlogic.git/
script/plugin install http://github.com/jim/carmen.git/


They both worked perfectly for me, so hopefully they'll work for you. If not, leave a comment and I'll try to help.

Sunday, December 13, 2009

The Knuth-Morris-Pratt Algorithm in my own words

Over the past few days, I've been reading various explanations of the Knuth-Morris-Pratt string searching algorithms. For some reason, none of the explanations were doing it for me. I kept banging my head against a brick wall once I started reading "the prefix of the suffix of the prefix of the...".

Finally, after reading the same paragraph of CLRS over and over for about 30 minutes, I decided to sit down, do a bunch of examples, and diagram them out. I now understand the algorithm, and can explain it. For those who think like me, here it is in my own words. As a side note, I'm not going to explain why it's more efficient than naïve string matching; that's explained perfectly well in a multitude of places. I'm going to explain exactly how it works, as my brain understands it.

The Partial Match Table



The key to KMP, of course, is the partial match table. The main obstacle between me and understanding KMP was the fact that I didn't quite fully grasp what the values in the partial match table really meant. I will now try to explain them in the simplest words possible.

Here's the partial match table for the pattern "abababca":


char: | a | b | a | b | a | b | c | a |
index:| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
value:| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |


If I have an eight-character pattern (let's say "abababca" for the duration of this example), my partial match table will have eight cells. If I'm looking at the eighth and last cell in the table, I'm interested in the entire pattern ("abababca"). If I'm looking at the seventh cell in the table, I'm only interested in the first seven characters in the pattern ("abababc"); the eighth one ("a") is irrelevant, and can go fall off a building or something. If I'm looking at the sixth cell of the in the table... you get the idea. Notice that I haven't talked about what each cell means yet, but just what it's referring to.

Now, in order to talk about the meaning, we need to know about proper prefixes and proper suffixes.

Proper prefix: All the characters in a string, with one or more cut off the end. "S", "Sn", "Sna", and "Snap" are all the proper prefixes of "Snape".

Proper suffix: All the characters in a string, with one or more cut off the beginning. "agrid", "grid", "rid", "id", and "d" are all proper suffixes of "Hagrid".

With this in mind, I can now give the one-sentence meaning of the values in the partial match table:

The length of the longest proper prefix in the (sub)pattern that matches a proper suffix in the same (sub)pattern.

Let's examine what I mean by that. Say we're looking in the third cell. As you'll remember from above, this means we're only interested in the first three characters ("aba"). In "aba", there are two proper prefixes ("a" and "ab") and two proper suffixes ("a" and "ba"). The proper prefix "ab" does not match either of the two proper suffixes. However, the proper prefix "a" matches the proper suffix "a". Thus, the length of the longest proper prefix that matches a proper suffix, in this case, is 1.

Let's try it for cell four. Here, we're interested in the first four characters ("abab"). We have three proper prefixes ("a", "ab", and "aba") and three proper suffixes ("b", "ab", and "bab"). This time, "ab" is in both, and is two characters long, so cell four gets value 2.

Just because it's an interesting example, let's also try it for cell five, which concerns "ababa". We have four proper prefixes ("a", "ab", "aba", and "abab") and four proper suffixes ("a", "ba", "aba", and "baba"). Now, we have two matches: "a" and "aba" are both proper prefixes and proper suffixes. Since "aba" is longer than "a", it wins, and cell five gets value 3.

Let's skip ahead to cell seven (the second-to-last cell), which is concerned with the pattern "abababc". Even without enumerating all the proper prefixes and suffixes, it should be obvious that there aren't going to be any matches; all the suffixes will end with the letter "c", and none of the prefixes will. Since there are no matches, cell seven gets 0.

Finally, let's look at cell eight, which is concerned with the entire pattern ("abababca"). Since they both start and end with "a", we know the value will be at least 1. However, that's where it ends; at lengths two and up, all the suffixes contain a c, while only the last prefix ("abababc") does. This seven-character prefix does not match the seven-character suffix ("bababca"), so cell eight gets 1.

How to use the Partial Match Table



We can use the values in the partial match table to skip ahead (rather than redoing unnecessary old comparisons) when we find partial matches. The formula works like this:

If a partial match of length partial_match_length is found and table[partial_match_length] > 1, we may skip ahead partial_match_length - table[partial_match_length - 1] characters.

Let's say we're matching the pattern "abababca" against the text "bacbababaabcbab". Here's our partial match table again for easy reference:


char: | a | b | a | b | a | b | c | a |
index:| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
value:| 0 | 0 | 1 | 2 | 3 | 4 | 0 | 1 |


The first time we get a partial match is here:


bacbababaabcbab
-|
-abababca


This is a partial_match_length of 1. The value at table[partial_match_length - 1] (or table[0]) is 0, so we don't get to skip ahead any. The next partial match we get is here:


bacbababaabcbab
----|||||
----abababca


This is a partial_match_length of 5. The value at table[partial_match_length - 1] (or table[4]) is 3. That means we get to skip ahead partial_match_length - table[partial_match_length - 1] (or 5 - table[4] or 5 - 3 or 2) characters:


// x denotes a skip

bacbababaabcbab
----xx|||
------abababca


This is a partial_match_length of 3. The value at table[partial_match_length - 1] (or table[2]) is 1. That means we get to skip ahead partial_match_length - table[partial_match_length - 1] (or 3 - table[2] or 3 - 1 or 2) characters:


// x denotes a skip

bacbababaabcbab
------xx|
--------abababca


At this point, our pattern is longer than the remaining characters in the text, so we know there's no match.

Conclusion



So there you have it. Like I promised before, it's no exhaustive explanation or formal proof of KMP; it's a walk through my brain, with the parts I found confusing spelled out in extreme detail. If you have any questions or notice something I messed up, please leave a comment; maybe we'll all learn something.

Tuesday, November 17, 2009

A method with no unit tests is a broken method

If you write software, you need to write unit tests. If you've written a method/function, and you haven't written a unit test for it, it's safe to assume that it's broken (even if it compiles and your other tests pass).

I'm not necessarily advocating full-fledged test-driven development. I'm just saying, if you release code into "the wild," and there are methods you haven't unit tested, your customers will almost certainly run into multiple bugs in each one of them.

That's an atomic point. Separate from that, I'd like to mention that this isn't always a bad thing. For a startup that wants to iterate as quickly as possible (and is writing non-life-critical software), writing the code with no unit tests, releasing it, and reproducing each customer-discovered bug with a unit test before fixing it is a totally reasonable model. These startups just shouldn't operate under the illusion that their software "works". In the hours after they make one of these releases, they should feel blessed if a single customer is able to register or log in.

Monday, November 16, 2009

Using C, convert a dynamically-allocated int array to a comma-separated string as cleanly as possible

EDIT: There are no "dynamic arrays", so to speak, in C. What I meant was "dynamically-allocated". I've updated the wording to reflect this.

EDIT 2: Someone on Reddit pointed out that my Python example doesn't actually work, since I have an array of ints rather than strings. I've updated the code example so it works.

I'm much less experienced in C than I am in higher-level languages. At Cisco, we use C, and I sometimes run into something that would be easy to do in Java or Python, but very difficult to do in C. Now is one of those times.

I have a dynamically-allocated array of unsigned integers which I need to convert to a comma-separated string for logging. While the integers are not likely to be very large, they could conceptually be anywhere from 0 to 4,294,967,295 In Python, that's one short line.

[code lang="python"]my_str = ','.join([str(num) for num in my_list])[/code]

How elegantly can people do this in C? I came up with a way, but it's gross. If anyone knows a nice way to do it, please enlighten me.

Tuesday, November 10, 2009

Any teachers or professors using classroom management software?

Every few months, I get an idea for a web app. My most recent one stems from hearing constant complaints from my UMass professors about the UMass classroom management software. It does a whole boatload of stuff, but apparently it's extremely hard to use. Keep in mind, I'm hearing that criticism from Computer Science professors. I'd wager a bet that it's much worse for professors whose subjects are unrelated to computers.

With that in mind, I've been working for the last month or so on my own classroom management web app. I doubt I'll find many teachers or professors with this post, but if I do, could you please leave me a comment? Specifically, I'd like to know if you use classroom management software. If you do, what do you use, and do you have any complaints about it? If you don't, what areas of your job would you like managed by software? Grading and student notifications ("You have a test in 2 days!") are the obvious ones to me, but are there any good ones I'm not thinking of?

Wednesday, November 4, 2009

Little Joys of Rails (Part 1) - Renaming Associations

I've spent the last month or so teaching myself the basics of Ruby on Rails. By far my favorite thing about it is that, at every turn, I find a little feature or helper that works really well and does exactly what I want without feeling messy or "magical".

Unfortunately, I have no one to share these little joys with, as pretty much none of my programmer friends know Ruby or Ruby on Rails. So, I figured I'd start sharing them on here. Hopefully, Rails newbies (like myself) will learn something, and Rails veterans will be able to chuckle knowingly as they fondly look back on the glory days of learning Rails.

Renaming Associations



If you have one model that belongs_to another, the association (in both directions) is named after the models. For example, if I have Users and Courses, and I want a course to belong_to its teacher (who is a User), I would start with the following:

[code lang="rails"]
class User < ActiveRecord::Base
has_many :courses
end

class Course < ActiveRecord::Base
belongs_to :user
end
[/code]

This is nice and simple, but if I want to access a course's teacher, I have to do it like this:

[code lang="rails"]
my_course = Course.first
puts my_course.user # Print out the course's teacher
[/code]

That's a little confusing. What if my course also has_many students (also Users)?

[code lang="rails"]
my_course = Course.first
puts my_course.users.first # Print out the course's first student
[/code]

So, my_course.user is the teacher, and my_course.users are the students. There's nothing to indicate that, I just have to know it. Wouldn't it be nicer if the associations could be named for what they represent?

[code lang="rails"]
class Course < ActiveRecord::Base
belongs_to :teacher, :class_name => 'User'
end
[/code]

To make this work properly, the foreign key needs to be teacher_id instead of user_id (that can be overridden too, but I won't go into that right now). Then, you get to do this:

[code lang="rails"]
my_course = Course.first
puts my_course.teacher # Print out the course's teacher
[/code]

Much better!

Now, I recognize that this is far from a revolutionary feature. I'm pretty sure it's possible in Django and most other MVC frameworks. I was just very happy to bump into it.

Saturday, September 12, 2009

Installing MySQL on Snow Leopard

Early on at Cisco, I realized that it's really beneficial to write down (step by step) what I've done when I install something new. It forces me to think about what I'm doing, it provides me with a guide in case I mess up and have to start over, and other people can benefit from it later.

With that in mind, I've decided to document my installation of MySQL on Snow Leopard (OS X 10.6). Hopefully, someone will get some use out of it, but if not, at least I'll have documentation of what I did.

The PATH Variable



MySQL has a bunch of useful executables that aren't in PATH by default. I could symlink to them, but I think that's less maintainable than just appending to PATH.


  1. Open the Terminal

  2. Open up the .bash_profile file in your home directory. This is a bash script that runs every time you start the Terminal, so the PATH variable will be extended properly every time. If you have TextMate and its UNIX command line tools installed, do it like this:
    [code lang="bash"]mate ~/.bash_profile[/code]
    Otherwise, do it like this:
    [code lang="bash"]/Applications/TextEdit.app/Contents/MacOS/TextEdit ~/.bash_profile &[/code]
    If you use the second one, make sure to add the ampersand at the end.

  3. We want to add MySQL's bin folder to PATH. MySQL's going to be installed at /usr/local/mysql, so let's add /usr/local/mysql/bin to PATH. Add the following line to .bash_profile:
    [code lang="bash"]export PATH="/usr/local/mysql/bin:/usr/local/sbin:$PATH"[/code]
    This replaces PATH with two new locations and the old value of path (so essentially, appending the two new locations to the beginning of PATH). You'll notice that, in addition to /usr/local/mysql/bin (which I mentioned earlier), I also added /usr/local/sbin. OS X doesn't include this in PATH by default, but I think it should, so I added it. I have no defense for that position, but this is as much a guide for myself as it is a guide for other people, so I don't need to defend it :)

  4. Save the updated .bash_profile and close it.

  5. Quit and reopen Terminal so that .bash_profile will be rerun (you could also run it explicitly, but I prefer quitting and reopening).

  6. Run the following command:
    [code lang="bash"]echo $PATH[/code]
    You should see /usr/local/mysql/bin and /usr/local/sbin in there now.


Downloading + Installing


I might try compiling and installing from source at some point, but I don't see any reason to when there are official OS X binaries available. Worst case scenario, it messes with a bunch of my settings, and I'll uninstall and then do it from source.

  1. Go to http://dev.mysql.com/downloads/mysql/5.1.html#downloads

  2. Scroll down to the Mac OS X section (near the bottom). Download the version labeled "Mac OS X 10.5 (x86_64)". It's very important that you download the 64-bit version. The 32-bit one will give you preference pane problems (I call them PPPs whenever I talk about them, though this is the first time I ever have). Eventually, there'll probably be a Mac OS X 10.6 version, but for now, this works perfectly.

  3. Open the .dmg, then run mysql-x.x.xx-osx10.5-x86_64.pkg. Continue through all the screens without changing anything, until it finishes.

  4. Optional: install the preference pane by running MySQL.prefPane.

  5. Optional: make MySQL start up along with OS X by running MySQLStartupItem.pkg. I didn't do this (I often use my computer for things other than development, and I don't want to bog it down unnecessarily), so I can't provide any instruction or vouch for how well it works on Snow Leopard.


  6. You should now be able to start MySQL by going into the MySQL preference pane and clicking "Start MySQL Server". If it doesn't work, leave me a comment, and I'll try to help you.

Monday, May 18, 2009

Django Middleware vs. Context Processors

This may be old news to many people, but it's something I just recently learned (after doing it wrong for about nine months), so I figured someone else may be able to benefit from my mistakes.

For a long time, when I needed to access the currently logged-in user in one of my Django views, they would follow this pattern:

[code lang="python"]from django.template import RequestContext

# some other view code

def my_view (request, obj_id):
context = RequestContext(request)
obj = get_object_or_404(SomeModel, pk=int(obj_id))

# do some stuff, including:
some_function(context["user"])

return render_to_response("some_url_name", {"some_var": some_val},
context_instance=context)[/code]

Now, seasoned Django vets (why are you reading this post, by the way?) are probably laughing, but this seemed perfectly fine to me. Luckily, my ignorance was revealed to me while asking on IRC about a problem which, while didn't seem so at the time, was completely tied to my misuse of RequestContext.

To put it simply, context processors are made to be used in templates. The only time they should ever be instantiated is at the very end of a view, like so:

[code lang="python"]return render_to_response("some_url_name", {"some_var": some_val},
context_instance=RequestContext(request))[/code]

I was using them all over views, and even in a few of my decorators. What's wrong with this? The main thing is, certain mutation functions are sometimes performed when a RequestContext is instantiated (such as user.get_and_delete_messages(), which gets all of a user's messages from the database and then deletes them), and performing them multiple times before loading the template can cause unexpected results. In my example, I was instantiating a RequestContext in a bunch of decorators, which meant that by the time my template was loaded, all of the user's messages were deleted (and stored in an earlier instance of RequestContext), making it look to me as if my auth messages were being thrown out.

What's the solution? Middleware. Middleware allows the programmer to attach variables to the request object before it reaches the view. With this (and the provided django.contrib.auth.middleware.AuthenticationMiddleware), I can still achieve my original goal (accessing the logged-in user from a view), but now I can do it without creating a RequestContext and potentially running mutation functions multiple times:

[code lang="python"]from django.template import RequestContext

# some other view code

def my_view (request, obj_id):
obj = get_object_or_404(SomeModel, pk=int(obj_id))

# do some stuff, including:
some_function(request.user)

return render_to_response("some_url_name", {"some_var": some_val},
context_instance=RequestContext(request))[/code]

I've removed all the references to RequestContext from my decorators, and made sure to only instantiate it at the very end of views. Now, messages work perfectly. I've even written my own middleware (which you can learn how to do here) to load instances of a few of my own models into the request.

To reiterate, I'm aware that this is common knowledge for many people, but it took me 9 months of moderate Django use and embarrassment on IRC to discover it for myself. Hopefully, this will help someone else in a similar position.

Sunday, May 10, 2009

Are you a better programmer than you were two years ago?

Two years ago, I was working on a fairly complicated (for my level of experience) web app for posting news articles. Quite a few times, I ran into situations where I was about to create tight coupling between two somewhat unrelated parts of my app. Sometimes, I wouldn't even recognize this as a problem. Other times, I would, but not be able to think of an easy fix, so I'd continue on.

Today, I find myself fixing tightly-coupled situations almost instinctively. I use design patterns that I was barely aware of two years ago, and I do it without straying into "design pattern fever" territory. This isn't to say I'm an expert at software design; it still takes a lot of thought to actually think of the best fix, and I'm sure I still make plenty of mistakes. My point is, my growth as a programmer is very obvious to me in these situations.

How about you? If you've been programming for more than two years, you're probably a better programmer than you were two years ago, but can you tell? If so, how can you tell? Can you give any good examples of moments when you realized it?

Tuesday, May 5, 2009

Non-painful email on Django development servers

I've been actively learning and using Django since August 2008, and I've loved almost every bit of it. There are plenty of places to read all about the virtues of Django, so I'll leave that out for now.

One thing that's always bugged me about web development in general is the sending of emails. I do development on my local computer (with a badly set up Apache / MySQL / PHP / Python / whatever else stack), and I've never felt like dealing with the headache of setting up a mail server. This means, when I add something that's supposed to send an email (like an activation email after registration), I have to get very hacky to test and debug it (making sure the email text is being produced correctly, making sure it's being sent to and from the right people, etc.).

This was one of the few web development pains that I thought Django didn't solve. Whenever I'd test a bit of code that was supposed to send email, I'd get a "Connection refused" error page (meaning my computer has no mail server to send the email with). I would usually add in a bit of printf debugging to make sure the subject and body had the correct text, but beyond that, I'd usually wait to test the email portions until I uploaded to a server that could send email (usually the production server, unfortunately).

Yesterday, I bumped into a little section in the Django documentation that explains how to get around this. As usual, Python has all the solutions. First, set this code in your settings.py file:

[code lang="python"]EMAIL_HOST = 'localhost'
EMAIL_PORT = 1025 # replace this with some free port number on your machine[/code]

Then, assuming you're on a Unix system (I'm on a Mac), run the following on the command line to start a "dumb" Python mailserver:

[code lang="bash"]python -m smtpd -n -c DebuggingServer localhost:1025[/code]

Make sure to replace 1025 with whatever you filled in for EMAIL_PORT.

Now, try running the email-sending code in your Python application. Voila! No error pages (or at least, none related to email), and the full text of the email (headers and all) appears in whatever command line prompt you ran the dumb mailserver on. This allows you to the see senders, recipients, subject, and body of the email being sent out, all without getting hacky or sending to an email account you own.

Taking this a step further, I created a small bash script called "dumbmail" in /usr/local/bin that looks like the following:

[code lang="bash"]#!/usr/bin/env bash
if [ -z $1 ]
then port=1025
else port=$1
fi

echo "Starting dumb mail server on localhost:$port"
python -m smtpd -n -c DebuggingServer localhost:$port[/code]

Now, when I'm testing a Django application and I get to a section that is going to send an email, I just run "dumbmail" (or "dumbmail some_number" if I need to use a different port, for some reason I can't imagine), and I'm ready to go.

Hope this helps people. The documentation was always there - I just never noticed that part until yesterday.

Monday, March 16, 2009

Benchmarking Python decimal vs. float

I'm writing a web app that includes, among other things, a good amount of (rational) non-integer numbers. Whenever I'm in this situation, and I'm using a language that supports Decimals (as opposed to just floats and doubles), I always wonder which one I should use.

If you're a programmer, you understand the difference and the dilemma. Floats/doubles are very fast, as all computers (built within the last 15 years) have hardware specifically made to deal with them. However, they're not perfectly accurate; because of binary representation, numbers that we use a lot (like 1/10 or 0.1) cause the same problems that 1/3 (0.33333...) cause in base 10.

Decimals, on the other hand, are slow. They are handled entirely in software, and thus take hundreds of instructions to do things that would take less than 10 with floats/doubles. The upside is that they're perfectly accurate; 0.1 is 0.1 is 0.1.

So the question becomes twofold:

  1. Do I really need my numbers to be perfectly accurate?

  2. How much slower are decimals than floats/doubles?



In my case, the accuracy would be nice, but not completely necessary. And thus, the latter question becomes important. I'm not writing a large application, and I don't expect it to get too popular too quickly, so if the slowdown is only moderate, I'll take the accuracy.

To learn what the slowdown was, I wrote two quick Python test programs:


# Decimal test
 
from decimal import Decimal
 
a = 0
for i in range(0, 20000):
    a = Decimal('%d.%d' % (i, i))
    print(a)



# Float test
 
from decimal import Decimal # kept this in on the float version
                            # so they'd have the same overhead
 
a = 0
for i in range(0, 20000):
    a = float('%d.%d' % (i, i))
    print(a)


When I ran each of these with /usr/bin/time (which I just learned about a couple weeks ago, and has replaced counting seconds on my fingers as my favorite benchmarking tool), the decimal version took an average of about 1.5 seconds (over 10 runs), while the float version took an average of 0.5. Just to make sure no overhead was getting in the way, I upped the limit to 40000 and ran it again. Decimal took 3.0 seconds, float 1.0. I can now confidently say that Python floats are about 3x the speed of Python decimals.

Or are they? While this tests the creation and printing of decimals and floats, it doesn't test mathematical operations. So, I wrote two more tests. I'm going to be doing a lot of division on these numbers, and that's definitely the most expensive mathematical operation to compute, so I made sure to do it in the tests (along with some subtraction).


# Decimal version
 
from decimal import Decimal
 
a = 0
for i in range(2, 20002):
    a = Decimal('%d.%d' % (i, i)) / Decimal('%d.%d' % (i - 1, i - 1))
    print(a)



# Float version
 
from decimal import Decimal
 
a = 0
for i in range(2, 20002):
    a = float('%d.%d' % (i, i)) / float('%d.%d' % (i - 1, i - 1))
    print(a)


This time, the float version averaged about 0.6 seconds (1.15 with 40,000 iterations instead of 20,000), while the decimal version averaged over 11 seconds (23 with 40,000 iterations instead of 20,000). So while Python float creation and printing is merely 3x as fast as Python decimal creation and printing, Python float division is almost 20x as fast as Python decimal division.

So what did I choose? Decimals. In the context of these tests, the decimal slowdown may seem significant, but if I finished my app using decimals and profiled it, I can almost guarantee (based on the speeds here) that the bottleneck would not be decimal division performance. If I was running an app that was handling hundreds of simultaneous requests, I may consider switching (I may also spring for better hardware, but that's a different topic). However, for my purpose, 1/20th the speed of floats is more than fast enough.

P.S. As my very late discovery of /usr/bin/time should suggest, I'm extremely new to benchmarking. If anyone has any suggestions for me, or criticisms of my method, please leave your thoughts. This is something I'd like to get better at.

Friday, December 12, 2008

Scraping the Bottom of the "Developer Productivity" Barrel

Patrick Smacchia writes a blog post about how buying a Solid State Drive will produce a worthwhile increase in developer productivity.

He cites a few self-run benchmarks. A couple are impressive: a certain build shaves off 2 minute and 14 seconds (a little under 50% faster), and running 1846 NUnit tests brings a 2 minute and 40 second process down to 37 seconds (about 80% faster). The rest... not so much. Most are cutting scant seconds off of already quick processes.

I do believe that there are some expenditures that are worth it. Developers spend 8 hours a day sitting, so buying them extremely comfortable chairs makes a huge difference. Similarly, developers spend 8 hours a day looking at their monitors, and buying them each two large monitors allows them to keep their work and minds better organized.

This SSD claim does not seem to pass muster. Look at the difference. An internal 80GB Intel SSD costs $559.99. How much does a normal 5400RPM internal 80GB drive cost? Well, I can get this Western Digital one for just over $40.00.

That means the Intel one costs almost 14 times as much. If I'm buying this for my developers, I want to make sure it's cost-effective. Let's say I pay my developers an average of $52.00 per hour (to make the numbers easy). That means, to justify the extra $520.00 I'm spending to get this SSD, it needs to save them 10 hours over the course of their time with me. That's 268 of those builds mentioned earlier, or 293 of those sets of NUnit tests.

Is this an outrageous number? No. But it's also not an obvious win, like buying a pair of large monitors or a comfortable chair. It's scraping the bottle of the barrel, to say the least. Unless he's got a super awesome chair, a couple 24-inch monitors, and all the other productivity staples, I'd say his money would be much better spent elsewhere.

Monday, December 8, 2008

Great Analogy for Entrenched Social Norms

I'm stealing this analogy from a blog post by core Django contributor James Bennett. I think it's brilliant:


There's an old joke, so old that I don't even know for certain where it originated, that's often used to explain why big corporations do things the way they do. It involves some monkeys, a cage, a banana and a fire hose.

You build a nice big room-sized cage, and in one end of it you put five monkeys. In the other end you put the banana. Then you stand by with the fire hose. Sooner or later one of the monkeys is going to go after the banana, and when it does you turn on the fire hose and spray the other monkeys with it. Replace the banana if needed, then repeat the process. Monkeys are pretty smart, so they'll figure this out pretty quickly: "If anybody goes for the banana, the rest of us get the hose." Soon they'll attack any member of their group who tries to go to the banana.

Once this happens, you take one monkey out of the cage and bring in a new one. The new monkey will come in, try to make friends, then probably go for the banana. And the other monkeys, knowing what this means, will attack him to stop you from using the hose on them. Eventually the new monkey will get the message, and will even start joining in on the attack if somebody else goes for the banana. Once this happens, take another of the original monkeys out of the cage and bring in another new monkey.

After repeating this a few times, there will come a moment when none of the monkeys in the cage have ever been sprayed by the fire hose; in fact, they'll never even have seen the hose. But they'll attack any monkey who goes to get the banana. If the monkeys could speak English, and if you could ask them why they attack anyone who goes for the banana, their answer would almost certainly be: "Well, I don't really know, but that's how we've always done things around here."

Friday, November 14, 2008

Don't Wonder if You Are Stupid

I'm reading a book on Mac programming called Cocoa Programming for Mac OS X. However, unrelated to programming at all was this piece of advice on believing in yourself:

While learning something new, many students will think, "Damn, this is hard for me. I wonder if I am stupid." Because stupidity is such an unthinkably terrible thing in our culture, the students will then spend hours constructing arguments that explain why they are intelligent yet are having difficulties. The moment you start down this path, you have lost your focus.

I used to have a boss named Rock. Rock had earned a degree in astrophysics from Cal Tech and had never had a job in which he used his knowledge of the heavens. Once I asked him whether he regretted getting the degree. "Actually, my degree in astrophysics has proved to be very valuable," he said. "Some things in this world are just hard. When I am struggling with something, I sometimes think 'Damn, this is hard for me. I wonder if I am stupid,' and then I remember that I have a degree in astrophysics from Cal Tech; I must not be stupid."


I think this is great advice. There have been many times where I've been struggling with something outside of computer science (like calculus or my Oceanography class last semester). Just as I start to consider giving up, I remember that I've found a way to succeed at some very difficult things; things that are much more difficult than these. It gives me the confidence to continue banging my head against the wall until I finally get it.

Thursday, November 6, 2008

Don't Criticize What You Don't Understand

Jessica Dolcourt of Webware criticizes the initial Android version of Meebo IM for not having enough features. I think Jessica needs to read up a little on the idea of iterative development.

Array Slicing is Messed Up

The mechanics of array-slicing (and, by extension, string-slicing) have bothered me since my very first Computer Science class. For those of you who aren't programmers but are braving the programmer warning, let me explain a little. Arrays in programming are essentially lists of data. For example, I could keep an array of test grades, and it might look something like this:

array = [99, 72, 85, 85, 100, 61, 88, 32]

Arrays throw beginners off a little bit, because indices are zero-based. This means that if I want to access specific elements in an array, the first element is considered element 0, the second is element 1, etc. An example using the above array: