Category Archives: technology

The product guy’s shame

Sometimes I read sites like HackerNews and I feel like I die a little bit. A developer writes an uppity missive about [why he won’t be my technical guy](http://martingryner.com/no-i-wont-be-your-technical-co-founder/). A product guy [returns fire in kind](http://news.ycombinator.com/item?id=3952951). Neither of them have a clue.

I’m in the “product guy” community, so that’s who I’ll speak to. A few choice quotes from the product guy rant:

> YOU are easily replaceable. You’re just the coder who builds the thing. Once MVP is launched and the company raises funding, you could easily be replaced because I guarantee you there are thousands of engineers just as good as you.

If your “coders” are replaceable cogs, I dispute that your product is worth a shit. A lot of what defines a *good* developer is their ability to grok a business idea and bring meaningful contribution to the product *as it is developed*. With every keystroke, a developer has the option to make the product better or worse.

> The product guy, on the other hand, is the DNA of the company. He forms the vision, culture, management. You can’t replace that.

You’re right. Good product guys have a vision of the product that steers the ship. You can’t build a great product on iteration alone. You need an overall vision. This, however, does not automatically make every other person in the organization meaningless drones.

The best products result when the visionary understands how to communicate their ideas to the development team, who internalizes these ideas and uses them to guide their efforts at every moment. The happiest moments in my day come when a developer pushes a commit/feature that fits exactly with my vision, but isn’t an explicit result of some directive I gave. These super-developers can infer good ideas from my product direction. I don’t treat them like drones and, what do you know, they don’t act like it.

I imagine that in this product guy’s view, developers are just there to do his bidding, but this marginalizes the developer, forcing them to perform the equivalent of ditch digging. Programming isn’t easy. Those that can do it are generally smart people. Do you think the best developers want to work for someone who wishes to marginalize them in this way?

Because I share my vision with developers, and empower them to guide the product, I cannot simply discard them. If I do, I throw away a significant investment as well as an asset. They need me, I need them, and the product is better as a result.

Walk a mile in Egor’s shoes

This was a rough weekend for [Egor Homakov](http://homakov.blogspot.com/). Right now, some of you are questioning my framing of this weekend’s events as rough for *Egor* and not Github. After all, he went and violated Github’s ToS. There’s a tone of arrogance that rings through in his posts, but I suspect a portion of this can be attributed to his broken English. Recognition may be what motivated him to take the path he did (public disclosure), but I don’t mean to judge him. I can identify with Egor to some degree, because I’ve worn his shoes for about a year now.

Some time late last year, while working on a systems automation project, I discovered a DoS vulnerability in a fairly common piece of voice network hardware. This particular hardware is commonly used in telecommunications to provide voice interconnets between VoIP networks and legacy systems. There are thousands (maybe tens of thousands) sitting on public IP addresses. This particular vulnerability causes the gear in question to crash spectactularly, invoking a core dump and cold boot. The really nasty part is that the “exploit” doesn’t require any special skill. I’d say there’s a fairly high likelihood that someone else could stumble upon this, just like I did, provided they were using the same toolchain. Maybe they have.

I disclosed the vulnerability to the vendor in March of last year (2011). Last time I tested, the issue remains. I disclosed through a third-party that has a VAR relationship with the manufacturer, so I don’t have any direct insight in to their handling of the issue. There’s a small part of me that desires to make an example out of one, or the both, of them.

It makes me pretty angry. I have some idea of how many of these devices are out in the wild. They’re in use by some very large companies. Exploiting the vulnerability causes the device to become unavailable for 3-5 minutes at a time. An attacker would only need a handful of hosts to keep a device offline for a long while, even if the device operator actively blocked attacking hosts. Mitigating the attack would take some pretty significant retooling of the network configuration because of the service that is exploited. This is telecommunications hardware. Imagine taking down a large company’s entire call center for a couple of hours. How big of a check do you think they’d write to make you go away?

So here I sit, knowing that this problem exists, but worrying public disclosure could make me an outsider really quickly. Maybe Egor is just young and naive. Maybe he has guts. I don’t know him, so I can’t say, but I can tell you that knowing about a major security exploit is an uncomfortable position to be in. This is doubly so when you are ignored after disclosing it.

Before you judge him for the way he handled the situation, take a step back and make sure you understand which side of the table you’re sitting on.

Windows Phone is failing because the seeds were planted elsewhere

Update: Excellent discussion over at [HackerNews](http://news.ycombinator.com/item?id=3422810).

Toronto Standard has an [interesting analysis of why Windows Phone is failing](http://www.torontostandard.com/business/navneet-alang-windows-phone-is-failing-because-its-great/). Spoiler alert: they say it’s failing because it’s too good. And by “too good” they mean “too constrained”. It’s an extended analogy; a cross-pollination of attribution stemming from the notion that Apple’s iOS devices are good because Apple exerts tight control over the user experience. Not a bad point, but I think there’s more to it.

Alternate title: Why I don’t have a Windows phone. My account is, of course, a single point of data, but I do fit some stereotypes pretty well, so take this for what it’s worth. Apple users, bear with me. I’m going to give up a lot of ground here so that, hopefully, Windows users will hear me out.

In recent years, Apple’s desktop market share has grown at the expense of Microsoft’s. Microsoft is not at any immediate risk of losing the desktop market to Apple, but the trend is evidence of some underlying consumer currents.

While any particular individual may not own an Apple desktop, there’s a much better chance that they know someone who does than, say, 10 years ago. Go to a coffee shop today, and there’s a very good chance that you’ll see someone toting an Apple laptop. More consumers have seen Apple devices in the wild, and have been — for better or worse — exposed to the typical Apple user. This presence, along with the continued refinement of their product and excellent marketing, has created a brand image that reflects quality, refinement, and yes, a little snobbery. I’ll stop here, because I can hear you lurching over your trash can.

What does this have to do with Windows Phone? Apple’s brand direction has stripped away (from Windows) a specific type of consumer. Consumers seeking a (at least perceived) high-end product that exhibits quality and refinement bought an iPhone. Stop and consider that for a moment. This isn’t a claim that iPhone is better; it’s a claim that Apple has cultivated an image of quality and refinement. Regardless of what you think of Apple’s products, consider what Apple fans think of the brand. More moaning; time to move on!

I used to be a die hard Windows user. I don’t “hate” Windows like some Mac users. I dislike using Windows in the way that someone who perfers Martin guitars might dislike playing a Taylor. I’ve grown accustomed to using a Mac, and I don’t like it when I can’t use the tools I prefer. So when it came time to buy a phone, I bought an iPhone. The seeds were planted when I bought a used eMac back in 2004. My roots are now deep, and I can’t see myself going back.

The problem for Microsoft is that they built the kind of product that I would probably buy. Consumers like me would also probably buy it, but there aren’t *that* many of us. There are millions of iPhone buyers, but we’re not all the same. Some people buy an iPhone because they have a Mac. Some people buy an iPhone because they want to reflect the image that Apple portrays. Some people buy an iPhone because their “friend who knows about computers” told them too. Some people buy an iPhone because they tried it at the store and it really does work just like they show in the commercials. **Microsoft built the phone that we would probably like, but we already bought in to something else, and we’re not unhappy.**

The other, and very large, group of Windows users are the ones that reject Apple’s platform because of the rules that come with it. Some people don’t buy the image that Apple is selling. Some people don’t like the fact that they can’t install their own software, gain access to the source code, change the color of the menu bar, load another web browser… you name it! Those people are buying Android phones in droves. Microsoft has built the phone that this group of people don’t want.

So, one way of putting it would be that Windows Phone is “too good”. I don’t really see it like that. This concedes that the iPhone is the best phone for everyone. I think the reality for Microsoft is even worse than that.

It takes a lot to unsettle consumers, but Microsoft’s jarring offenses are pretty well known. Vista was a train wreck, and Windows Mobile was a non-starter. Couple this with some great timing on Apple’s part, as well as a quality product that appeals very strongly to a specific market segment, and you’ve got a perfect storm on your hands. “Apple consumers” exited Microsoft’s product stream a couple of years ago. On the other flank, Android is delivering a product that goes above and beyond the flexibility that tinkerers and customizers expect, which strikes at Windows’ core demographic. **In between Apple’s loyal users, and Android’s flexibility-minded consumer, there’s very little room for Windows Phone.** The most probable Windows Phone users planted their seeds elsewhere.

How to: Successfully resolve XBOX Live error code 80169D94

Like a lot of proper geeks, I received a video game for Christmas. Battlefield 3 to be specific. Because I have a long history with the Battlefield series, many of my XBL friends also play this game, but have purchased map packs (of course), which means I pretty much have to purchase the map packs.

The day after Christmas I fired up my XBox and hopped online. I tried to purchase the Back to Karkand map, only be be met with an error and status code:

> Can’t retrieve information from Xbox LIVE.
> Please try again later.
>
> Status code: 80169D94

Oh boy, “Try again later.” *Riiiiiight…*

I was patient, figuring that maybe XBL was overloaded because of the holiday. I tried again this morning before 9 AM when load would surely be less. Same thing. I even went to Best Buy yesterday and purchased a 1600 point XBL card in an attempt to work around the problem; thinking maybe it was credit card related. No dice.

Some Google-ing turned up a great article by [Brad Feld][1]. The article revealed a couple of important facts: 1) this code means your account is on some sort of billing lock, and 2) that XBL support would likely fail to resolve the issue on the first shot. What follows is how I managed to get resolution in about 30 minutes.

### Before calling XBL support

* Make sure you have your live.com login and password handy (I use [1Password][2], yay!)
* Log in to [http://billing.microsoft.com](http://billing.microsoft.com) and update/verify all your payment information
* Is your billing address correct?
* Is the card on file expired?
* Have you ever had to dispute any charges from XBL?
* Fire up your XBox and:
* Press the Guide/Dashboard button in the center of the controller
* Go to Settings, Account Management
* Move right one panel to “Your Memberships” and select your subscription
* Select “Change Payment Options” and verify that the card in use is active and all details are correct
* Jot down some notes about your issue
* The exact message and status code you received
* Any steps you’ve taken to attempt self-resolution (like this list here!)
* If you’re redeeming an XBL card, write the code out using the NATO [Phonetic alphabet](http://en.wikipedia.org/wiki/NATO_phonetic_alphabet) (handy bash script [here](https://gist.github.com/1523933) for nerds among you); you’ll need to repeatedly provide this number to the support rep

### Calling XBL support

When I called XBL support, Hector (my rep) first insisted that my account was not locked. He attempted to add the points to my account three or four times while I read him the code from the card (ugh that was painful). Hector’s diagnosis was that the store had not properly activated the card and that I should go back to Best Buy. Having watched the clerck activate the card, I wasn’t prepared to let him off the phone, so I pressed further.

In my case, I had resolved a dispute with XBL in August of last year regarding some unauthorized purchases. I explained this to the rep, along with the steps I had already taken, and pressed him to look in to my error message further. I reiterated that he might need the status code I received, which he made note of, and placed me on hold while he did his support joo-joo.

When Hector returned, he acknowledged that the acount was “locked” afterall, and that, if I could verify all my billing information was correct, he could unlock it. I had already updated my info, so I gave him the green light to unlock. Bingo! The card worked immediately.

### What might be different about your situation

It seems to me that this status code is probably related to billing lock for reasons related to fraudulent activity. It might not be constrained to fraudulent activity, but I found plenty of other status codes covering other reasons. This appears to be one of the few status codes not covered by the XBL support website, and when searching, I didn’t see any results returned for xbox.com. It’s almost as if Microsoft purposefully avoids keeping information related to this code out in the open.

I suspect I was able to get a resolution quickly because I supplied the dates of my previous support incident. The rep was able to look up my resolution and unlock the account on his own because my account had already been “released” by the fraud department. The delay that Brad Feld experienced likely relates to the tiered structure of XBL support. Fraud is handled by an entirely different department, which in my experience, is staffed by much more competent staff. The problem is that they’re almost always backlogged.

All of the above is speculation on my part, but a large part of what I do is troubleshooting large organizations. I hack people as much as I do technology, and I have a pretty good track record of building an accurate internal picture of how a company works without actually stepping inside.

Hopefully this write up augments the other information you’ll find when searching for information on 80169D94.

[1]: http://www.feld.com/wp/archives/2008/12/the-unbearable-frustration-of-error-code-80169d94.html
[2]: https://agilebits.com/onepassword

Rails: rails vs ./script/rails

Long time Rails developer Jesse Storimer (of Shopify) recently blogged about the efficiency of the new (since 3.0) `rails` command versus using plain old `./script/rails`. You can find his piece here: [The rails command and exec(2)](http://jstorimer.com/2011/12/20/rails-and-exec.html). You should definitely read that before moving on.

So Jesse’s premise is that you should consider using `./script/rails` instead of the `rails` command because of the overhead involved with the rails script and exec. When I read this, I was skeptical. Just how much overhead can there be in a simple wrapper like the rails script? Why ask rhetorical questions when we can know for sure!?

I performed this testing in our staging environment:

* Linode 512 instance running Debian 6.0
* Rails 3.1.1
* Ruby 1.9.3-p0
* Bundler 1.0.21
* Gems in bundle: 68

Since this is a capistrano deployed environment, we rely on bundler, so I wasn’t able to test the bare `rails` wrapper. Maybe another time.

I ran two sets of tests. First, I figured I’d run a test against `rails runner` because it wasn’t immediately obvious to me how I’d execute `rails console` using the GNU `time` utility. Once the console is open, the REPL interface would sit there waiting on me and be included in the `time` results. Easy fix. I simply added a call to `exit` to my ~/.irbrc file causing irb to execute immediately and reliably without waiting on a slow human. Once I figured out how I’d test the console, I wired up my tests.

TL;DR – Avoiding the wrapper `bundle exec rails` saves you about 1 second for every execution of `console` or `runner`. Meh. Although, a full second can seem much longer when you’re reloading your console 5 times in a row because of some stupid error you keep making.

Read on to see the methodology and analysis.

**Files**

Exec: count gems (no file, just executed at a prompt)

# Be sure to subtract 1 for the header line!
bundle show | wc -l

File: test_script

puts “Runners gon’ run!”

File: ~/.irbrc

# Be sure to remove this later or you’ll be reeeeeally confused
exit

File: bench_runner.sh

#!/usr/bin/env bash

echo “Runner tests: 4 times each”
echo
echo “cmd: bundle exec rails runner -e staging test_script”
time bundle exec rails runner -e staging test_script
time bundle exec rails runner -e staging test_script
time bundle exec rails runner -e staging test_script
time bundle exec rails runner -e staging test_script
echo
echo “cmd: ./script/rails runner -e staging test_script”
time ./script/rails runner -e staging test_script
time ./script/rails runner -e staging test_script
time ./script/rails runner -e staging test_script
time ./script/rails runner -e staging test_script

File: bench_console.sh

#!/usr/bin/env bash

echo “Console tests: 4 times each”
echo
echo “cmd: bundle exec rails console staging”
time bundle exec rails console staging
time bundle exec rails console staging
time bundle exec rails console staging
time bundle exec rails console staging
echo
echo “cmd: ./script/rails console staging”
time ./script/rails console staging
time ./script/rails console staging
time ./script/rails console staging
time ./script/rails console staging

**Test Results**

Let’s have a look at the results for `runner`:

Runner tests: 4 times each

cmd: bundle exec rails runner -e staging test_script
Runners gon’ run!

real 0m9.949s
user 0m9.190s
sys 0m0.715s
Runners gon’ run!

real 0m9.519s
user 0m8.753s
sys 0m0.724s
Runners gon’ run!

real 0m9.652s
user 0m8.898s
sys 0m0.712s
Runners gon’ run!

real 0m9.898s
user 0m9.105s
sys 0m0.747s

cmd: ./script/rails runner -e staging test_script
Runners gon’ run!

real 0m8.765s
user 0m8.101s
sys 0m0.621s
Runners gon’ run!

real 0m8.417s
user 0m7.731s
sys 0m0.642s
Runners gon’ run!

real 0m8.991s
user 0m8.290s
sys 0m0.657s
Runners gon’ run!

real 0m8.530s
user 0m7.828s
sys 0m0.658s

And now `console`:

Console tests: 4 times each

cmd: bundle exec rails console staging
Loading staging environment (Rails 3.1.1)

real 0m9.712s
user 0m8.885s
sys 0m0.782s
Loading staging environment (Rails 3.1.1)

real 0m9.725s
user 0m8.905s
sys 0m0.774s
Loading staging environment (Rails 3.1.1)

real 0m9.661s
user 0m8.861s
sys 0m0.754s
Loading staging environment (Rails 3.1.1)

real 0m9.742s
user 0m9.024s
sys 0m0.675s

cmd: ./script/rails console staging
Loading staging environment (Rails 3.1.1)

real 0m8.617s
user 0m7.978s
sys 0m0.596s
Loading staging environment (Rails 3.1.1)

real 0m9.168s
user 0m8.385s
sys 0m0.738s
Loading staging environment (Rails 3.1.1)

real 0m8.589s
user 0m7.868s
sys 0m0.677s
Loading staging environment (Rails 3.1.1)

real 0m8.573s
user 0m7.846s
sys 0m0.684s

**Analysis**

Time to do the math. Since we’re interested in the time we have to wait before we can do more work, we’ll look at the real field results (using Ruby, of course).

Runner:

# average times for bundle exec rails runner
([9.949, 9.519, 9.652, 9.898].reduce(:+).to_d / 4).to_s

# average times for ./script/rails runner
([8.765, 8.417, 8.991, 8.530].reduce(:+).to_d / 4).to_s

Using `bundle exec rails runner`: 9.7545s avg
Using `./script/rails runner`: 8.67575s avg

Result: avoiding `bundle exec rails` for `runner` saves you an average of 1.07875 seconds every time you execute runner.

Console:

# average times for bundle exec rails console
([9.712, 9.725, 9.661, 9.742].reduce(:+).to_d / 4).to_s

# average times for ./script/rails console
([8.617, 9.168, 8.589, 8.573].reduce(:+).to_d / 4).to_s

Using `bundle exec rails console`: 9.71s avg
Using `./script/rails console`: 8.73675s avg

Result: avoiding `bundle exec rails` for `console` saves you an average of 0.97325 seconds every time you boot a console.

Bringing balance to the force: CarrierIQ

There’s a severe lack of balance in this whole conversation surrounding CarrierIQ. The fact that CarrierIQ logs keystrokes makes the whole issue so terrifyingly intrusive that it’s difficult to look at the broad picture objectively.

Working in telecom isn’t much different than working in application development. Consider this common scenario:

> A user’s application crashes. They immediately call your customer service and spew vitriol, claiming the application has crashed ten times in the last week and lost all their data. ZOMG!

> Because you’re a seasoned — albeit somewhat cynical — developer, you include a crash reporter component that sends you detailed application usage and crash information. You pull up the customer’s records and see the application has only crashed three times in the two years the customer has owned it. The log of their data file size shows the file size has only ever gone up, and the report of data file integrity that runs every time the app boots reports no issues reading the file. Ever.

Wow, this must be the worst customer ever, right? And what’s up with this developer spying on his users. What a sociopath, right? Oh, I see he has got SpyMeSat Mobile App to notify him when an imaging satellite could be taking his picture. Of course he would say he just needs new satellite images, but the real reason is evident.

Probably not. This is more typical than we’d like to admit, but what drives users to such hyperbole when reporting issues? Tech support practices teach users this behavior. In order to understand how, you must understand a few things.

First is that, to your customer, the technology is a “black box”. To quote Arthur C. Clark: “Any sufficiently advanced technology is indistinguishable from magic.” When a customer calls a support line, there is a good chance the CSR is going to have them go through some basic steps they’ve already tried. Unfortunately, you can’t just take the customer’s word for it, because customers are liars…

Woops. See what just happened there? That’s the second thing you need to understand: common tech support methodologies tell us to distrust what the user says.

Thus begins the cycle of distrust. A certain percentage of customers will lie to save time. Another set will lie because they *think* they know what’s causing the problem, but they lack the depth of subject matter knowledge to even understand why they’re wrong. The technology is magic to them, so “restart the application/device” might as well be “say hocus-pocus three times.”

This issue runs even deeper because many customers really *do not* want to call your support line. They really don’t. Who wants to feel distrusted? They learn from every support experience, and will often take the basic troubleshooting steps themselves. They’ll tell you this, but as we know, a certain percentage will lie, and you have no way of knowing who this percentage is, therefore we must treat all customers as liars.

You spin me right round
baby right round
like a record baby…

*Ah-hem…* Sorry.

In a tech support conversation, the customer very quickly feels distrusted, and as we know from some rather infamous psychological experiments [1], people who feel distrusted will act in a way worthy of said feeling. Because of this deep rooted, dysfunctional relationship with our customers, we develop solutions that circumvent the issue entirely and gather the data directly. Pay no mind to the man behind the curtain, and all that. You see, the customer relationship problems are the same in telecom as they are in application development; web or otherwise.

This begs several questions:

Why ask them if they recently rebooted the device when the technology can tell us so accurately?

Why ask a customer how many dropped calls they experienced if we can simply look at a log?

Why not have a look at where the user was when they reported poor call quality, so we can correlate it to our tower location database?

Why trust that a particular setting is configured correctly when we can inspect the condition of the device?

*Why rely on a user’s assertion that they typed the URL correctly when we can just look at their keystrokes.*

Whoa, hold on a minute.

Let me back up a moment and be clear about something. I am not advocating that the data collection performed by CarrierIQ is “OK”. It’s also not entirely clear whether carriers can actually see your keystrokes, but they are logged on some devices. I am playing devil’s advocate here. I hope the scenario I’ve presented is identifiable to you. The technical groups at the carriers want you to have a positive experience, and this drives them to collect data.

I know more than a few people who work in technical departments at AT&T. They don’t live in a mountain-side complex plotting schemes for world domination. They really don’t. They’re normal people like you and I, and they care that people think their service sucks. Like we’ve all experienced, management doesn’t always give them the resources they need to fix the root cause. As is typical in service-related enterprise, they focus on *fulfilling* failure demand [2], rather than restructure their organization to reduce it.

This (excessive failure demand) is what drives the market for tools like CarrierIQ. I would be very surprised if the genesis of CarrierIQ was the marketing department, but the conundrum we face is that data collected for troubleshooting is like a trifecta of meth-heroin-cocain to marketers. The same data you’d need to build a robust support mechanism where the user does zero troubleshooting could be used to lead thousands of marketers right off a cliff. It’s too powerful an attraction. No firewall can withstand the gravity of “the bottom line”.

So take a step back for a moment and re-evaluate the CarrierIQ situation. Should there be more transparency? Yes, definitely, but let’s not turn this in to Salem 1692. These tools are incredibly valuable for carriers from a tech support perspective. They can’t go away entirely, but we do need better transparency and regulation of how the data is used.

Comments welcome on the [Hacker News item](http://news.ycombinator.com/item?id=329997).

1 – I’m referring to the [Stanford Prison Experiment](http://www.prisonexp.org/) conducted by Philip G. Zimbardo in 1971.

2 – [Failure demand](http://leanandkanban.wordpress.com/2009/05/24/failure-demand/): A work product that does not meet the customers needs and generates additional work. It is opposed to value demand, which is the customer wanting something new.

What exactly is “hacking” photography, anyway?

Andrew Cholakian says [you can’t hack photography](http://blog.andrewvc.com/you-cant-hack-photography) in response to Peter Norvig’s [article dedicating significant amounts of text to the technical aspects of creating photographic art](http://norvig.com/dance-photography.html?). You’ll noticed that I used the word “create” in my previous statement, which will stir up its own sub-controversy. There are a group of people who feel that photography it not art, and is definitely not creation, because you’re only capturing something, not creating it. This is where both that group of people, and Andrew, are wrong.

Photography is art. A great photographer sees a scene and very quickly develops a strategy to capture the essence of that scene. Think of the many times you’ve seen beauty with your eyes. How did you feel? Capturing that moment means capturing the feeling. The photographic image is a vehicle for all the emotions you felt while being there.

When technical individuals, like Peter Norvig, enumerate the mechanics of photography, they’re teaching the tools used to create. Leonardo Di Vinci created some of his greatest works by understanding not only the appearance of his subjects, but the way their bodies worked. This allowed him to *create*. He could see a man or woman in any natural pose because he understood how the body worked. He also understood the mechanics of his tools. Di Vinci knew when to use a pencil and when to use a brush. He knew exactly how to stroke the canvas in order to create his visions.

Many non-technical photographers cannot relate to mechanics because they learned through a different method. Some individuals are intuitive learners. They learn by trial and error: “When I do this, I get that.” “That” is never a technical concept, it’s a result. The next time they find themselves in need of “that”, they replicate the steps. They may not even think of it in such explicit detail. They just do it. It’s an intuitive process, not a list of steps or parameters.

Andrew claims you can’t hack photography, but I can’t find where that claim was supported. I’m not even sure what it means? Does this mean we can’t use the technical aspects of photography to create moving photographs? I’d certainly disagree with that. Composition, exposure, and depth-of-field are the photographer’s palette. If you don’t know how to conjure these elements on demand, then you’ll miss lots of opportunities to create a great photograph. How you learn them is up to the individual.

So what’s the appropriate rant? Many technical learners never get past the mechanics. Every photo remains an algorithm to be perfected. Their photographic output is largely without emotion because they’re not trying to create; they’re simply trying to replicate the scene.

A more appropriate rant, I believe, would be to encourage technical learners to apply their new-found skills in broader ways. Intuitive individuals “feel” the scene more naturally, but often struggle with the mechanics of their device resulting in lost opportunities. Technical individuals learn their device, but find it more difficult to identify the “feel” of the scene, resulting in lost opportunities. As it turns out, the two have a lot to learn from each other.

*Note:* I don’t mean this as an attack on Andrew or any other author. I think he’s got some things wrong, but I prefer to use direct language when I write, rather than litter the material with qualifications to avoid offense. I have a tremendous amount of respect for anyone who takes their time to write about their views. Otherwise, there would be no discussion.

Print the active Pow config in a readable format

[Pow](http://pow.cx/) is great. I don’t know why it’s taken me so long to make the switch from `rails s`. Pow is short on management utilities though; probably for good reason. Ideally, you never have to touch your Pow instance. It ‘just works’. I like to dig around under the hood though, so I put together this quick Python script that outputs the running Pow config in a format I can read.

#!/usr/bin/env python

# filename: powconfig

#############################################################################
# Print POW config with nice formatting #####################################
#
# Works as is with Python 2.6. For Python 2.7, ‘import simplejson as json’
# instead. Save this file to a location in your PATH and make sure it is
# executable. Then you can call ‘powconfig’ from anywhere and see the active
# POW configuration.
#############################################################################

import sys
import json as json
import httplib as http

conn = http.HTTPConnection(“localhost”)
conn.request(“GET”, “/config.json”, ”, {“Host”:”pow”})
resp = conn.getresponse()
data = resp.read()
print json.dumps(json.loads(data), indent=4)
sys.exit(0)

Save this script somewhere in your path and you can run `powconfig` from anywhere to see your running config, which looks like this:

bradland@macbookpro:~$ powconfig
{
“bin”: “/Users/bradland/Library/Application Support/Pow/Versions/0.3.2/bin/pow”,
“rvmPath”: “/Users/bradland/.rvm/scripts/rvm”,
“workers”: 2,
“dnsPort”: 20560,
“httpPort”: 20559,
“logRoot”: “/Users/bradland/Library/Logs/Pow”,
“timeout”: 900,
“domains”: [
“dev”
],
“dstPort”: 80,
“hostRoot”: “/Users/bradland/Library/Application Support/Pow/Hosts”,
“extDomains”: []
}

If you don’t care about formatting, you can simply add this snippet as a bash alias.

alias powconfig=’curl -H host:pow localhost/config.json’