Tuesday, December 19, 2006
The Man Who Sold The World
Just for fun, compare the lives of Edison and his great rival Nikola Tesla. Edison was everything that Tesla was not. In particular, financially successful. However Tesla really was a great inventor. Without Tesla's polyphase alternating current power distribution technology, the world would not have developed the widespread use of electric power.
Which is not to say that Edison did not contribute anything...but his contributions were more developmental than inventive in nature.
Patently Absurd
As someone who gets paid by creating IP, I am supposed to support the US patent system, right? But I cannot help but think, what if the attitude of the early innovators in software had been like that of the current industry? How would have kids like me grown up hacking the Apple ][ using the published source code of its ROM? How would the Mac or Windows been created without mimicking the various OSes that went before it? How would innovation have occurred at all, let alone at the pace at which we have grown?
I look at my career, and it only exists thanks to standing on the shoulders of the giants. Where will we tell the next generation of dead programmers to stand? If we do not cultivate the minds and collective knowledge of our industry, the future programmers may be standing in an unemployment line.
Patents are intended to protect the public good, not merely to enrich individuals. If we do not reform the patent system for software, and quickly, we run the risk of massive consolidation in intellectual capital. Without the entrepreneurism and sharing of ideas that have made the software industry an engine of economic growth, the future of the US will be much poorer as a result.
Thursday, December 14, 2006
Merb Is The Word
Where this all started, was that I was looking for some way to use Mongrel to create a new REST web service for a client. This service has no UI, and basically just needs to front end a Ruby DSL that I have been working on. Another need was that I had to have something that can easily handle multithreading. Rails will block on a single thread, and also I do not need all of the Rails AJAXy goodness. So Rails seemed like a bit too much, and Camping not quite enough.
It was at this point that I discovered Merb. Merb handles threading very simply, by dispatching each new request to a new controller created on a new thread. It also has a very clean and simple support for rendering of HTML, JavaScript, and XML views. In my case, I only really cared about returning XML, so Merb seemed ideal for my needs on this project.
Starting a new Merb project requires some hand copying of files. There is a sample application included, which helps see some of what Merb can do, as well as showing how to setup your own project. At this point, Merb is very early in its lifecycle, so if you want to use it, prepare for a hands-on experience. The Merb code is very simple and clear, however, so it is not hard to see what is going on under the hood.
One of the main problems that Merb was designed to solve is better handling of long running server processes. One example of this is uploading files to your web server. Rails will block on each file upload until the upload is completed, which is a recipe for poor performance (read server unresponsive) if you will have a lot of requests to upload big files. The Merb sample app shows how to use Merb to deal with file uploads. And since Merb supports ActiveRecord, you could easily integrate a Rails app with a small Merb app or two to handle some performance bottlenecks.
Merb is much newer than Rails or Camping, but despite it not being even quite half-baked yet, the parts that are finished are delicious! Looks like it will be a really nice option for an alternative/adjunct to Rails. Now someone just needs to create a generator that lets you start a new Merb project...and some documentation/tutorials.
Monday, December 11, 2006
Resistance To Change
Obie notes that "innovators catch the most grief from management". But why would this be the case, when management is so eager to capitalize on said innovations? Perhaps a look at anthropology can help. A few years ago I watched a fantastic program on the Discovery Channel called "Walking With Cavemen." As one review of the DVD on Amazon states, "Using real actors and actresses, amazing make-up and special effects, Professor Winston takes us through the lives and times of our remote caveman ancestors." The part that impressed me the most was a example of why Neanderthal was eventually replaced by Homo sapiens, although the two lived side by side for thousands of years.
Homo sapiens was able to learn from experience, and change behavior based on new observations. The example used in the show was that by seeing a group of scavenger birds flying in a circle in the distance, and then noting that a dead animal was in the same location where the birds had been flying, a group of homo sapiens was able to find food the next time they saw birds flying in a circle. A group of Neaderthal was unble to make the connection, and starved to death.
Consequently, homo sapiens was able to survive climate changes that caused Neanderthal to become extinct. Simply enough, our capacity to change and adapt, which is our greatest survival trait as a species, is what most people actually resist the most.
Friday, December 08, 2006
That Ruby Is So Hot Right Now...
Ruby is still surging in popularity, in fact it has had the biggest rise in popularity over the last year of any language in the top 20. I know that Python/Django enthusiasts have been dissing Ruby heavily of late, but I refuse to get drawn in to any such nonsense. Any language that people actually use has good things going for it at something, or no one would use it at all. Especially any language that is reasonably popular.
Besides, dead programmers are eager to learn new languages. I myself am fluent in 7 of the top 20 languages, and able to fake my way thru a few others. And I'm sure that there are others out there who know more of the languages in this list than I do.
But my love affair with Ruby continues, and it sure looks like I am not the only one!
Friday, December 01, 2006
RubySSPI Gets Thru The Firewall
Recently Justin Bailey released a Gem called RubySSPI that patches the Net::HTTP class to support NTLM authentication. I was excited! I manually downloaded and installed it...but it did not work for me. Others had successfully used it, however, so I did not give up so easily.
I have been working with Justin Bailey to help him figure out why it was not working in our typical huge corporate environment, and we have achieved success! I was just able to download a Gem in the "normal" way. I'm sure he will uploading an updated version of the Gem soon...Thank you, Justin!
Saturday, November 25, 2006
New Hebruby Release
To install it, just:
gem install hebruby
Thank you to everyone who has helped with Hebruby, especially Joshua Harvey, and enjoy!
Sunday, November 19, 2006
Ruby Domain Specific Languages - The Basics (Part 3)
First, I want to simplify the syntax for declaring things in our DSL. Getting rid of the class definition stuff can make it a lot easier for domain experts to read. A simple, cool declarative style like Rake is what we want to end up with, something like this:
person "Dorothy" do
temperament :nice
food :sunflower_seeds, :carrot_juice
end
Furthermore, I want to extend our DSL to put all of the descriptions of Pets and Persons together into a PetShop. Here is our PetShop DSL:
shop = PetShop.create do
pet "Toto" do
friend_test do |person|
true unless person.temperament == :mean
end
end
pet "Tweety" do
friend_test do |person|
person.has_food?(:sunflower_seeds)
end
end
pet "Slugworth" do
friend_test do |person|
true # I like anyone
end
end
person "Dorothy" do
temperament :nice
food :sunflower_seeds, :carrot_juice
end
person "Witch" do
temperament :mean
food :cheetos, :soda
end
end
One interesting technique used is declaring the "pet" within the "do-end" block for the "petshop" object:
shop = PetShop.create do
pet "Toto" do
friend_test do |person|
true unless person.temperament == :mean
end
end
...
end
The little bit of DSL niceness is achieved using the class_eval method. The block of code within the "do" block is called in the context of the newly created object. Here is the Ruby code that achieves this for a new Pet:
def self.pet(name, &blk)
@pets = Hash.new
p = Pet.new(name)
p.class.class_eval(&blk) if block_given?
@pets[name] = p
p.copyvars
end
Now that we have declared all of the Pets and Persons in to the context of a PetShop, we can test the relationships between Pets and Persons is a more general purpose way then in my previous post. The older, more fragile code was this:
dog = Toto.new
bird = Tweety.new
snail = Slugworth.new
person = Dorothy.new
puts "#{dog.class.name} is a friend of #{person.class.name}: #{dog.is_friend?(person)}"
puts "#{bird.class.name} is a friend of #{person.class.name}: #{bird.is_friend?(person)}"
puts "#{snail.class.name} is a friend of #{person.class.name}: #{snail.is_friend?(person)}"
puts
person = Witch.new
puts "#{dog.class.name} is a friend of #{person.class.name}: #{dog.is_friend?(person)}"
puts "#{bird.class.name} is a friend of #{person.class.name}: #{bird.is_friend?(person)}"
puts
The new, cleaner code is like this:
shop.people.each_value do person
shop.pets.each_value do pet
puts "Is #{pet.name} a friend of #{person.name}? #{pet.is_friend?(person)}"
end
end
And here is the output when we run the program:
Is Toto a friend of Witch? false
Is Slugworth a friend of Witch? true
Is Tweety a friend of Witch? false
Is Toto a friend of Dorothy? true
Is Slugworth a friend of Dorothy? true
Is Tweety a friend of Dorothy? true
We have simplified the syntax of our domain specific language, and added some additional functionality. We have also been able to reduce the amount of Ruby code required at the same time.
Here is the final version of the code:
class DSLThing
def copyvars
self.class.instance_variables.each do |var|
instance_variable_set(var, self.class.instance_variable_get(var))
end
end
end
class PetShop < DSLThing
attr_accessor :pets, :people
def self.create(&block)
f = PetShop.new
f.class.class_eval(&block) if block_given?
f.copyvars
return f
end
def self.pet(name, &blk)
@pets ||= Hash.new
p = Pet.new(name)
p.class.class_eval(&blk) if block_given?
@pets[name] = p
p.copyvars
end
def self.person(name, &blk)
@people ||= Hash.new
p = Person.new(name)
p.class.class_eval(&blk)
@people[name] = p
p.copyvars
end
end
class Animal < DSLThing
attr_accessor :name
def initialize(name=nil)
@name = name
end
end
class Person < Animal
attr_accessor :temperament
def initialize(name=nil)
super
end
def self.temperament(type)
@temperament = type
end
def self.food(*types_of_food)
@food = []
types_of_food.each do |food|
@food << food
end
end
def has_food?(type_of_food)
@food.include?(type_of_food)
end
end
class Pet < Animal
def initialize(name=nil)
super
end
def self.friend_test(&test)
@friend_test = test
end
def is_friend?(person)
@friend_test.call(person) == true
end
end
shop = PetShop.create do
pet "Toto" do
friend_test do |person|
true unless person.temperament == :mean
end
end
pet "Tweety" do
friend_test do |person|
person.has_food?(:sunflower_seeds)
end
end
pet "Slugworth" do
friend_test do |person|
true # I like anyone
end
end
person "Dorothy" do
temperament :nice
food :sunflower_seeds, :carrot_juice
end
person "Witch" do
temperament :mean
food :cheetos, :soda
end
end
shop.people.each_value do |person|
shop.pets.each_value do |pet|
puts "Is #{pet.name} a friend of #{person.name}? #{pet.is_friend?(person)}"
end
end
Monday, November 13, 2006
The Dark Side Of Metrics
But any tool, no matter how well intentioned its purpose, can be used for ill. Any system that relies entirely on user entered information can by lied to. Such systems can be gamed, for various purposes. In a very political environment, a rogue group can manipulate their own metrics to gain power over other groups, gain budget, or simply steer the company in the direction of their own egos. Sometimes metrics can be used to hide something important. "But look at our metrics," protests the manager in charge of a spectacular failure.
Wednesday, November 08, 2006
Ruby Domain Specific Languages - The Basics (Part 2)
Last time we created a simple Animal DSL class. The important part of that class is this bit that makes sure our DSL like syntax works for the declarative methods:
class Animal
attr_accessor :number_of_legs
def self.number_of_legs(number_of_legs)
@number_of_legs = number_of_legs
end
def initialize
self.class.instance_variables.each do |var|
instance_variable_set(var, self.class.instance_variable_get(var))
end
end
end
Now we will create a Person class that can interact with the Animals. Each Person will have a temperament (either mean or nice), and will be carrying some kind of food in their pocket to feed their pet. We will define people using our DSL as follows:
class Dorothy < Person
temperament :nice
food :sunflower_seeds, :carrot_juice
end
class Witch < Person
temperament :mean
food :cheetos, :soda
end
The implementation is very similar to the basic Animal class
class Person < Animal
attr_accessor :temperament
def self.temperament(type)
@temperament = type
end
def self.food(*types_of_food)
@food ||= []
types_of_food.each do |food|
@food << food
end
end
def has_food?(type_of_food)
@food.include?(type_of_food)
end
end
Since we are working with an array, the self.food method has a variable number of parameters, represented using the asterisk in front of the parameters list. The cool little idiom @food ||= [] returns with the current @food variable, or if it is nil, returns an empty array. There is also a has_food? method to tell us if a person is carrying a certain type of food.
Now let us introduce the Pet. We will use the power of Ruby blocks to tell each pet the rules about when it likes a person, or not. Here is the DSL we want to use for the Pets:
class Toto < Pet
friend_test do |person|
true unless person.temperament == :mean # I like anyone who is not mean
end
end
class Tweety < Pet
friend_test do |person|
true if person.has_food?(:sunflower_seeds) # I like anyone who has sunflower seeds
end
end
class Slugworth < Pet
friend_test do |person|
true # I like anyone
end
end
And now the implementation of the Pet class:
class Pet < Animal
def self.friend_test(&test)
@friend_test = test
end
def is_friend?(person)
@friend_test.call(person) == true
end
end
The friend_test method stores a block containing the test for that Pet, and the is_friend? method tests to see if a Pet will be friendly toward a particular Person.
Last, we put it all together with some simple code to display the interactions between the People and the Pets:
dog = Toto.new
bird = Tweety.new
snail = Slugworth.new
person = Dorothy.new
puts "#{dog.class.name} is a friend of #{person.class.name}: #{dog.is_friend?(person)}"
puts "#{bird.class.name} is a friend of #{person.class.name}: #{bird.is_friend?(person)}"
puts "#{snail.class.name} is a friend of #{person.class.name}: #{snail.is_friend?(person)}"
puts
person = Witch.new
puts "#{dog.class.name} is a friend of #{person.class.name}: #{dog.is_friend?(person)}"
puts "#{bird.class.name} is a friend of #{person.class.name}: #{bird.is_friend?(person)}"
puts "#{snail.class.name} is a friend of #{person.class.name}: #{snail.is_friend?(person)}"
The result of running this code is:
Toto is a friend of Dorothy: true
Tweety is a friend of Dorothy: true
Slugworth is a friend of Dorothy: true
Toto is a friend of Witch: false
Tweety is a friend of Witch: false
Slugworth is a friend of Witch: true
Using this simple Ruby DSL approach, it is very easy to add a new Person or Pet, just by entering the rules that define its behavior. As the objects in a system become more complex, one of the best ways to manage that complexity is to create an abstraction that hides the ugly bits.
Here is the full code for this example:
class Animal
attr_accessor :number_of_legs
def self.number_of_legs(number_of_legs)
@number_of_legs = number_of_legs
end
def initialize
self.class.instance_variables.each do |var|
instance_variable_set(var, self.class.instance_variable_get(var))
end
end
end
class Person < Animal
attr_accessor :temperament
def self.temperament(type)
@temperament = type
end
def self.food(*types_of_food)
@food ||= []
types_of_food.each do |food|
@food << food
end
end
def has_food?(type_of_food)
@food.include?(type_of_food)
end
end
class Pet < Animal
def self.friend_test(&test)
@friend_test = test
end
def is_friend?(person)
@friend_test.call(person) == true
end
end
class Toto < Pet
friend_test do |person|
true unless person.temperament == :mean
end
end
class Tweety < Pet
friend_test do |person|
person.has_food?(:sunflower_seeds)
end
end
class Slugworth < Pet
friend_test do |person|
true
end
end
class Dorothy < Person
temperament :nice
food :sunflower_seeds, :carrot_juice
end
class Witch < Person
temperament :mean
food :cheetos, :soda
end
dog = Toto.new
bird = Tweety.new
snail = Slugworth.new
person = Dorothy.new
puts "#{dog.class.name} is a friend of #{person.class.name}: #{dog.is_friend?(person)}"
puts "#{bird.class.name} is a friend of #{person.class.name}: #{bird.is_friend?(person)}"
puts "#{snail.class.name} is a friend of #{person.class.name}: #{snail.is_friend?(person)}"
puts
person = Witch.new
puts "#{dog.class.name} is a friend of #{person.class.name}: #{dog.is_friend?(person)}"
puts "#{bird.class.name} is a friend of #{person.class.name}: #{bird.is_friend?(person)}"
puts "#{snail.class.name} is a friend of #{person.class.name}: #{snail.is_friend?(person)}"
The Planning Game Vs. The Crying Game
In the Planning Game, the play is not quite so simple as the description above might suggest. As each team discusses a feature from their perspective (cost vs. value), the requirements for that feature can come into clearer focus, or be negotiated to reduce their cost.
More often, what I have experienced is the "Crying Game": whoever cries the loudest gets their way. The outcome of the Crying Game is not a good one. Either arbitrary and unrealistic deadlines are set by customers, or the wrong solution is provided by the developers. The crying doesn't end there; everyone is crying later as the recriminations fly back and forth. The moral of the story is that internal cooperation yields a lot more business value than internal competition.
Saturday, November 04, 2006
Ruby Domain Specific Languages - The Basics (Part 1)
I have been working on a prototype of a Ruby domain specific language for one of my clients, a very large financial services company. I have learned a whole bunch of really interesting lessons, which I will share over a short series of posts as I make more progress.
The first thing I tried to do was create a basic bit of DSL tastiness like this:
class Dog < Animal
number_of_legs 4
end
class Bird < Animal
number_of_legs 2
end
class Snail < Animal
number_of_legs 0
end
My first implementation of the Animal class looked like this:
class Animal
attr_accessor :number_of_legs
def self.number_of_legs(number_of_legs)
@number_of_legs = number_of_legs
end
end
Just looking at this bit of code, it seems like it should work. However, when trying this, it doesn't work as expected.
pet = Dog.new
p pet.number_of_legs # prints nil
In Ruby EVERYTHING is an object. This includes the class objects themselves, not just the instances of class objects! This means that when the number_of_legs method is called, it is actually being called before the actual instance object itself has been created. We need to get to our instance variable, and one way is to copy the class-level instance variable to the instance itself when the actual instance is being initialized like this:
class Animal
attr_accessor :number_of_legs
def self.number_of_legs(number_of_legs)
@number_of_legs = number_of_legs
end
def initialize
instance_variable_set("@number_of_legs", self.class.instance_variable_get("@number_of_legs"))
end
end
Now the class works as expected:
pet = Dog.new
p pet.number_of_legs # prints 4
As you add new things to your DSL, having to copy each variable manually is boring. The final change is to copy every class instance variable automatically at initialization, as follows:
class Animal
attr_accessor :number_of_legs
def self.number_of_legs(number_of_legs)
@number_of_legs = number_of_legs
end
def initialize
self.class.instance_variables.each do var
instance_variable_set(var, self.class.instance_variable_get(var))
end
end
end
This is a lot cleaner, and is much more maintainable code.
Thursday, November 02, 2006
Gmail For Mobile Rocks
I just installed the brand new Gmail for Mobile on my Motorola RAZR. In just a matter of a couple moments, I was looking at my Gmail inbox! Gmail for Mobile is a Java-applet that runs on your Java enabled phone. Keeping the network traffic down this way is a lot better option that a WAP-based email solution, which is what I had tried and abandoned previously.
Not having a QWERTY keyboard is a big limitation for smartphones especially where something heavily textual like email is concerned. Gmail for Mobile deals with this thru a clever mapping of hotkeys to the most commonly used functions (delete, in my case).
Between Google Reader, and now this Gmail for Mobile, another bit of my online existence has been sucked into the Googlesphere...and I like it!
Monday, October 23, 2006
Ruby Can Now Take Over The World
MS had already jumped into dynamic languages with the acquisition of IronPython. Now it only a matter of time until "Ruby#" or whatever MS will call it (since there is already a Ruby.NET project, which is heading in yet another direction). *nix purists and MS haters may be cringing, but I think it quite legitimizes the Ruby meme to naysayers who may have thought Ruby was just a fad.
Working in the big corporate space, one of the primary objections that I have encountered to implementing systems written in the Ruby language, is needing to run on Windows and use existing complex .NET services that are already deployed in the enterprise. A Ruby coming from Microsoft, on top of a Ruby coming from Sun (JRuby), just sends a clear message to corporate types that "This Ruby thing is real". Heh. Heh heh, even.
Many of us have been trying to get Ruby taken seriously for a while, and this is exactly the kind of ammo that dead programmers need to not just sneak Ruby in, but storm the castle walls! Ruby will rule!
Toward A Test-Driven Culture
Software testers are considered "lower on the food chain" then software developers or analysts. That means testers are compensated less than their developer or analyst counterparts. Even on many important projects, across both companies and industries, this is still the case. This amazes me, given how much of the world runs on software. If the software that runs your airplane's landing gear, or your electronic voting system, or whatever other ultra-critical system has not been sufficiently tested, lives can be at stake. Despite the fantastic work being done in the area of software quality, and the proliferation of practices that can dramatically reduce the cost of creating and maintaining a system, we still all too often see quality getting shortchanged on required resources.
So testing is good, and we need more of it, surely. But we need to go further, much further. Giving proper attention to test-driven development is great. But even all this is not enough. What we need to do is to apply a test-driven mentality to all aspects of business. Marketers have found that testing and measuring their marketing yields a far greater ROI then marketing based on opinion or theory. Salespeople have been measured by quotas, and the most successful sales organization performing testing for some time.
In particular, we need to move toward management philosophies that apply testing to business decisions, in particular those about processes. All too often, decisions have consequences often than those intended, perhaps failing in one of more objectives. A new process designed to streamline a company, can actually result in adding more burden with less efficiency.
Without the ability to test, management will eventually lose control over itself. With no feedback, failures may be looked at as successes, and vice versa. Management cannot be arbitrary; by carefully adopting a test-driven culture across all disciplines within an organization we can improve both the quality of our work, as well as the quality of the experience we have while doing that work.
Sunday, October 22, 2006
The Coolest New Device I've Seen For A While
I just saw a really neat new gadget called "The Device Patented Process Indicating Apparatus " for the first time. Check it out at http://www.processindicator.com/
It is a USB device that can be connected to take whatever data feed you desire, and use the "inscrutable dials", Ethereal Glowing Tube (EGT), and incandescent lamp on "The Device" to indicate whatever status you want. "The build is broken", "sales are up", or "there is a soccer game today" are only some of the neat automation possibilities that come to my mind without much effort.
The only process indicating apparatus you'll ever need, indeed! Too bad they are not yet for sale...I want one of these! It is the coolest thing I have seen since the "Ambient Orb".
http://www.ambientdevices.com/cat/orb/orborder.html
Thursday, October 12, 2006
Architect Is Not An Honorary Title
Knowing the business is great. In fact, knowing the business is essential. I hope that business experts abound within your organization. But "architecting" is not what these people are doing.
Here is a list of some of the knowledge, skills, or qualities that I think are required for anyone who wants to be called "architect". In no particular order:
- Speaks in terms of design patterns
- Uses open source/commercial off the shelf(COTS) software
- Can determine the least cost solution for a particular business need
- Desire for a career path in technology which is NOT management
- Knowledge of both current and next-generation technologies, so that development efforts can be synched up with other developers in the industry
- Knows how to avoid or get out of anti-patterns
- Embraces simplicity as a fundamental design principle
- Can embrace the good ideas of others
- Unafraid of change
Do you know who the architects are in your organization? Are these the qualities that they are known for?
Wednesday, October 11, 2006
Programming Zombies Will Crush You
This post on the "Creating Passionate Users" blog really sums up what makes a Dead Programmer, well....dead!
http://headrush.typepad.com/creating_passionate_users/2006/10/knocking_the_ex.html
A colleague of mine once quipped, "Why is it that at some point in most people's career, they suddenly decide that they now know everything and don't have anything else to learn?". That is around the point that they are past the point of no return converging on the "micro-manage every detail" side of the "Zombie Function" graph.
So what happens to these poor middle managers? Is it the tribal "showing the size of one's beak"? Or is it a kind of post traumatic stress disorder, brought on by being way into the "anxiety zone" (I will post about the state of flow soon).
Odd that the very factors like creativity and free-thinking that can most stimulate success, are the ones most likely to be blocked by management at many companies.
So what can a poor, Dead Programmer do about it? Speaking truth to power is both difficult and dangerous. If you really think you are ready to take on a monoculture within your organization, you had better prepare. Once you finally get your chance to pitch them, you will only get one chance. Writing down your ideas well in advance of this can refine them down to the size of information packet that management will be able to digest.
Focusing on new solutions instead of dwelling on the mistakes of the past is also a good idea. This is particularly true when speaking to the individuals who got the company into whatever mess it is in, in the first place. Be very diplomatic, but still take a firm stand ("tough love"?), and keep it far, far away from anything that can be taken personally.
Lastly, keep in mind that only benefits that will really impact the business are important enough to risk the firestorm that sometimes arises when confronting a zombie organization. When I say impact the business, I mean either bring in major new sources of revenue, or dramatically reduce the cost per running tested feature.
Small incremental improvements to the organization are best implemented from the bottom up in a "stealth mode" project. More about that next time...
Too Skilled For Programming?
http://perso.wanadoo.fr/lothar/datastore/TooSkilledForProgramming.pdf
In a world where taller people tend to more successful, I guess it makes sense. But logic has nothing to do with it! And things that lack logic offend the sensibilities of any self-respecting Dead Programmer...
Time To Reboot The Dead Programmer Society!
This site is dedicated to the Dead Programmers who dwell inside the corporate world. Not really so much dead, but slumbering.
Wake up, wake up... and carpe codex!