Monday, October 29
The new Ruby logo: Name those fonts?
I just saw the new Ruby logo (to the left of this text, in case you're reading this from an RSS feed) on Ruby Inside and I'm curious if anybody can identify those fonts?
Saturday, October 20
Coping with Doing Nothing
A little over a year ago, my little start-up was acquired by a big fish, and through a rapid and chaotic series of political implosions, I found myself running the entire technology department of the conglomerate... and I haven't written a single line of code since. I'm essentially a cat herder now. It's frustrating and depressing to spend my time approving personal time-off request forms, attending meetings about why we're always in meetings and how we can cut down on the number of meetings, dealing with the hiring and firing of a constantly fluctuating team, and watching projects, processes, techniques, and technologies that I spent nearly a decade developing and perfecting die by inches as I lose my grip on them. I miss the small company start-up atmosphere.
In the Kübler-Ross model of dealing with tragedy, I've recently graduated from bargaining to depression. But thanks to a book I'm currently reading, I'm quickly moving on to the acceptance phase. I've barely cracked the book and there's already been two key phrases that have stuck with me. The author describes managers as people who "organize and delegate." That's me to a tee right now. I'm too busy with the standard bureaucratic B. S. to get anything done myself, so I have to figure out who might be best suited to get it done for me, and pass the buck. The author also describes consultants as people who have "impact without control." Again, hitting the nail on the head regarding my situation. All I can do in my endless chain of meetings is tell people how I've seen it fail in the past, how it should be done going forward in order to succeed, then hope and pray that they take my words to heart.
Although the author tries to draw a distinction between a manager and an internal consultant, I think the line is pretty blurry, and depending on the day of the week or the attendees of a given meeting or the catastrophe du jour, I have to play both roles. It's hard being a team player when you're sitting on the bench for every game... but I guess that's what the coach does, right? Perhaps I just prefer to be the quarterback, taking the hits, getting my uniform dirty, and relishing the touchdowns from the end-zone rather than the sideline.
In the Kübler-Ross model of dealing with tragedy, I've recently graduated from bargaining to depression. But thanks to a book I'm currently reading, I'm quickly moving on to the acceptance phase. I've barely cracked the book and there's already been two key phrases that have stuck with me. The author describes managers as people who "organize and delegate." That's me to a tee right now. I'm too busy with the standard bureaucratic B. S. to get anything done myself, so I have to figure out who might be best suited to get it done for me, and pass the buck. The author also describes consultants as people who have "impact without control." Again, hitting the nail on the head regarding my situation. All I can do in my endless chain of meetings is tell people how I've seen it fail in the past, how it should be done going forward in order to succeed, then hope and pray that they take my words to heart.
Although the author tries to draw a distinction between a manager and an internal consultant, I think the line is pretty blurry, and depending on the day of the week or the attendees of a given meeting or the catastrophe du jour, I have to play both roles. It's hard being a team player when you're sitting on the bench for every game... but I guess that's what the coach does, right? Perhaps I just prefer to be the quarterback, taking the hits, getting my uniform dirty, and relishing the touchdowns from the end-zone rather than the sideline.
Saturday, October 13
Ruby Revelations: An Introspection
I was code-reviewing a cohort's contribution to adPickles the other day and I came across a line of Ruby that had me completely perplexed:
That's quite a mouthful. If you know what that does, go ahead and stop reading now, 'cause the rest of this post is just self flagellation.
I'm no Ruby expert, but this guy is (in case you couldn't discern that from this one line masterpiece). I had to ask him to break it down for me, and after a few back-and-forths I think I've got it. Here's the skinny, for those of you that are still reading and, like me, want to see the secret unravelled.
My gut instinct is to start from the deepest nested block and work my way out, but you first need a little nugget of context.
An Advertisement has several aspects, such as "new", "approved", "rejected", etc. Knowing that, now we can work from the bottom up.
Here he's referencing a variable named advertisements which we can safely assume is an Array of objects, each being an instance of Advertisement.
He wants to call a method on the array (which in turn calls the method on each contained element) but at runtime he doesn't know the name of the method, so he constructs it on the fly.
In Ruby, if you want the value of a variable to be evaluated in a string, you wrap the reference in #{}, so in this case if the value of aspect is "rejected", the method name being constructed from "count_#{aspect}" is going to be count_rejected.
So each member of the Array named advertisments is going to have the method count_rejected called.
And for each aspect, a two-element array is created, where the first element is the value of aspect and the second element is the results returned by the on-the-fly-generated-method-name.
For example, one of these arrays might look like ['rejected',23].
Each of these arrays are collected into an outer containing array by the map method, which will leave us with something like [['new',14],['live',51],['rejected',23]].
The the whole thing gets flattened into a one-level (flat) array, like ['new',14,'live',51,'rejected',23].
As my cohort explains the next part, "Hash doesn't take an array as an argument, so the asterisk breaks the array into multiple arguments. Hash[*[1,2,3,4]] is the same as Hash[1,2,3,4]."
Which bring us to the crusty outer shell, leaving us with a simple hash which looks like {'new' => 14,'live' => 51,'rejected' => 23}, where as you can see, each aspect is now mapped to its respective count.
Clear as mud? Excellent!
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]
That's quite a mouthful. If you know what that does, go ahead and stop reading now, 'cause the rest of this post is just self flagellation.
I'm no Ruby expert, but this guy is (in case you couldn't discern that from this one line masterpiece). I had to ask him to break it down for me, and after a few back-and-forths I think I've got it. Here's the skinny, for those of you that are still reading and, like me, want to see the secret unravelled.
My gut instinct is to start from the deepest nested block and work my way out, but you first need a little nugget of context.
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]
An Advertisement has several aspects, such as "new", "approved", "rejected", etc. Knowing that, now we can work from the bottom up.
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]
Here he's referencing a variable named advertisements which we can safely assume is an Array of objects, each being an instance of Advertisement.
He wants to call a method on the array (which in turn calls the method on each contained element) but at runtime he doesn't know the name of the method, so he constructs it on the fly.
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]
In Ruby, if you want the value of a variable to be evaluated in a string, you wrap the reference in #{}, so in this case if the value of aspect is "rejected", the method name being constructed from "count_#{aspect}" is going to be count_rejected.
So each member of the Array named advertisments is going to have the method count_rejected called.
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]
And for each aspect, a two-element array is created, where the first element is the value of aspect and the second element is the results returned by the on-the-fly-generated-method-name.
For example, one of these arrays might look like ['rejected',23].
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]
Each of these arrays are collected into an outer containing array by the map method, which will leave us with something like [['new',14],['live',51],['rejected',23]].
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]
The the whole thing gets flattened into a one-level (flat) array, like ['new',14,'live',51,'rejected',23].
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]
As my cohort explains the next part, "Hash doesn't take an array as an argument, so the asterisk breaks the array into multiple arguments. Hash[*[1,2,3,4]] is the same as Hash[1,2,3,4]."
Hash[*Advertisement.aspects.map {|aspect| [aspect,advertisements.send("count_#{aspect}")]}.flatten]
Which bring us to the crusty outer shell, leaving us with a simple hash which looks like {'new' => 14,'live' => 51,'rejected' => 23}, where as you can see, each aspect is now mapped to its respective count.
Clear as mud? Excellent!
Thursday, October 11
Clients don't always say what they mean
When I wrote my last post, I worried [rightfully so, it turns out] about readers reading too much into the example client requirements. My intent was to simply explain how to breakdown a chunk of verbiage into a checklist of manageable requirements. Unfortunately, I planted a few too many language bombs in my contrived example and a former colleague (Craig Walker) ran with it and posted a follow-up over on his blog.
Craig focuses on the first and last sentences of the example requirement verbiage. My intent was to use those sentences as examples of non-requirements, since they merely expressed opinion and speculation from the client. But, as Craig astutely pointed out, they are good warning flags that what the client expects doesn't match up with what the client requested. The client expects fewer mistyped passwords and better security, but the client requested changes that might not necessarily deliver those results. Craig outlines some guidelines for digging deeper and finding the root cause of the problem rather than taking the client's proposed solution for granted.
As a side note, if you're not subscribed to Craig's blog feed, I highly recommend it. He's one sharp guy. He was my right-hand man in the Java world for a great many years and he recently set off on his own to dabble with .NET, which he rants about quite frequently in his "Stupid .NET Tricks" posts.
Craig focuses on the first and last sentences of the example requirement verbiage. My intent was to use those sentences as examples of non-requirements, since they merely expressed opinion and speculation from the client. But, as Craig astutely pointed out, they are good warning flags that what the client expects doesn't match up with what the client requested. The client expects fewer mistyped passwords and better security, but the client requested changes that might not necessarily deliver those results. Craig outlines some guidelines for digging deeper and finding the root cause of the problem rather than taking the client's proposed solution for granted.
As a side note, if you're not subscribed to Craig's blog feed, I highly recommend it. He's one sharp guy. He was my right-hand man in the Java world for a great many years and he recently set off on his own to dabble with .NET, which he rants about quite frequently in his "Stupid .NET Tricks" posts.
Wednesday, October 10
Digestible Requirements
I commonly get "requirements" documents from clients that read like a college book report, just pages and paragraphs of disorganized stream-of-consciousness ramblings. When this happens, I use a little trick to restore some order to the chaos. I break it down into one long checklist of digestible tasks.
Why a big checklist? Several reasons:
Here's how I typically go about it. Let's use this as an example "requirement" from a client:
For the moment, set aside the issues of usability and obscurity over security; those topics are outside the scope of this discussion. The first thing I do is break the document into a checklist where every sentence is an item on the list. You can do this pretty quick and dirty with a simple regular expression find and replace of every period with a carriage return and a checkbox. This gives me something like:
Now you'll probably notice that the first and last items aren't really requirements, they're just an opinion and a speculation respectively. They can be removed, leaving us with:
Is the order important here? I don't think so. Let's get rid of that "first" and "second" nonsense:
Then a little clean-up of the grammar to make it more declarative:
Now comes a tricky issue. Whenever you see a conjunction like "and" or "or" or in this case the word "between" you're probably looking at multiple requirements in a single sentence, and you're better off breaking them up:
By breaking it up into two separate items, we can writer better isolated tests.
As a side note here, try to keep in mind that most people don't think "logically" like programmers. When they say "between eight and twelve" they don't consider the literal interpretation of "greater than eight and less than twelve." In most cases they probably mean inclusive of eight and twelve. Make sure you clarify with them before taking their words for granted.
Now, depending on the situation and the particular client, I will probably pass this list back to them, asking for them to review it and confirm that I have accurately interpreted their prose. And in some extreme cases, I'll ask them to sign it.
Once I've got my checklist, I'm golden. I can write a test case for every item on the checklist. I can use the list to gauge my progress with the project. And when everything on the list has been checked off (i.e. all the tests pass), I know I'm done.
Why a big checklist? Several reasons:
- It's easier to digest smaller tasks in comparison to big paragraphs of verbiage.
- It's easier to organize and group those smaller tasks.
- It translates directly into a test plan; every checkbox is a test that has to be written.
- It gives me a pretty decent status report and evidence of progress and I work through the list checking things off.
- Once everything is checked off, I know I'm finished. I know that I've satisfied the client's request.
Here's how I typically go about it. Let's use this as an example "requirement" from a client:
"We're tired of users mistyping their passwords and not being able to access their new accounts. First of all, we want to add a password confirmation field to the sign-up form. Secondly, we want to force them to pick a password between eight and twelve characters. This should make their accounts a little more secure."
For the moment, set aside the issues of usability and obscurity over security; those topics are outside the scope of this discussion. The first thing I do is break the document into a checklist where every sentence is an item on the list. You can do this pretty quick and dirty with a simple regular expression find and replace of every period with a carriage return and a checkbox. This gives me something like:
[_] We're tired of users mistyping their passwords and not being able to access their new accounts.
[_] First of all, we want to add a password confirmation field to the sign-up form.
[_] Secondly, we want to force them to pick a password between eight and twelve characters.
[_] This should make their accounts a little more secure.
Now you'll probably notice that the first and last items aren't really requirements, they're just an opinion and a speculation respectively. They can be removed, leaving us with:
[_] First of all, we want to add a password confirmation field to the sign-up form.
[_] Secondly, we want to force them to pick a password between eight and twelve characters.
Is the order important here? I don't think so. Let's get rid of that "first" and "second" nonsense:
[_] we want to add a password confirmation field to the sign-up form.
[_] we want to force them to pick a password between eight and twelve characters.
Then a little clean-up of the grammar to make it more declarative:
[_] add a password confirmation field to the sign-up form
[_] passwords must be between eight and twelve characters
Now comes a tricky issue. Whenever you see a conjunction like "and" or "or" or in this case the word "between" you're probably looking at multiple requirements in a single sentence, and you're better off breaking them up:
[_] add a password confirmation field to the sign-up form
[_] passwords must be more than eight characters
[_] passwords must be no more than twelve characters
By breaking it up into two separate items, we can writer better isolated tests.
As a side note here, try to keep in mind that most people don't think "logically" like programmers. When they say "between eight and twelve" they don't consider the literal interpretation of "greater than eight and less than twelve." In most cases they probably mean inclusive of eight and twelve. Make sure you clarify with them before taking their words for granted.
Now, depending on the situation and the particular client, I will probably pass this list back to them, asking for them to review it and confirm that I have accurately interpreted their prose. And in some extreme cases, I'll ask them to sign it.
Once I've got my checklist, I'm golden. I can write a test case for every item on the checklist. I can use the list to gauge my progress with the project. And when everything on the list has been checked off (i.e. all the tests pass), I know I'm done.
Monday, October 8
acts_as_conference (is Orlando the new Silicon Marsh?)
Ah ha ha, get it? Valley. Marsh. Oh I kill myself. Anyways...
I'm still coming down from the high that was BarCamp Orlando (you rock, Gregg) and I've just discovered that one of my favorite presenters (Robert Dempsey) is trying to organize a full-fledged Rails conference right here in my backyard! I'll take the three-hour drive over a ten-hour flight any day of the week. Rails For All (Dempsey's organization) just announced their call for proposals today. I'm praying he gets flooded with submissions.
I'm still coming down from the high that was BarCamp Orlando (you rock, Gregg) and I've just discovered that one of my favorite presenters (Robert Dempsey) is trying to organize a full-fledged Rails conference right here in my backyard! I'll take the three-hour drive over a ten-hour flight any day of the week. Rails For All (Dempsey's organization) just announced their call for proposals today. I'm praying he gets flooded with submissions.
Sunday, October 7
Ted Driven Development
Earlier this year, I was helping an old friend weed-out candidates for his latest Ruby on Rails venture, adPickles, and in the interest of cutting to the chase and letting the potential hires know what they were getting themselves in to, I sent them the following manifesto:
I just wanted to drop you a note to give you a little heads-up on the way they run development teams, so you can comment, inquire, or run screaming for the hills :-)
* TDD
First of all, they're a huge proponent of Test Driven Development (TDD). They aren't always faithful, but they enforce it as much as they can, and when you see the code that has been written so far, you'll see that there's a veritable butt-load of tests.
They haven't yet incorporated any sort of test coverage metric, but they do rely on autotest, which re-runs all of the appropriate tests every time they change a line of code. This tool is invaluable for catching all those "oops I didn't expect this to break that" moments.
If you commit code to the repository that fails a test, your humiliation will be merciless :-)
* DDT
Likewise they are adamant about Defect Driven Testing (DDT). Whenever the Big Boss Man shoots a developer an e-mail saying "hey I think I found a bug" the first thing the developer does is write a test to reproduce the issue. That test becomes a permanent part of the application test suite so that particular bug will never again sneak back into the application without unnoticed. They will expect you to do the same every time you are asked to fix or change something.
* Branching
If you don't know how to handle branching in Subversion, now is the time to figure it out :-)
Every time they take on a new developer, they insist that the new hire perform all of his development in branches rather than directly in trunk, until such time as they trust them to interact directly with trunk, or until the team grows so big that working directly with trunk is simply taboo and they all work in branches. This ensures the purity of the code base.
* Commits
One commit per change. If you have three items on your TODO list, you do one then commit, then another and commit, then the last and commit. You don't do all three at once and commit one big chunk of changes. Why? Code reviews...
* Code Reviews
They use Trac for code reviews. If you've every poked around the Rails core site you've seen this tool. The changeset feature gives them a nice purty color-coded "diff" of every change you made for each commit. This is why they insist on one change per commit. This allows them to review your work on a clean per-ticket basis. Your work will not be merged back into trunk until they have code reviewed and approved it.
* Referential Integrity
Just a side note here. I believe in foreign keys. If you've dug deep enough in my blog you'll see that DHH and I disagree on this issue, and if you saw/heard the keynotes from RailsConf last year [ED: referring to RailsConf 2006 in Chicago] you'll know that I'm not alone in my disagreement. This project has referential integrity in the database, which they achieve through a plug-in. You'll see this if/when you come on board. [ED: I later changed my stripes on the matter, during RailsConf 2007 in Portland as a matter of fact.]
* Deployment
They use Capistrano for deployment, and for now only the lead developer performs deployments.
When you finish up some work and it needs to be reviewed by the president, you'll let the lead developer know (through Trac). He will then code review it and (if he approves it) merge it into trunk. He will then deploy from trunk.
That's the basic gist of it. If you have any questions, comments, or concerns, please let me know. Thanks for your time.
Wednesday, October 3
Book Review: Secrets of Consulting
Secrets of Consulting is like Sun Tzu's The Art of War for consulting. Gerald Weinberg has organized the book into a rapid-fire succession of juicy tidbits of sage advice, packaging each in a quaint but often esoteric mnemonic, and reinforcing each nugget with a story. That's all good. The stories are relevant and they get the point across. However, he gives each anecdote a silly name (like "Prescott's Pickle Principle"), then uses that nomenclature when referring to them later on in the book. I found that annoying and confusing. Since I didn't memorize each goofy alias, I had to read the book with my pinky finger lodged in the glossary, so I could continuously flip back and forth to reference the pseudonyms. Thankfully, he lists them all out by name, alphabetically, right before the index at the back of the book.
It wasn't the "fix problem X with solution Y" book I had expected. It's much more of a "get to know yourself" and "be able to step outside yourself and observe your own actions without bias" sort of tome. The lessons learned from the book are basically methods for keeping an objective view on a situation, being able to see things from different angles, being cognizant of and compassionate to your client's personal and emotional investment in a project as well as your own, realizing when you're falling into ruts, discerning when you've become part of the problem rather than the solution, etc., ad infinitum. It wraps up with a few short and sweet chapters on pricing and trust.
In short, it's worth reading. You'll be a better consultant for having read it. You'll better deal with other consultants for having read it. I've heard there's a sequel, but I think my next endeavor is going to be the book Weinberg praises in his own writing: Flawless Consulting: A Guide to Getting Your Expertise Used.
It wasn't the "fix problem X with solution Y" book I had expected. It's much more of a "get to know yourself" and "be able to step outside yourself and observe your own actions without bias" sort of tome. The lessons learned from the book are basically methods for keeping an objective view on a situation, being able to see things from different angles, being cognizant of and compassionate to your client's personal and emotional investment in a project as well as your own, realizing when you're falling into ruts, discerning when you've become part of the problem rather than the solution, etc., ad infinitum. It wraps up with a few short and sweet chapters on pricing and trust.
In short, it's worth reading. You'll be a better consultant for having read it. You'll better deal with other consultants for having read it. I've heard there's a sequel, but I think my next endeavor is going to be the book Weinberg praises in his own writing: Flawless Consulting: A Guide to Getting Your Expertise Used.
Subscribe to:
Posts (Atom)