Friday, July 3, 2009
Software Development as a Profession?
Friday, June 26, 2009
Software Development is Hard

Friday, June 19, 2009
The Talent Code
I read an interesting book couple of weeks ago. The Talent Code by Daniel Coyle. While the whole book is really good I particularly liked the section explaining the "sudden" appearance of extraordinary talents - like Brazilian football players, Bronte's sisters, Mozart, venetian sculptors and painters and so on. How it's believed that all those people were simply born with great talent and their great work just happened. And how the reality was almost a total opposite. The main idea behind the book is that no matter what you do, in order to achieve a world class skill you need to spend 10,000 hours in practice. The skill is basically "neural connections" in the brain. Those connections are created during practice but what determines the level of the skill is the "quality" of those connections. What increases the quality of the connection is myelin by insulating nerve fibers. The 10,000 hours is necessary to create sufficient myelin. What's interesting is that it's not just any practice - the best results are achieved in "deep practice".
The author goes on to explain how any of the great talents already had their 10,000 hours clocked in by the time they were "discovered". How bronte's sisters had written tens of training books before Jane Eyre or Wuthering Heights, how Mozart had his 10,000 hours of practice in very early age and how venetian painters and sculptors got their hours of training in apprenticeship. They were not born great but started from scratch and achieved their greatness by increasingly improving their skills. Interesting thing is that the apprenticeship didn't mean that they would simply paint the whole day and after 10 years became masters. No - they had to do all sorts of things - especially all sorts of "low level things" - like setting the canvas, preparing chisels and so on for their masters. Only after they learnt the basics could they move on to more difficult things. And this is what basically constitutes the "deep training" - choose a goal just outside your comfort zone and keep on failing until you achieve it. Then repeat the process.
To sidetrack a bit to IT - this is where I think today's system breaks down. Software development is hard. And it only starts with the technical skills - you still need to understand the domain (when working on accounting system you have to have a very solid understanding of accounting), you have to be able to work with people and translate their ideas and feelings about something they've never seen to reality. And there is no magic shortcut - you have to have your 10,000 hours clocked in before you can really do something. Programming is like writing - just like bronte's sisters - you have to write a lot a lot a lot and about everything you can find - only then your writing will start making sense. Unfortunately, I wouldn't really count any hours spent at school. Maybe it's an unfortunate situation around here - but the IT schools here are set to producing CIO's making decisions on a golf course rather than doing programing or any other actual IT work. To their defense that's pretty much what market here wants - most of the fresh grads from IT will find a good shake-leg work in a bank or MNC paying at least $10,000 per month. I meet quite a few of those people in my trainings or recently, as the banks scale down certain areas, in interviews.
Anyway, my point here was that only very very few people in IT are willing (or forced) to go through the whole journey - from setting up networks, installing computers, moving to servers, maintaining different operating systems, doing backup / recovery, working with databases, optimizing databases, writing SQL, programming in several languages for several years, understanding patterns going through several releases and so on. And none of that means "I used MS Access in 3rd semester".
Another interesting thing was the difference between masters and beginners. The difference was explained on chess players. The difference between chess masters and beginners is that masters can remember the whole board set up on one look. There is a twist, though. They could only remember the set up from an actual game. If the figures were in random order the chess masters photographic memory vanished. The reason why chess masters could remember the board setup from actual games is because they were not seeing the individual pieces - they were seeing the patterns.
I picked up the book because I wanted to know more about how people learn - I wanted to understand why after a year of training and working some of my employees are not able to do even a simple task, why is it that they spent a week installing a server without any success while somebody else can do the same work in 1 hour. Why some spend months and months programming a piece of functionality creating a total mess and somehow not "seeing" the simple solution that can be finished in a few hours or days. I used to be especially puzzled about the not seeing part. Even after showing them the simple solution they just couldn't understand it and had to go a big round of all possible wrong ways to arrive at the same solution. I guess some of them see patterns while others see thousands of lines of commands. As a result, in the light of the book's 10,000 hours I start to look very differently at this kind of things. The good news is: it's learnable. The bad news is: it may take 10 years for someone to learn it.
Saturday, May 9, 2009
Rails 2.3 with Ruby 1.9

incompatible character encodings: UTF-8 and ASCII-8BITwhen data from database contain special characters. There are several patches available in lighthouse here and here but non of them really worked for me - safe for
contact.name.force_enconding('utf-8')4) send_data doesn't work on binary files with message invalid byte sequence in UTF-8. I used to use it for on the fly generated images but had to replace it with creating a temp file and then use
send_file
Friday, May 1, 2009
File Downloads in Rails on Apache
Over the past half a year we worked on several systems for logistics/courier/delivery companies. It was quite fun as a big logistics and inventory control system was my very first project when I arrived to Asia 9 years ago. Last week we launched one of them. It was slightly different as most of our systems are used mainly internally but this one is also used by clients of the courier company to book deliveries. Each delivery requires printing of confirmation documents, internal routing documents, then signed and stamped documents are uploaded back to the system as a proof of delivery for clients. It has quite a number of other (technically) interesting features like integration with google maps to geo-tag the orders and notifying drivers in the area - I'll write about it soon.
As this courier company handles several hundreds of deliveries every day it requires quite a number of uploads/downloads of documents and images. The issue with uploads and downloads is that they will tie up rails processes for much longer than usual requests. The problem is that rails processes are quite expensive in terms of memory (around 70 - 100MB for simple operations). Do 20 concurrent uploads/downloads and all your rails processes and memory will get tied up - and quite wastefully as there is no 'dynamic' processing during upload or download. The process just sits there and waits for the file to get delivered. Luckily there's a simple way how to move this burden to Apache. This kind of upload/download processes will take only around 2MB on average and your rails processes are free to handle the dynamic requests.
To let apache handle file downloads use x_sendfile. All you need to do is install x_senfile module on apache and adjust you config a little. Here's the installation instructions for Ubuntu 8.04:cd /usr/local/src wget http://tn123.ath.cx/mod_xsendfile/mod_xsendfile.c apxs2 -cia mod_xsendfile.cOpen apache config file and add the module line:
sudo vim /etc/apache2/apache2.conf
LoadModule xsendfile_module /usr/lib/apache2/modules/mod_xsendfile.soAdd the following 2 lines to your virtual host file
sudo vim /etc/apache2/sites-available/my_app
XSendfile on XSendFileAllowAbove onYou will have to restart the apache and you should be done:
sudo /etc/init.d/apache2 restartNow in your controller handling download add x_sendfile attribute and set it to true
send_file("/home/test/big_file.zip",
:filename => "big_file.zip",
:type => "application/zip,
:x_sendfile=> true)
The uploads are equally easy. You just need to apache modules libapreq2 and modporter.
wget http://mirror.nus.edu.sg/apache/httpd/libapreq/libapreq2-2.12.tar.gz tar -xzf libapreq2-2.12.tar.gz cd libareq2-2.12 ./configure --with-apache2-apxs=/usr/bin/apxs2 make sudo make installNow download and install modporter. Get it from the github. Either use the download link and unzip or just clone the repository
git-clone git://github.com/actionrails/modporter.git cd modporterOn Ubuntu I had to change reference to apxs in Rakefile as the in ubuntu it's apxs2.
vim RakefileChange the APXS line to the following:
APXS = "apxs2"No just run rake:
sudo rakeadd the following to your apache2.conf
sudo vim /etc/apache2/apache2.conf
LoadModule apreq_module /usr/lib/apache2/modules/mod_apreq2.so LoadModule porter_module /usr/lib/apache2/modules/mod_porter.soTo actually use it in your rails application you will need modporter plugin.
script/plugin install git://github.com/actionrails/modporter-plugin.gitAdd the following to the application_controller.rb (application.rb before Rails 2.3)
class ApplicationController < mod_porter_secret = "secret">and just enable porter in you vhost file:
sudo vim /etc/apache2/sites-available/my_app
Porter On PorterSharedSecret secretAnd that's all now if you check the uploaded files they will be a ModPorter::UploadedFile Credit for this goes to Koz from theRailsWay.com - you can find more information here.
Tuesday, April 14, 2009
Another Training
Monday, April 13, 2009
Dynamic Columns In Ext JS Grid
What I needed was a column model dynamically adjusting depending on the data received from data store (i.e. JSON provided by server). Something like the following:
Where each site has a different set of dimensions (rows) and different set of auditors (columns).
It turned out that the problem was actually two-fold - first dynamically change the columns and then display the editable checkboxes instead of true/false values.
To change the columns I used metaData attribute in my JSON. Including metaData attribute causes Ext.data.JsonReader to be adjusted to the new set of fields and other config parameters and fire metachange event. Here's an example of the JSON with metaData:
{
"results": 3,
"auditors": [
{"dimension_name": "Audits and Reviews", "auditor_3": false, "dimension_id"
: 10, "id": 10, "auditor_4": false, "auditor_18": false},
{"dimension_name": "Emergency Management & Fire Safety", "auditor_3": false, "dimension_id": 8, "id": 8, "auditor_4": false, "auditor_18": false}
.
.
.
],
"metaData": {
"fields": [
{"name": "id"},
{"type": "string", "mapping": "dimension_name", "name": "auditor[dimension]"},
{"type": "int", "mapping": "dimension_id", "name": "auditor[dimension_id]"},
{"type": "bool", "name": "auditor_3", "header": "Dasha Alexo"},
{"type": "bool", "name": "auditor_18", "header": "Tester 3"},
{"type": "bool", "name": "auditor_4", "header": "Zobak Zobakovic"}],
"root": "auditors",
"totalProperty": "results",
"id": "id"
}
}
In this case it's the number of auditors (and their names) that's changing. My data types are booleans but displaying any other data type would look pretty much the same.
Next I needed to update the column model based on the new meta data. I did that by adding metachange listener to my store
auditors_store.addListener("metachange", function(store, meta){
var grid = Ext.getCmp('auditors-grid');
var columns = [
{header: 'Dimension', dataIndex: 'auditor[dimension_id]', hidden: true},
{header: 'Dimension', dataIndex: 'auditor[dimension]', width: 200}
];
for (var i = 3; i < meta.fields.length; i++ ) {
var plugin = new Ext.grid.CheckColumn({
header: meta.fields[i].header,
dataIndex: meta.fields[i].name,
grid: grid,
width: 120
});
columns.push(plugin);
plugin.init(grid);
//use columns.push( { header: meta.fields[i].header, dataIndex: meta.fields[i].name, type: meta.fields[i].type }); for fields that don't require plugins
}
grid.reconfigure(auditors_store, new Ext.grid.ColumnModel(columns));
});
I had to create and initialize the plugin for each new column as I needed editable checkboxes. For normal fields just use the usual column config object { header: meta.fields[i].header, dataIndex: meta.fields[i].name, type: meta.fields[i].type }.
The actual magic happens is in the last line. Grid has an obscure method reconfigure( Ext.data.Store store, Ext.grid.ColumnModel colModel ) that allows to switch the store and column model (the columns displayed) on the fly.
My next problem was to make the checkboxes editable. To do that I had to put a small fix to the CheckColumn plugin (see the plugin) as the original plugin assumes that the grid is not rendered when initializing, however, when dynamically adding columns the grid is already rendered so I had to change the following:
init : function(grid){
this.grid = grid;
this.grid.on('render', function(){
var view = this.grid.getView();
view.mainBody.on('mousedown', this.onMouseDown, this);
}, this);
},
to the following:
init : function(grid){
this.grid = grid;
if (this.grid.rendered) {
var view = this.grid.getView();
view.mainBody.on('mousedown', this.onMouseDown, this);
} else {
this.grid.on('render', function(){
var view = this.grid.getView();
view.mainBody.on('mousedown', this.onMouseDown, this);
}, this);
}
},
And that's all - nice and simple. If only there were more books on Ext :-(
Wednesday, April 8, 2009
Agile Employment Contracts (part 1)
Tuesday, April 7, 2009
Ubuntu 8.04 mod_rails
sudo apt-get install apache2 apache2.2-common apache2-mpm-prefork apache2-utils libexpat1 ssl-cert apache2-prefork-dev sudo gem install passenger sudo passenger-install-apache2-moduleNow add the 3 generated lines (almost last on the screen) to config file:
sudo vim /etc/apache2/apache2.confRight now it looks something like the following - BUT THE VERSION NUMBERS WILL CHANGE SO COPY THE LINES FROM THE PREVIOUS PROCESS
LoadModule passenger_module /usr/local/lib/ruby/gems/1.8/gems/passenger-2.1.2/ext/apache2/mod_passenger.so PassengerRoot /usr/local/lib/ruby/gems/1.8/gems/passenger-2.1.2 PassengerRuby /usr/local/bin/rubyIn the same config file add the server name and NameVirtualHost otherwise the server will complain at restart:
ServerName XYZ NameVirtualHost *:80save the config file and continue - we'll need to enable modrewrite and disable default site:
sudo a2enmod rewrite sudo a2dissite defaultnow just configure your application by adding your_app_name to /etc/apache2/sites-available/ with the following content:
sudo vim /etc/apache2/sites-available/your_app_name
<VirtualHost *:80> ServerName abc.def.com ServerAlias xyz.com DocumentRoot /home/bohm/rails/access/current/public </VirtualHost>where abc.def.com and xyz.com are the URLs of your application and DocumentRoot points to the public folder of your application. To enable the application run
sudo a2ensite your_app_name sudo /etc/init.d/apache2 reloadTo disable it run
sudo a2dissite your_app_name sudo /etc/init.d/apache2 reloadAnd you're done :-)
Monday, April 6, 2009
Multiline Tree nodes in ExtJS
new Ext.tree.TreePanel({
//renderTo:'treecontainer',
id: 'upcoming_events',
loader: new Ext.tree.TreeLoader({
dataUrl:'/datasource/upcoming_events_json',
baseParams: {},
baseAttrs: {uiProvider: Ext.ux.tree.MultilineTreeNodeUI}
}),
root: new Ext.tree.AsyncTreeNode({
expanded: true
}),
rootVisible: false,
collapsed: false
})
Just include the MultilineTreeNodeUI.js in the page and create a usual TreePanel. The only difference is to include baseAttrs: {uiProvider: Ext.ux.tree.MultilineTreeNodeUI} in your loader. Multilne tree requires the data in the following format:
{
text: '09 April 2009',
iconCls: 'calendar',
details: ['Program for the day...'],
uiProvider: Ext.ux.tree.MultilineTreeNodeUI,
children:[
{
text: '9:30',
iconCls: 'time',
details: ['Win $1M', 'Sure win...'],
uiProvider: Ext.ux.tree.MultilineTreeNodeUI,
leaf: true
},
{
text: '11:30',
iconCls: 'time',
details: ['Buy private jet', 'Or something'],
uiProvider: Ext.ux.tree.MultilineTreeNodeUI,
leaf: true
}
]
}
Where the additional lines are provided by details attribute. It's an array - one item per line - maybe not an ideal solution but still workable - worst case you just split the longer line by number of characters. The uiProvider will generate the multiple lines. Your controller doesn't have to do anything about it as it will be added automatically by the baseAttrs: {uiProvider: Ext.ux.tree.MultilineTreeNodeUI} we added in the loader.
