tag:blogger.com,1999:blog-50038070277242896402024-03-15T18:12:10.970-07:00D. Patrick Caldwell on Software Engineeringanother programmer trying to enhance his craft through information sharing and socializationD. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.comBlogger138125tag:blogger.com,1999:blog-5003807027724289640.post-28706949442710622382020-10-27T12:13:00.001-07:002020-10-27T12:24:51.762-07:00Autopilot Against Drug Testing<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhoDiz37mARKq2Flhj9h4nU3lCROzAUP-q4EB63be49t-ziGtjCRGGDavpCJO5KLrCUmZ0ZPIlWlgWce0WR3Rku6LZnw89mF-TAvcEbndsMxbHvRbmd0fYiXeDNE4RpvMv2-YUqmAkp0cQ/s320/" alt="Drug Testing Lab" style="float: left; margin-right: 7px;" class="postimage" />
<p>
Autopilot Consulting, LLC. can <em>NOT</em> support mandatory drug testing. Autopilot was founded on the belief that a business could be
both successful and ethical. I believe that mandatory drug testing is unethical and therefore we can not support it.
</p>
<p>
<a href="https://autopilotconsulting.com/about.html">Autopilot's core values</a> are:
<br>
Integrity above all else.
<br>
People matter
<br>
It can always be better
<br>
If it's complicated, it's too complicated
</p>
<p>
It's exactly these core values that have led me to the decision that as a policy Autopilot would not permit staff to submit to drug testing as a
condition of working with Autopilot Consulting. Drug testing provides very little value to employers and can only harm employees and staff.
</p>
<h1>Drug Testing For Employers</h1>
<p>
Drug testing provides very little value to employers. Current drug tests typically address only a small subset of abused drugs, are not particularly
reliable, and are widely regarded as poor predictors of on-the-job performance.
</p>
<h2>Prescription Drugs</h2>
<p>
Pre-employment drug screening often excludes legal drugs including prescription medications (when they do include prescription medications, you
begin to approach the privacy concerns mentioned below). The Americans with Disabilities Act and the Equal Employment Opportunity Commission have
strong guidelines to protect the privacy of those legally taking prescription medications.
</p>
<p>
These prescription medications are often abused and contribute to workplace safety concerns that are not readily addressed by common drug screening.
Even when these medications are tested for, studies have found that the screening typically does not improve safety ratings.
</p>
<h2>Alcohol</h2>
<p>
Many pre-employment drug screens do test for alcohol. While these would provide valuable information in an after-incident exam, they're not
particularly good at identifying people who abuse alcohol. Alcohol is detectible in urine for up to 12 hours. There are better tests for when
alcohol consumption is completely prohibited, but in the typical case, if someone who is suffering with alcohol addiction can sober up for
an interview, they can also sober up for a negative drug test too.
</p>
<p>
For perspective, a study in 2003 showed that people who consume alcohol on average three or more times per week had 3.2 injuries per 10,000
person-work-days vs 1.9 injuries over the same duration for non-drinkers. This 70% increase in risk is typically not predicted by pre-employment
drug screens and many people don't consider this level of alcohol consumption to be heavy use.
</p>
<h2>Detection Times</h2>
<p>
Most drugs are detectible for only a few days. Marijuana can be detectible for 1 to 2 months for chronic users but is typically less than 7 days
for infrequent uses. This means that drug users (except for some marijuana users) who are looking for jobs can easily pass a pre-employment drug
screening by abstaining for only a few days.
</p>
<p>
Exacerbating this problem, drug tests have a 10 to 15 percent false negative rate. Some false positives are due to weaknesses in the tests,
differences between the way different people metabolize drugs, volumes of water people typically consume, etc. Others are deliberate
subversions of the test. While false-negatives probably have a less dramatic impact than false positives, this does greatly reduce the value
of the tests in general.
</p>
<h2>Impacts of Drug Testing on Job Performance and Safety</h2>
<p>
Some studies have been able to show a relationship between drug use and job performance and safety. Many other studies fail to show any relationship.
Most studies have failed to show a correlation between drug screening and job performance and safety with several researchers suggesting alternatives
for safety critical positions like computer-assisted performance tests and non-punitive employee assistance programs.
</p>
<p>
The inability to test for several classes of drug abuse, the unwillingness to treat alcohol abuse with the same vigor other drugs, and the
unreliability of drug screens as a predictor of performance greatly reduce the value of drug tests as a pre-screening employment qualification.
</p>
<h1>Drug Testing for Employees</h1>
<p>
Drug testing can only serve to harm potential employees. Drug tests have an unacceptably high false-positive rate, are privacy-invasive, and have
been historically biased against poor and minorities.
</p>
<h2>Harmful False-Positives</h2>
<p>
While drug tests are generally unreliable as a measure of drug use in general, the false-positive rate is an alarmingly high 5 to 10 percent. That
means that if I have 10 employees undergo a drug screening as a condition of a contract with an Autopilot client, one of them will likely with a
documented positive drug test. It would be unethical to require my staff to subject themselves to a test with a 10% chance of harming them or
their reputation.
</p>
<h2>Privacy</h2>
<p>
I have worked with and would like to continue working with people who live in (or travel to) states (or countries) where some drugs are legal.
Because these drugs are also controversial, it is reasonable for someone to prefer to keep their legal activities private. If I permit some
employees to submit to drug tests, I cannot protect the privacy of all of my employees.
</p>
<p>
For example, I could allow some employees an opportunity to opt out of drug tests. The problem is that through deduction, it's possible people
will come to the conclusion (correctly or incorrectly) that someone is opting out of contract opportunities to hide drug use. It's less complicated
and considerably more respectful of the privacy of my staff (and thus more ethical) to not permit any employees to submit to drug tests in the first
place.
</p>
<h2>Disproportionate Impact on Poor and Minorities</h2>
<p>
One study, conducted by Yale University, found that 63% of black workers were employed in a workplace that performs drug testing while only 46% of
white workers were. Further, 54% of blue collar employees were tested compared to only 44% of white collar workers. In further analysis, the study
found that the racial discrepancy was true among executive, administrative, managerial, and financial workers as well as technician and support
occupations.
</p>
<p>
Counterintuitively, the National Bureau of Economic Research found that places where drug testing is required tend to have more racial diversity. The
author of the study suggests that these differences are due to biases in the hiring process because drug tests gives minority applicants an opportunity
to prove counter assumptions and prove their innocence.
</p>
<em>This is a bias that should be eliminated by ethical thinking rather than by putting a burden of proof on job applicants.</em>
<p>
The same author has found that drug testing policies tend to correlate with a decline in employment for women as well.
</p>
<h1>TL;DR</h1>
<p>
Drug testing provides very little value to employers because these tests typically address only a small subset of abused drugs, they are not
reliable, and are widely regarded as poor predictors of on-the-job performance. Drug testing has no benefit to potential employees because the tests
have an unacceptably high false-positive rate, are privacy-invasive, and have been historically biased against poor and minorities.
</p>
<p>
Our specialty is building amazing software that delights users. We believe in the value of people and we're always working to improve ourselves and
those around us. We would be compromising our integrity if we didn't join in taking a stance against privacy violations and discrimination.
</p>
<p>
It's for these reasons that we respectfully decline any contracts where mandatory drug testing is a condition of the contract.
</p>
D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com1tag:blogger.com,1999:blog-5003807027724289640.post-48472999340808334352015-10-12T21:30:00.001-07:002015-10-13T06:29:35.950-07:00Size Matters: Improve Productivity with Cheap Screen Real Estate<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivHFiN-4k12zJOdg_WW1KAic2coswENuLq5DqkazqaJiKnfQfBpgjblidkbak_vfFl24Sdc357-2A9PuQU2XQHUuXdLs0r48c_VEXGyABy7Z9BLjNEZ2Xg8bQamBcx9lddCmqbspCv3yg/s320-Ic42/screen_comparisons.png" alt="Common Screen Sizes" style="float: left; margin-right: 7px;" class="postimage" />
<p>
It's hard to hire developers, and technical resources are expensive. Everyone is trying to figure out how to attract top talent,
reduce turnover, and increase productivity of existing staff without reducing their work/life balance.
</p>
<p>
Early on, I decided that if you're going to spend 8 hours a day 5 days a week for almost 50 weeks a year doing something, everything
about it should be as pleasant as it could be. I bought a new Macbook pro and two thunderbolt displays. Those investments very
quickly paid off.
</p>
<p>
Until recently, the benefits of large technology investments were just intuition. Last week, I realized how much more productive I
was working remotely than when I'm onsite with the client. One reason is that when I'm remote I get a lot more flow, but another reason
is that when I'm remote, I have a lot more screen real estate so I can see a lot more information at once.
</p>
<p>
I decided it was time to be more productive onsite too. This time, I wanted to have more concrete justification.
</p>
<h1>Screen Selection</h1>
<p>
A friend of mine and I did a fair amount of research picking out monitors that gave us the best bang for our bucks. I'm really happy
with my <a href="http://www.amazon.com/s/ref=as_li_ss_tl?_encoding=UTF8&camp=1789&creative=390957&field-keywords=apple%20thunderbolt%20display&linkCode=ur2&rh=n%3A541966%2Ck%3Aapple%20thunderbolt%20display&sprefix=apple%20thunderbolt%20display%2Caps%2C171&tag=dpatcalon-20&url=search-alias%3Dcomputers&linkId=XPZAS3MXMLZNDU5L">Thunderbolt Displays</a>.
They're IPS and they're quite high PPI. This time, I wanted to try something a little cheaper and a little larger and I wanted to try a
VA panel so I ended up getting a
<a href="http://www.amazon.com/gp/product/B00ITORMNM/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B00ITORMNM&linkCode=as2&tag=dpatcalon-20&linkId=2CS7LLXH6IBH2NDP">BenQ BL3200PT</a>.
This monitor, I think, has the same panel as the <a href="http://www.amazon.com/gp/product/B00L3KNOF4/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B00L3KNOF4&linkCode=as2&tag=dpatcalon-20&linkId=AGFQQK6SYIZYABAS">Samsung S32D850T</a>,
which has a slightly smaller price tag.
</p>
<h2>Screen Real Estate</h2>
<p>
When I talk about screen real estate, I'm not talking about the diagonal size of the screen; I'm talking about the resolution of the
screen. Having a larger screen doesn't do anything for you (at least not in the sense of a computer monitor) with regard to how much
information you can fit on the screen at one time. The difference between an HD 1080 screen at 27" vs. 32" is just stretching.
Resolution, however, is how many pixels the screen has. The more pixels there are, the more options the computer has to present data
(provided you have the visual acuity to perceive the data without scaling).
</p>
<p>
These displays are 2560 x 1440 or WQHD and are the same resolution as my thunderbolt displays, albeit over a larger surface area. Thus
the resolution is not quite as crisp as my thunderbolts, but still these are very high quality screens. If you're into lower pixel pitch
and higher PPI, I don't blame you. Higher pixel density screens are much more pleasant to look at, but they do come at a cost.
</p>
<h2>Comparing Resolutions</h2>
<p>
There are two very common resolutions in offices these days. An older WSXVGA+ which is an 8:5 aspect display at 1680 x 1050 pixels.
HD 1080 is also quiet popular which is the 16:9 display at 1920 x 1080. The HD 1080 has about 17.6% more pixels than the WSXVGA+. By
comparison, the WQHD, also 16:9, has 2560 x 1440 pixels (sometimes called 1440P). It has about 77.8% more pixels than HD 1080 and about
110% more pixels than WSXGA+. That is to say that if you have 1 WQHD display, you have more pixels than a dual screen WSXGA+ setup.
</p>
<h1>Benefits of Screen Real Estate</h1>
<p>
The benefits of additional screen real estate, unfortunately, are easier to experience than they are to describe. Having additional real
estate may mean that you will sense more data at the same time, but does not necessarily mean you will perceive these data. Similarly,
having the ability see multiple windows does not mean that you won't just continue editing one file at a time in a full screen editor. On
the other hand, not having the ability to optimize your workflow guarantees that you will not.
</p>
<h2>Anecdotes</h2>
<p>
I do have a few anecdotes that buttress my belief that having more screen real estate is inherently valuable. One time when I'm
particularly keen to the benefits of higher resolutions is when I'm developing responsive designs. I'll often dock my browser's DOM
inspector on the side and I'll slide the split pane to different responsive breakpoints watching the HTML/CSS changes flash in the
inspector. The additional real estate means I can see meaningful data in the DOM inspector <em>and</em> the responsive design at
the desktop breakpoint at the same time.
</p>
<p>
Another time I notice the benefits of having additional screen real estate is when working on native mobile applications. Native
development often involves simulators and emulators. Scaling during emulation isn't always well supported. Thus, the additional
pixels allows you to see the entire simulator at once.
</p>
<p>
Another example that I found particularly fascinating happened shortly after I took over a new project. I had been working on the project
for a few weeks from the client site. One day, I was working remotely and I had my terminal window off to the left and I had the application
in a browser to the right. This is a usual screen configuration for my workflow on my cinema displays. I had changed some code and the
application reloaded. When it did, I noticed dozens of queries pop up in the log. None of the ORM queries had been optimized to prevent
N + 1 queries. I spent a few hours fixing those, but may never have noticed them had I not had enough room on my screen to comfortably view
the browser and the terminal at the same time.
</p>
<h2>Summary of Benefits</h2>
<p>
Typically on HD 1080 and especially on WSXGA+, I have only one window visible at a time. That's why I didn't notice the N + 1 queries until
I was remote. There may be myriad other benefits of the additional screen real estate when considering looking at code in multiple panes,
<a href="https://en.wikipedia.org/wiki/Test-driven_development">test driven development</a>, debugging,
<a href="https://github.com/guard/guard">guard</a> and <a href="http://livereload.com/">live reload</a>, etc.
</p>
<h1>Costs</h1>
<p>
High quality screens can be expensive! The thunderbolt displays are almost $1,000 each. The BenQ was $550. The Samsung is $500. It's
hard to imagine pulling out all of your existing displays and replacing them with displays that cost several hundred dollars. Heck, I
spent hours justifying buying only one.
</p>
<p>
That's why it clicked. At some point I realized, it doesn't take much before the investment in display technology pays off. According to
several job posting / salary research sites, the average developer in Atlanta makes about $120K per year. To keep the math easy, I'll
call it $100K (also because I like lowballing estimates like this). Normally, overhead on a W-2 employee is roughly 25% - 50% of annual
salary. If you have a very modest benefits package (and not a lot of PTO), then an average developer in Atlanta will cost about $125K
per year.
</p>
<p>
Even if your organization buys these screens the same way I did (i.e., no sales tax and no bulk discount), then it'll cost about $550, or
about 0.4% of the cost of an average developer. That means that if you bought 100 of them, it would cost less than half of the annual
salary of your average developer for a year. Thus, if you have 100 developers and you get 100 screens and each developer is 0.5% more
productive for the year, then you actually make money on the investment.
</p>
<p>
If the cost still seems too high, you don't have to get VA panels. In fact, 32 inch screens are pretty big (they take up a lot of desk real estate too). These 25" <a href="http://www.amazon.com/gp/product/B00SPWPF1O/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B00SPWPF1O&linkCode=as2&tag=dpatcalon-20&linkId=3HJNJAYIAZO7YZIY">Dell U2515H</a>
IPS panels were only $350 at the time of writing. 100 of these would be less than 1/3 of the underestimated developer annual cost.
</p>
<h2>Recuperating Costs</h2>
<p>
The average American works 1,780 hours per year. To recuperate 0.28% of a developer's cost, the developer only needs to produce about 5 hours
more per year. That could be working an additional 5 hours per year because work is more fun. It could be 5 hours saved by not having to
tab between windows and the cost of brief context switches. It could be 5 hours <em>not</em> spent tracking down performance issues in
production because some N + 1 queries sneaked through.
<p>
<p>
It could be 5 fewer hours spent recruiting great job candidates because the technology makes the company a better place to work. Developers
with neat technology are more inclined to passively as well as actively recruit for you. This can bolster your recruiting efforts, also
saving money. Similarly, developers with a satisfying work environment are less likely to leave their jobs reducing the high cost of
turnover.
</p>
<p>
Studies on worker productivity show that American workers tend to have about 6 (and some studies show as few as 3) productive hours in an 8
hour workday. Better screens may improve ideal hours per day. That means if all of your developers show up Monday morning and find their
brand new screens, and in their excitement they're productive for 8 hours on Monday and Tuesday (rather than 6) and 7 hours on Wednesday,
then in the first 3 days, the screens have paid for themselves. I could be completely wrong about everything else and your net could still
be more valuable somewhat liquid (they have good resale values) assets for free.
</p>
<p>
Remember, you only need 5 additional productive hours per year to cover the cost of 1 high resolution display … and that's if you buy
new screens every year. If you plan on keeping the displays for 5 years, it's just 1 hour per year.
</p>
<h2>Incredulous?</h2>
<p>
Let's consider a more pessimistic case. Let's say your staff, on average is pretty junior (and you're using technologies that aren't
particularly backend heavy. If that's the case, your developers may cost you an average of $80K salary and per year and $20K in benefits,
taxes, recruiting efforts, etc. Let's say they also take no vacation or sick leave and thus work a full 2,080 hours per year. Further,
your staff is productive 100% of working hours.
<p>
<p>
In this case, you're paying an average of $48 per productive work hour. If you buy very nice displays at say, $960 each, then you need 20
additional productive hours to cover the cost of one display. Even in this very unlikely case, that's less than 1% per developer. And
they don't need to work 2,100 hours in a year per se. They can be 1% more efficient. That means if they're writing 100 lines of perfect
code per day, then to cover the cost of a new display, they only need to write 100 lines of perfect code per day (and on the tenth day,
perhaps they write 10 extra perfect line of code . . . or better yet, they delete a line or two).
</p>
<p>
More likely your average developer is closer to $125K per year, they work 1,780 hours per year, and they are productive fewer than 6 of
every 8 hours. Thus, their cost is $94 per productive hour. Also, it's very reasonable to get smaller IPS WQHD panels for $350 each
(especially if you have a large team and get a bulk or corporate discount). So, you need fewer than 4 hours per developer or 0.3% more
productivity. An average developer contributes 10 lines per day to the codebase
(<a href="http://www.amazon.com/gp/product/0201835959/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=0201835959&linkCode=as2&tag=dpatcalon-20&linkId=E7QPLDQW5WSHZJHX">Mythical Man Month</a>) so
to break even on your investment, your average developer only needs to net an additional 3 lines of code every quarter.
</p>
<p>
<em>
n.b., I don't support using lines of code as a measure of productivity. It's merely illustrative of the relative scale of the cost of
a display to the cost of a developer. Try your own calculations using bugs sent to production, mean time to defect resolution, etc.
</em>
</p>
<h1>Recommendation</h1>
<p>
I recommend making an investment to replace low resolution displays for developers and pairing stations (and I would consider broadening
the reach really to non-technical employees as well) with high resolution displays. I think that the cost of the hardware would be
recovered very quickly by improved developer efficiency and performance. The investment is low risk because the cost is relatively low
and the displays are a moderately liquid asset.
</p>
<p>
If you have 100 developers on your team, it may be hard to get approval for the capital to buy $35,000 worth of displays. In that case,
you can always hire another developer (well, you can try at least) for $35,000 per quarter.
</p>
<h2>Display Options</h2>
<p>
<a href="http://www.amazon.com/gp/product/B00ZFFN656/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B00ZFFN656&linkCode=as2&tag=dpatcalon-20&linkId=CZX5CHG4TEI7QK4M">Dell P2416D 24 Monitor with QHD 23.8-Inch Screen, Black</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=dpatcalon-20&l=as2&o=1&a=B00ZFFN656" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
<br /><a href="http://www.amazon.com/gp/product/B00SPWPF1O/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B00SPWPF1O&linkCode=as2&tag=dpatcalon-20&linkId=JCVVJODJH2HATE6O">Dell UltraSharp U2515H 25-Inch Screen LED-Lit Monitor</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=dpatcalon-20&l=as2&o=1&a=B00SPWPF1O" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
<br /><a href="http://www.amazon.com/gp/product/B00P0EQD1Q/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B00P0EQD1Q&linkCode=as2&tag=dpatcalon-20&linkId=5ICOW3QZQKNMJP5Y">Dell UltraSharp U2715H 27-Inch Screen LED-Lit Monitor</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=dpatcalon-20&l=as2&o=1&a=B00P0EQD1Q" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
<br /><a href="http://www.amazon.com/gp/product/B00ITORMNM/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B00ITORMNM&linkCode=as2&tag=dpatcalon-20&linkId=SDRZQFKBJMLE5SG5">BenQ BL3200PT 32-inch WQHD 10-bit USBx4 HDMI Monitor built for Creative Class</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=dpatcalon-20&l=as2&o=1&a=B00ITORMNM" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
<br /><a href="http://www.amazon.com/gp/product/B00L3KNOF4/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B00L3KNOF4&linkCode=as2&tag=dpatcalon-20&linkId=RGTVERDTNGX6YQGD">Samsung 32" WQHD LED Monitor (S32D850T)</a><img src="http://ir-na.amazon-adsystem.com/e/ir?t=dpatcalon-20&l=as2&o=1&a=B00L3KNOF4" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com4tag:blogger.com,1999:blog-5003807027724289640.post-28673175875613903352015-06-10T22:31:00.001-07:002015-06-10T22:31:38.694-07:00Project Nostalgia: Warehouse Management<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiZDrSn0yMHhO13PeY_oPtJ0W06_zZ6-1j6c_WGBa4MvuT8g4XhSH1vCkfcq4YlQ6Jic4Xpsfgma60uzbVx2pgMwREYAaWVhyDZJhJeOdrtjkNqv4K0v0Edou8wWGJN-iLZOJbyVSprry0/s320/all_work_no_play_adjusted.jpeg" alt="All Work and No Play" style="float: left; margin-right: 7px;" class="postimage" />
<p>
Early in my development career, I got a job at an entertainment company that rented party supplies like inflatable bounce
houses, inflatable slides, human gyroscopes, etc. On my first day, I was introduced to my new boss, the owner of the company,
on his way out the door headed to China for 2 weeks.
</p>
<p>
I introduced myself, thanked him for the opportunity, and asked what he wanted me to work on while he was gone. "You'll figure
it out," he said, "make us some money." And out the door he headed.
</p>
<p>
Once I found my office and tracked down a computer, I sat down and thought, "what the heck am I going to do here?" Having just
gotten my MBA, I decided I'd take a page from my business consulting class and just "see what my lantern shows me." I walked
around asking my new coworkers what the company does, what they do, and what could make their lives easier.
</p>
<p>
There were the usual gripes, manual processes, duplicate paperwork, etc. One problem in particular, however, I found particularly
intriguing. It turns out that the company had an uncomfortably high refund rate because they'd show up at parties and equipment
would be missing. They also would only schedule a few parties a day, despite having plenty of equipment to spare, because it took
so long to prepare for an event. Similarly, they required reservations be made a week in advance.
</p>
<h2>Shopping in Your Own Warehouse</h2>
<p>
If you're like me, when you make a grocery list, you write items down in the order you think of them. Then, when you go to the store,
you walk up and down the items, grabbing things from the list and crossing them off as you go. Also, if you're like me, sometimes you
pass an item and you have to go back. Or, you don't notice an item that you missed and you go home without it.
</p>
<p>
If you want to try an experiment (actual or thought), commit to going to the grocery store and getting everything on your list in the
order it appears on your list. You'll probably find you forget less stuff, but you do a lot more walking around the store. It's not
a particularly efficient way to organize a shopping trip. If you implement this in, say, a warehouse you may issue fewer refunds but
you can't increase the number of events you can service in a day.
</p>
<p>
I'm sure by now, you're imagining myriad ways to organize a list of grocery items so that, if nothing else, at least you have some
consistency. You could organize them alphabetically. That way, your items on your list will follow more or less the same pattern
visit to visit. That may or may not be better than in the order you thought of them (assuming you remember grocery needs that are
grouped together).
</p>
<p>
Imagine, however, that you can't sort your grocery list in the order you think of it. Imagine you can't even sort the list
alphabetically. In fact, your only option is to sort the items on the list in the order the item first appeared in your grocer's
inventory. Imagine how nonsensical it would be trying to track down where all of your groceries were. How much you'd forget (or
perhaps even leave deliberately). That's what we had.
</p>
<h2>A Guided Tour</h2>
<p>
I printed our inventory list and I walked around the warehouse writing down the exact coordinates of every product and every
component we had. I also estimated the volume, weight, and stack-ability (if you will) of the item.
</p>
<p>
Hoping to get a cheap win and to prove that it could make a difference (some of the guys in the warehouse weren't interested in
their inventory lists being optimized), I got a few of the guys to agree to try it out for 1 day. I entered my data into the
inventory table and sorted the inventory list by aisle, bin, then level. That was it. Only took a few minutes, was easy to
revert if it failed, and I could switch it based on the user.
</p>
<p>
When I got back from lunch, one of my coworkers who sat outside my office said, "hey, warehouse really wants to talk to you …
they've been in and out of here constantly since you left." I thought, "oh no . . . I probably broke something and they can't
print inventory lists at all now."
</p>
<p>
I headed out to the warehouse preparing my apology. I had barely walked through the door when someone said, "hey Patrick, check this
out?" There were several palates stacked with boxes and party equipment distributed along the loading dock. "What's up?" I asked.
</p>
<p>
Turns out my first sorted list was such a hit that they decided to start racing each other to see who could fulfill the inventory
list faster. They had not only prepared the next days events, but several days after. I know it doesn't sound like it should take
that long, but this warehouse typically had 4 or 5 people preparing 10 or 15 parties a day give or take.
</p>
<h2>A Deliberate Solution</h2>
<p>
Feeling I'd created some real value, I went back to my sorting. I updated the logic so that bins were in reverse order on odd number
aisles (they started at the end of aisle 1, went to 2, the back up 3, and so forth). I sorted the items so that, given close proximity,
the larger or heavier items would appear in the list first so they'd be on the bottom of the cart.
</p>
<p>
In the display, I grouped the items by aisle. Once I did this, I realized I should start at the end of the first aisle that had equipment
on the list, then continue on the beginning of the next aisle with equipment (rather than just naively going up and down each aisle).
With the help of the warehouse team and some statistics I pulled from the historical invoice data, we also started physically grouping items
based on how frequently they were pulled on the same list.
</p>
<p>
Some of them were obvious. If you ordered an inflatable, you'd likely also get a blower. People who rent tents also rent chairs. Some
were less obvious. People who rented dance floors typically didn't rent inflatables but did rent a bar and concessions. Once we established
that not only could we control our own inventory but that we can control our own reports, it became a game to optimize the process.
</p>
<p>
In the end, it only really took 1 person to run the entire warehouse, though we staffed 2 because people liked working together. The rest
of the warehouse team got promoted. It was actually something of a coveted job to get to deliver the equipment, spend some time outside,
visit with the people having the party (partying people are usually pretty happy people).
</p>
<p>
We also started booking more parties. We even booked parties same day if we had the equipment and the people available. I'm sure there
are COTS products that are really good at solving this very problem, but solving a real business problem with feedback
from the people who were most impacted by it and seeing measurable gains in revenue was well worth the effort. It also help build some
relationships and establish a collaboration they'd never had before (I was the first developer who worked onsite).
</p>
D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com4tag:blogger.com,1999:blog-5003807027724289640.post-3109521096211167892015-05-24T19:39:00.003-07:002015-06-04T16:48:14.367-07:00Remote Interview Pairing Challenge with Cloud9<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFepXGZLTKPIZflUMOh-VAvToD326MA_294ZiFDdUwXsaJGHilcF9SjUqzYokCO1hPojmaYppFdm1XOaQ5emepA5bw2CXE8F65h9JRaE3Q50tg2EXptPAx3hueFCMSgqIkyzQ3fSPbaJY/s320/cloud9.png" alt="Cloud9 IDE" style="float: left; margin-right: 7px;" class="postimage" />
<p>
My interview process typically involves just trying to get to know someone to see if we're a good match culturally. After all, if you have a good mind
for development, I know you'll be great if we can put you in the right environment with the right culture. As a result, I tend to spend a lot more time
talking about what kinds of problems a developer finds interesting, what sorts of roadblocks she finds frustrating, and what technologies we have in
common.
</p>
<p>
There is, however, still the necessary evil of seeing how far along the candidate is on the programming journey. To that end, I usually try to do a very
basic code challenge. Normally it's something simple enough that we can still have a healthy conversation while we solve it together as a pair, and it's
not so academic as to be useless in real life. Mostly, I just want to pair together to solve a problem.
</p>
<p>
I give the candidate the opportunity to select the language of her choosing and typically I'll know it well enough that we can pair to solve the problem.
I explain the challenge, we set up unit tests, write a failing test, and make it pass. Sometimes, if I don't feel like I know enough, I'll change it a little
and we'll try to adapt to the changes together.
</p>
<p>
This has been such a positive experience for me (and for the candidates I've talked with after their interviews), that I've spent a fair amount of time
practicing the process. As I recently discovered, it's such a fundamental part of my interview, that I was completely unprepared when a client asked me, at
the last minute, to step into an interview to do a pairing challenge with a candidate . . . who was remote!
</p>
<h1>Remote Pairing Interview</h1>
<p>
We tried to come up with a few options for doing the code challenge. After all, our shop pairs pretty much all the time and we try to be supportive of
remote work. I mean, we've solved this problem before; however, it's seldom without hiccups. I know that candidates are usually nervous in an interview
and I feel a lot of responsibility to help set their minds at ease. To that end, I like to appear calm, collected, and organized.
</p>
<p>
We considered using <a href="http://tmate.io/">tmate</a> and vim. We talked about screen sharing with <a href="http://www.teamviewer.com/">TeamViewer</a>.
We also thought about just letting this candidate work on a harder challenge, maybe something from <a href="https://www.hackerrank.com/">Hacker Rank</a> or
<a href="https://projecteuler.net/">Project Euler</a>, and email us the solution.
</p>
<h2>Shortcomings</h2>
<p>
Each of these had a downside. Tmate would give the dev access to my machine and I wanted to let them do whatever they wanted without putting my machine in his
hands. Alternatively, his machine could host but I wanted to be able to get to the solution later. Screen sharing with TeamViewer isn't as responsive as I like
when pairing, especially if I want to be able to help out rapidly. Also, I type in Dvorak and sometimes that's an issue (turns out he does too . . . by the way).
The async challenges, while good, don't let us know if we'll enjoy pairing together.
</p>
<h2>Enter Cloud9</h2>
<p>
I remembered, a while back, running across <a href="https://c9.io/">Cloud9</a> and playing with it a little bit. I thought, "hey, what if we just try this?" Cloud9 would give us a sandbox
that we can work in with a full ubuntu virtual machine. The candidate can do whatever he wants to it, we can code, install tools, whatever. I said, "hey, give me
a second . . . I'm going to try this Cloud9 thing . . . go ahead and get your account set up and I'll invite you to a project."
</p>
<p>
In about 5 minutes, the candidate, my co-worker, and I were pairing and running tests on our Cloud9 workspace. There were no security concerns, communication was
simple, we still installed and used tmux and vim, we had a sandbox, I was able to save the code. It was a really great experience. Not only did we offer a position
to the candidate, but the candidate has been happy on our team ever since. I consider it a sign of a good interview when a candidate takes a position and I think
that Cloud9 had a lot to do with this one.
</p>
<h2>Next Steps</h2>
<p>
I've not used Cloud9 (or any web based IDEs for that matter) on day to day work. Unfortunately, I'm not really in a position to try it out on the free tier. At
some point, I want to go ahead and get a subscription and see how well it works out for me. I certainly see the value of cloud based IDE services and I like the
idea that the most expensive development hardware you need is a <a href="https://www.google.com/chromebook/">Chromebook</a>.
</p>
<p>
Okay, so maybe that's a little hyperbolic, but not for long. We have pairing stations that we treat like cattle rather than pets. We can wipe it completely clean
and have it up and running again ready to go in just a few minutes. The hardware isn't commodity, but it could be. In fact, it could just be a web interface to
a virtualized instance and why couldn't that be accessed from a Chromebook, pairing station, laptop, tablet, or . . . maybe in a pinch . . . a phone? Personally,
I like it and hopefully I'll get a chance to push Cloud9 beyond "hello world."
</p>
D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com2tag:blogger.com,1999:blog-5003807027724289640.post-33018093793943702782014-12-31T12:46:00.000-08:002015-05-23T12:51:53.094-07:00Our 2014 Charity Picks<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhy0yGLZkMSI1LvL6iGPNO4gim6AIPnEREVL1AOxI3LOCwTUTAaZcrtfFsIq39FLs_XkB_Wpko4KEDOzJIYWRg_iDdpV5SNqAgTEw_9nHaGvP5Hh1_vcitjrksDagkPc1LhaFKB70h0KvA/s320/about_patrick.jpg" alt="Charitable Giving" style="float: left; margin-right: 7px;" class="postimage" />
<p>
We feel lucky every day. Our community has provided us with a great place to live and work. Our kids can grow and learn in
safety and comfort. We're surrounded by opportunities personally and professionally.
</p>
<p>
For that, we are eternally grateful, and we like to give a little bit back to our community. We work hard to research the charities
we choose to support and to make sure they're doing the greatest good with their contributions.
</p>
<p>
Here are the charities we selected for 2014
</p>
<h1>Episcopal Relief and Development</h1>
<p>
<a href="http://www.episcopalrelief.org/">Episcopal Relief & Development</a> works to save lives and transform communities worldwide.
They rebuild after disasters and empower people to create lasting solutions that fight poverty, hunger, and disease. They working in
40 countries impacting the lives of nearly 3 million people around the world.
</p>
<p>
ERD has a high rating on <a href="http://www.charitynavigator.org/index.cfm?bay=search.summary&orgid=10634#.VWDVBtNViko">Charity
Navigator</a>. While we typically seek a higher rating, it's very close to the highest it can be and we have close personal
ties with the organization.
</p>
<p>
One thing we love about this program is you get to donate specific needs to the communities you're supporting. This is certainly
the first time we've donated a cow and chickens. We also funded farming tools and community gardens, prenatal and postnatal
maternity care, and 1 year of education for 5 girls.
</p>
<h1>Tiger Flight Foundation</h1>
<p>
A little closer to home, the <a href="http://tigerflightfoundation.org/">Tiger Flight Foundation</a>, base in Rome, Georgia inspires
kids to be "Pilot in Command" of their own lives. All donations go toward the motivational and character development programs.
</p>
<p>
My relationship began with Tiger Flight as a volunteer pilot. I had a young man in my plane and as we taxied out, he said, "I've never
been in a plane before." I asked him what was his favorite place to visit. He told me he'd never been out of Rome, GA. He was
nervously excited as we started rolling down the runway.
</p>
<p>
We had barely left the ground when he could no longer contain his excitement and he looked at me almost teary-eyed and said, "I feel
like I can see the whole world from here!" I thought, "from here kid, the whole world is in front of you . . . and I think you'll see
it."
</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com1tag:blogger.com,1999:blog-5003807027724289640.post-28909750419445597282014-12-05T22:18:00.000-08:002014-12-15T19:15:46.917-08:00Secure Foscam IP Cameras with SSL on Raspberry PI and NGINX<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtc4Hdhj0bG4JiCHosb8f69RRztQHP3SxwUP62pyj2fjuU9J5f9VrwqeVHqK7OtACQg9s_5hEzPH_ffPhpBN1K6c1aGb9crfJNpUN4n-2BCX0uCT9pIiQDCNQQPBmMfQ_X9J_mjZ7guXY/s320/foscam-pi.png" alt="Foscam + Raspberry Pi = SSL" style="float: left; margin-right: 7px;" class="postimage" />
<p>
When my first kid was born, my wife and I wanted a convenient, cheap, but externally accessible way to monitor our daughter. Thus,
the usual baby monitors wouldn't do the trick. We ended up getting a really handy and super cheap
<a href="http://www.amazon.com/gp/product/B006ZP8UOW/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B006ZP8UOW&linkCode=as2&tag=dpatrickcaldwell-20&linkId=SF2R2TF76RFPBDGP">Foscam FI8910W IP Camera</a>.
</p>
<p>
Everything about it is pretty great … except security. For that reason, I never poked a hole through my firewall so that friends
and family could peek in on Piper from time to time. I could always access the camera over VPN, but nobody else could ('cause I'm stingy
with my network access).
</p>
<p>
When I found out I had another one on the way, I decided that not only did I need another camera, but I needed a more convenient way to
securely access my cameras from outside my house. I decided I to pick up a <a href="http://www.raspberrypi.org/">Raspberry Pi</a> and
expose an https endpoint that could reverse proxy requests to my ip camera. This way, I have a secure connection into my house. It's
still plain text between the camera and the pi, but that's inside my network and I'm less concerned about it there.
</p>
<p>
In any case, I picked up a
<a href="ttp://www.amazon.com/gp/product/B00LVAFC7Y/ref=as_li_tl?ie=UTF8&camp=1789&creative=390957&creativeASIN=B00LVAFC7Y&linkCode=as2&tag=dpatrickcaldwell-20&linkId=4V4HV3B5QUT6AYOI">Raspberry Pi Starter Kit</a>
which I recommend for your first pi. It'll come with the components you'll need to get set up. The second time I did this (for the sake
of recording the steps to write this blog, I just formatted my own noobs card and I used the wifi dongle from the previous pi kit.
</p>
<p>
I tried to install noobs lite on an 8gb microsd card I got from the <a href="http://www.raspberrypi.org/downloads/">Raspberry Pi Downloads</a>
page, but noobs lite didn't work with the wifi dongle so I recommend plain old noobs. For the second time around, I just downloaded Raspbian
and used <a href="http://en.wikipedia.org/wiki/Dd_%28Unix%29">dd</a> to image the microsd. Again, I recommend noobs (and I recommend the
starter kit) unless you feel pretty comfortable with command line utilities. If you are, use the
<a href="http://www.raspberrypi.org/documentation/installation/installing-images/">instructions for installing operating system images</a>
from the raspberry pi site.
</p>
<h1>Pi</h1>
<p>
That being said, with noobs, you just format your micro SD card with FAT and copy the contents of the noobs zip to the sd card root. Put
the SD card in the pi, connect the mouse and keyboard, connect ethernet or the wifi dongle, connect some video output, etc. Then, plug in
the device.
</p>
<p>
The first thing to do is get connected to wifi. It's easier to do in the GUI so run startx, configure your wifi network, and
log out.
</p>
<p>
Enable SSH in sudo raspi-config.
</p>
<p>
Using SSH to administer a box is kind of a pain without tmux so get that. Also, vim is awesome so get that too. Finally, we're going to
be using <a href="http://nginx.org/">nginx</a> as our reverse proxy so install that as well. <pre name="code" class="shell">sudo apt-get update && sudo apt-get install tmux vim nginx</pre>
To make SSH even easier, <a href="http://en.wikipedia.org/wiki/Secure_copy">scp</a> your public key to your pi's ~/.ssh folder and
<a href="http://en.wikipedia.org/wiki/Cat_%28Unix%29">cat</a> it into authorized_keys.
</p>
<p>
If you are using wifi, you'll find that wifi is disable after rebooting until the dongle is removed and re-inserted. You can change this
behavior by executing <pre name="code" class="shell">sudo vim /etc/network/interfaces</pre> and changing <pre name="code" class="shell">iface wlan0 inet manual
wpa-roam /etc/wpa_supplicant/wpa_supplicant.conf</pre> to <pre name="code" class="shell">auto wlan0
iface wlan0 inet dhcp
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf</pre>
</p>
<p>
You'll want nginx to start automatically on reboot too probably so execute <pre name="code" class="shell">sudo update-rc.d nginx defaults</pre>
</p>
<h1>Router</h1>
<p>
Give your pi a static ip address. My router lets me map static ips to mac addresses. DD-WRT lets you do that too. If you can't with your
router, configure a static ip address following the
<a href="https://wiki.debian.org/NetworkConfiguration#A.2Fetc.2Fnetwork.2Finterfaces">Debian Network Configuration Instructions</a>.
</p>
<p>
Give your router a static ip as well.
</p>
<p>
Forward port 443 (the default SSL port) to 443 on the pi's IP address
</p>
<p>
Get a dynamic DNS account that's supported by your router. If your router doesn't do dynamic DNS (and you can't install a decent firmware
that does, you can use <a href="https://help.ubuntu.com/community/DynamicDNS">ddclient</a> on your pi instead.
</p>
<h1>Domain</h1>
<p>
You'll need a domain name that you own to get an SSL certificate. Register one.
</p>
<p>
In your domain's DNS configuration, create a subdomain with a CNAME record pointing to your dynamic DNS domain.
</p>
<h1>SSL</h1>
<p>
Get your SSL certificate from <a href="https://www.startssl.com/">Start SSL</a> (the free certificate will be fine). You'll have to
validate your domain. The process is pretty straightforward.
</p>
<p>
Download your certificate, key, and the intermediate certificates and make a unified certificate: <pre name="code" class="shell">wget http://www.startssl.com/certs/ca.pem
wget http://www.startssl.com/certs/sub.class1.server.ca.pem
cat ssl.crt sub.class1.server.ca.pem ca.pem > ssl-unified.crt</pre>
<p>
SCP the key and the unified certificate to the pi's /etc/nginx folder (I like putting my certs in a subfolder)
</p>
<h2>Configure nginx</h2>
<p>
Create a configuration file called /etc/nginx/sites-available/ipcams <pre name="code" class="shell">server {
listen 80;
server_name your.domain.com;
server_name your.pi.ip.address;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
ssl_certificate /etc/nginx/certs/ssl-unified.crt;
ssl_certificate_key /etc/nginx/certs/ssl.key;
server_name your.domain.com;
server_name your.pi.ip.address;
location /front_porch/ {
proxy_pass http://your.porch_cam.ip.address:80/;
}
location /baby_room/ {
proxy_pass http://your.baby_cam.ip.address:80/;
}
}</pre>
</p>
<p>
Remove the default symlink from /etc/nginx/sites-enabled and add new symlink <pre name="code" class="shell">cd /etc/nginx/sites-enabled
sudo ln -s ../sites-available/ipcams ./ipcams</pre>
</p>
<p>
Restart nginx: <pre name="code" class="shell">sudo service nginx restart</pre>
</p>
<h1>Summary</h1>
<p>
So, now https requests to your subdomain are resolved by your dynamic DNS to point to your IP where your Pi is. Your Pi gets an https
request and forwards it inside your well protected network (in plain text) to your camera. I keep my Pi wired to cut back on the
wireless traffic that happens in plain text. In any case, this way you can get from outside your house to inside your house over an
encrypted ssl connection.
</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com97tag:blogger.com,1999:blog-5003807027724289640.post-6237558497297366542014-11-14T18:11:00.000-08:002014-12-15T19:15:03.754-08:00Refresh/Update jQuery Selector after Ajax or other DOM Manipulation<a href="http://www.jquery.com"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhpC2VGBv82SARViqUpFN-KQLhElwHllFEiyKpsShlorY2ZftzWalAZtc20gNNJTHzC4W9BUDdn8CdAXJoHFYpTUIVI1B53qQNdeecM3WihUY5FE6_u41_51rONVNrZ0vSWIiUrEozh3I/s288/jquerylogo1.png" alt="jQuery Logo" style="float: left; margin-right: 7px;" class="postimage" /></a>
<p>
I'm working on a more or less single page app right now. As such, there's a lot of dynamic DOM manipulation. Sometimes, we need to re-evaluate a jQuery selector to get the new set of objects after the DOM has changed.
</p>
<p>
Typically we've solved this problem by always using the selector to find the children. This became really frustrating in my <a href="http://jasmine.github.io/edge/introduction.html">Jasmine</a> tests where I found myself doing this a lot! <pre name="code" class="js"> it("toggles the display of an element", function() {
var $togglableItem = $('.togglable');
expect($togglableItem.length).toBe(0);
$button.trigger('click');
var $togglableItem = $('.togglable');
expect($togglableItem.length).not.toBe(0);
$button.trigger('click');
var $togglableItem = $('.togglable');
expect($togglableItem.length).toBe(0);
});</pre>
</p>
<p>
Even in production code (outside of tests) that can be kind of a pain. I wanted to be able to do something more like this: <pre name="code" class="js"> it("toggles the display of an element", function() {
var $togglableItem = $('.togglable');
expect($togglableItem.length).toBe(0);
$button.trigger('click');
expect($togglableItem.refresh().length).not.toBe(0);
$button.trigger('click');
expect($togglableItem.refresh().length).toBe(0);
});</pre>
</p>
<h2>A Common Solution</h2>
<p>
I've seen some implementations of a jQuery refresh plugin that look like this: <pre name="code" class="js">(function($) {
$.fn.extend({
refresh: function() { return $(this.selector); }
});
})(jQuery);</pre>
<p>
<p>
I have two problems with this approach. First, I don't always have my elements attached to the DOM when I want to do a refresh. In fact, typically in testing, I don't add my test elements to the DOM so I can't just reuse the selector. The selection depends on the parent jQuery object.
</p>
<p>
Second, I also really enjoy the fluency of jQuery and often make use of <a href="http://api.jquery.com/end/">.end()</a> method when it makes sense. The approach above flattens the selector stack and .end() returns the default jQuery object.
</p>
<h2>Preferred Approach</h2>
<p>
To maintain the fluency of jQuery and allow the refresh, here's what I'm proposing: <pre name="code" class="js">(function($) {
$.fn.extend({
refresh: function() {
var $parent = this.end();
var selector = this.selector.substring($parent.selector.length).trim();
return $parent.find(selector);
}
});
})(jQuery);</pre>
</p>
<h2>Caveats</h2>
<p>
I haven't used this in the wild yet. I know <a href="http://api.jquery.com/selector/">.selector</a> is deprecated, but it's the best I could find for now. It does, however, work pretty darned well in my Jasmine tests. :)
</p>
D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com20tag:blogger.com,1999:blog-5003807027724289640.post-54324610958255468642014-05-12T20:20:00.000-07:002014-05-12T20:20:01.156-07:00How to Market a Mobile App: Professional Design<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuciRHN8KvA4Rl-bcm5zgvJOt-vEFCd8TDXiwSfjQm2tyWypOasHqmTbci2BJALcKRKMo78OOW-Cc51MdSXVhDlbQPkOo5omKentEr-yNJTxh5OqAho1wMynsgqdqm1OV51of4dDW2-P0/s320-p-no/" alt="The New This to That" style="float: left; margin-right: 7px; border-radius: 25px;" class="postimage" />
<p>
The next phase in the <a href="http://dpatrickcaldwell.blogspot.com/2014/01/how-to-market-mobile-app-where-to-start.html">How to Market a Mobile App</a> series
was to try a whole new look and feel. While I feel like the design of This to That was actually pretty good, it's obvious design is not really my strong suit.
I hired a professional design company that reworked my icon for about $150.
</p>
<p>
I dropped the new icon into the game and had my wife play with the This to That theme generator for a while. She came up with what is now the current
This to That default theme. We are pretty pleased with the way it turned out and it appears users are too.
</p>
<h2>Promotional Images</h2>
<p>
Another thing we changed were the promotional images that show up with the app on the app store. I had reviewed dozens of my favorite apps to see which ones
I thought had the most compelling promo images. I noticed I was most enticed by promotional images that featured interesting design with the application
screenshot embedded in the image (rather than the whole image itself).
</p>
<p>
I had tried my hand at this kind of design and these are what I had before:
</p>
<p style="text-align: center;">
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipflfKzCbvkjbzcW77LkEzPOTTiguVpwVOEv1sHhOcW_sydlHlp03xcfHG4WCMCM-naD2M6_HYaGeda0dSbA_PekJX7s5L6GQXqYVj9ykiXmGA3bmdflTZVwU_prHbYwAIaDYCyl3zywA/h200/iPhone5+Game+Center.png" alt="Game Center Integration" />
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEga7KAqHWF4Cf9QivtxN3kra4NSvGc0yjsTl9cBIJzlWqs-eljPbmvhVyp_9ELjIr7J4cYiLPW9W5Qt7IapO4XPmQxSRrVojSjec4Y5hhIQ2naYlrcb4aUoB1XNJLGYnAQFsIenlatpapk/h200/iPhone5+Intuitive+Play.png" alt="Intuitive Play" />
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVhVosVA9vmAoNQ4K7F3c_NgJ57dz9V9oMpn6aEpNK3-zkTHJkyB1QKOJ5kF_eftEWnwiEplimqP95YmVH0_OF_Kj9ZbtSP_RRZQQZgqlJhFYPuka62TZ05Ivi36tpMNolHHxoDsuKmI0/h200/iPhone5+Social+History.png" alt="Social Features" />
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_QwenqJXh6cAKiYberyB5iRfUKZM_hqJKxHBlLCTERnCJhYx3v7_2AJcEC-c17pEf_K3hj0fGjSSXn1FUfmq09aE4xbyVixAiZg6L3rqoX-Jv5NVGlAcM0mrngS_OddLHhA17eafVi2U/h200/iPhone5+Customizable+Theme.png" alt="Custom Themes" />
</p>
<p>
In an effort to try something new (and to get a new release out faster), I decided to replace these with simple screenshots. You can see them on the
<a href="https://itunes.apple.com/us/app/this-to-that/id499422300?mt=8">This to That on iTunes</a> page. I may have my designers throw together some
new promotional images if I start to get enough data about new downloads that I'll be able to share any significant observations about their effectiveness.
</p>
<h2>Results</h2>
<p style="text-align: center;">
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1HgmatC6lPXK6gvcU-npCNyMlIVKRQz73iSWIPmplUtFFwQ7NXAJu-R1YpgVcQCWOWWhbQ_UNFDZzfhgiosOlwtOMYVmkTOddW4tSlXe60tmvgw5SSp5vfhMqIR6PJQCN5NTDQz3HXEw/s600/pro_design_before_after.png" alt="Before/After Pro Design" />
</p>
<p>
The new graphics don't appear to have had a huge impact on new users. It looks like about 10% - 20% more downloads than before. None of the current users
have complained in comments or ratings so that's probably a good sign really. I think the most interesting statistic I've observed this time around is in
session duration.
</p>
<p>
It appears whatever I added between 3.5 and 4.1 was displeasing to users. We went from an average of 4 minutes per session to about 2 minutes per session.
4.2 brought us back up and 5.0 and 5.1 have leveled us off at about 3 minutes per session, but almost all of our players now play every day.
</p>
<h2>A few other observations</h2>
<ul>
<li>Very few people attempt to play multiplayer</li>
<li>Many players open the drawer; very few swipe it</li>
<li>Since we moved the help button, more people click help</li>
<li>There are very few social sharing events</li>
<li>Most common incorrect words: TOOS, BEAS, SEAP, BONS, BAIN</li>
</ul>
<h2>Next Steps</h2>
<p>
I have a release ready to go in hopes that we get more reviews on the app store (hopefully positive too). We have always had a feedback button in the app,
but at some point the link broke so presently it takes you to the app store but not to the app's page. To fix this, we started using
<a href="https://github.com/arashpayan/appirater">appirater</a>. On a related note, I recommend using <a href="http://cocoapods.org/">cocoapods</a> for iOS
app dependencies.
</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com70tag:blogger.com,1999:blog-5003807027724289640.post-58582145115512304632014-04-19T11:05:00.000-07:002014-04-19T11:05:42.376-07:00How to Market a Mobile App: Professional Review Sites<a href="http://mobiappsreview.com/iphone-ipad/iphone-games/this-to-that-iphone-app">
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhw4Qy1dCLdKozaalaadx0VvSMniXxz8rjFD3E37gwXbrQpAq-Ti6d_fZ5SXTto-Dsmu2LpOdPzWyns3PTT8Yfi7jCikHFq3D4v_n2eJHJpD4hR5a07mykz3zhNQAX5kJQ1-raKF_Yo3bI/w246-h367-no/mobi_apps_review.png" alt="Mobi Apps Review" style="float: left; margin-right: 7px;" class="postimage" />
</a>
<p>
I've been working on the revealing (and humbling) <a href="http://dpatrickcaldwell.blogspot.com/2014/01/how-to-market-mobile-app-where-to-start.html">How
to Market a Mobile App</a> series so I've found myself more open to some ideas that I usually am. A few months ago, I got an email from the owner of
<a href="http://www.mobiappsreview.com">Mobi Apps Review</a>. I've heard about sites like this before that establish a viewership on their website and
on social networks. Some friends tried it out paying as much as $5,000 for one review.
</p>
<p>
Mobi purported to have 25,000 followers on twitter and the same on facebook. The offer was to write a review and publish it on all of their social
networks for $25. I figured, it's worth 25 bucks just to know what a little exposure could do for me. Besides, it'll give me something to post about
on the <a href="https://www.facebook.com/MobileMagicDevelopers">Mobile Magic Developers facebook page</a>.
</p>
<p>
The <a href="http://mobiappsreview.com/iphone-ipad/iphone-games/this-to-that-iphone-app">Mobi Apps Review of This to That</a> came out March 6th, 2014.
It was nice to see that the review was generally positive. Not all of the reviews on Mobi are positive so I feel like the reviewer really did enjoy the
game.
</p>
<h2>Results</h2>
<p>
I reviewed the <a href="http://analytics.google.com">Google Analytics</a> for the 3 weeks before and the 3 weeks after the Mobi review was posted. I
saw no discernible effect on new users.
</p>
<h2>Conclusion</h2>
<p>
I don't think the lack of results necessarily indicates that the review had no impact. To be sure, I could've spent more effort promoting the review. I
also, unfortunately, have no insight into how many viewers actually saw the review. Of those viewers, I have no idea how many visited the
<a href="https://itunes.apple.com/app/id499422300?mt=8">This to That for iOS</a> app store page. Even if I did, I have no way of knowing how many people
who view the app store page actually download the application and whether or not the review had an impact on that.
</p>
<p>
All in all, I think it was worth 25 dollars, and I expect to get value out of it at some point. Now, I can mention the 5 star review in the profile
description which needs to be revamped anyhow. I can also reference it in posts on our facebook page. The gestalt effect of having an additional
third-party link out there is likely to help support my efforts in marketing This to That.
</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com0tag:blogger.com,1999:blog-5003807027724289640.post-58881522591224589232014-04-19T09:26:00.000-07:002014-04-19T09:26:03.807-07:00How to Market a Mobile App: Design Counts<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3rNP25vYQG9CUIdFCXeZ6-IeQH76JzEqteHgpDZagHoNBdQ35ko6ACnw1CduFcv9AGdY3-6OW94MwGYeefDM-iHrXMUxPzDQEOX7TeduqV4cz7ZGO-8UKbe5SkaEUw71EQ9DvAZlfNaQ/s320/default_theme_progression_home.png" alt="Default theme progression" style="float: left; margin-right: 7px;" class="postimage" />
<p>
As I discussed previously in the <a href="http://dpatrickcaldwell.blogspot.com/2014/01/how-to-market-mobile-app-where-to-start.html">How to Market a
Mobile App</a> series, one of the first things I could do to improve user experience in <a href="https://itunes.apple.com/app/id499422300?mt=8">This
to That for iOS</a> is not only to support the look and feel of iOS 7 but also to improve the color selections of the default theme. At the time I
released the latest version of This to That, the iOS 6 SSL vulnerability hadn't been discovered yet, so I have a new default iOS 6.0 theme (that
nobody will ever see again). :)
</p>
<h2>Results</h2>
<p>
There were no notable changes in new user acquisitions nor in returning user frequency. There may have been a slight reduction in session time and a
slight increase in screen views per session. Reviews are still generally positive, but it appears changing the theme had very little impact on the
performance of the application
</p>
<h2>Next Steps</h2>
<p>
I had a new icon designed professionally. I made the current icon and while I think it's pretty cool, it definitely doesn't look quite as engaging as
the new one does. I need to re-release This to That with that icon in hopes that it increases new user acquisition.
</p>
<h2>Screenshots</h2>
<p>
Each set of screenshots shows the original theme on the left, the new default iOS 6 theme in the middle, and the new flat iOS 7 theme on the right. You
can still select the "reflective" tile type in the theme builder to get the old look and feel, but the linen background is no longer available to iOS 7
users. Instead, it's just gray.
</p>
<p>
Basically, the theme is lighter, flatter, and sharper. The last few images demonstrate the difference in texts. Instead of inlaid icons for the letters,
they're all just flat letters in a built in system font. Eventually, I think I'd like to switch the fonts throughout the application to use iOS 7's default
font.
</p>
<p style="text-align: center;">
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3rNP25vYQG9CUIdFCXeZ6-IeQH76JzEqteHgpDZagHoNBdQ35ko6ACnw1CduFcv9AGdY3-6OW94MwGYeefDM-iHrXMUxPzDQEOX7TeduqV4cz7ZGO-8UKbe5SkaEUw71EQ9DvAZlfNaQ/s600/default_theme_progression_home.png" alt="Default theme progression" />
</p>
<p style="text-align: center;">
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpsm7VxgPPMRs0uKHwRec-sFeAMY85ElBEwXkWrBiBp3YsFfbNAvZKgdlJxIXwVkWWkQezcKK574bEEPyZ5FcQ8aqdD1xym-NLN7OJb9Sc3YAOWGBetjR7HWm3B4Ka5Pvhw-3lT1A0sDY/s600/default_theme_progression_selector.png" alt="Letter selector theme progression" />
</p>
<p style="text-align: center;">
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEis18oCFDecZGj7kRRR5Si50hxHzy4kJ8ff_hxfE7AxG8hGJV_jxWzLG0-tBjls6ingar8G9TW38Hbp3uXQB2Zl2qCBvbirf6HTGDJuKqvTd4_vwZlpXchX4AyAOtGt7Bvhw3lYENQFcpQ/s600/default_theme_progression_complete.png" alt="Complete game theme progression" />
</p>
<p style="text-align: center;">
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrjq7dJ7FljWPPvwkEQKkR191SZS-R6AWmc4fdqeihYC_Bqm6qjB6yJyffXBJu77DM4X-qlTORziEhoZIKMGNu1hjT5qUSodhKv1r30HLUhFwZcVjNKj7i0cu18neI6gbuKmqJBhHvncM/s600/default_theme_progression_history.png" alt="History theme progression" />
</p>
<p style="text-align: center;">
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqR-9QMNl_2tkew1C-khDZqDMMZP6oc5-m_k0blLd5XRbvFefQyCDXkTxS1q6KeMOOXo1KEnY5BzghNIsRVQVxo2ZxUpJhIBmvgCUINB-yNto6_GPQMUG2NeWr82RuFRt45A7RuWoJiKg/s600/tile_letter_difference.png" alt="Game font changes" />
</p>
<p style="text-align: center;">
<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpBnjgd8edCIoi_nr4cYtObS9JsVw9eD04YvVbEmdVWjhGhlI9aEle5LUN64aekrNg6xVyK7Vp8wwA055c4j3YJ3gdPC6z8fUoHwC7sTKMPxta-XwtidFVrm9fkLYjUADD1UvssmIA9uQ/s600/tile_letter_selector_difference.png" alt="Letter selector font changes" />
</p>
D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com0tag:blogger.com,1999:blog-5003807027724289640.post-45784515864247658722014-03-15T08:06:00.002-07:002014-03-15T08:08:12.110-07:00A Limerick Standup: AngularJS, PhantomJS, and Jasmine<img src="http://upload.wikimedia.org/wikipedia/commons/1/10/Tux_Paint_clover.svg" alt="Logs" style="float: left; margin-right: 7px;" width="217" class="postimage" />
<p>
In honor of St. Patty's Day, I decided a limerick standup would be appropriate:
</p>
<pre>
I've made some good progress with <a href="http://www.angularjs.org">Angular</a>
Though nothing of mention particular
I have some good tests
That pass in <a href="http://phantomjs.org/">PhantomJS</a>
And if <a href="http://jasmine.github.io/2.0/introduction.html">Jasmine</a> were here, I would strangle 'er
</pre>
<div style="clear: both;"></div>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com1tag:blogger.com,1999:blog-5003807027724289640.post-54762431155729785542014-02-07T13:08:00.000-08:002014-02-07T13:41:29.996-08:00Sending Java Stack Traces to Loggly<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmbCO9zciFSCtPyBpZIeE_Z1Rg9psRyyMGKnDSFGjYkza-9LqFhKE-GtWJKhgnz58gywbTX4A4QLFl6oxN4jLezBxSLTSZgnN9e3-4tAPEojraCXYBQsYcQtH_NbEX-UPEaoxNtHvtQZk/s320/logs.png" alt="Logs" style="float: left; margin-right: 7px;" class="postimage" />
<p>
Recently at my new <a href="http://www.autopilotllc.com">Autopilot</a> contract, I was tasked with configuring some of our applications
to send log entries to <a href="https://www.loggly.com/">Loggly</a>. We're using <a href="http://logging.apache.org/log4j/1.2/">log4j</a>,
<a href="http://www.rsyslog.com/">rsyslog</a>, and <a href="http://syslog4j.org/">syslog4j</a>.
</p>
<p>
The Loggly service produces nice data which are easily accessible through a pleasant interface. The configuration was simple, logging
works from anything that can log with syslog, and Loggly support was generally responsive (it definitely helps to have a corporate
relationship).
</p>
<p>
Once we had the environment configured, we configured log4j following <a href="https://www.loggly.com/docs/java-log4j/">Loggly's Log4j
Setup</a> instructions. We were getting log messages into loggly in no time. The only problem was that we weren't getting stack
traces.
</p>
<p>
This was a little bit confusing because the setup document reads:
<blockquote>
You can send your Java logs using Log4j. The method shown supports multi-line events such as a Java stacktraces over Syslog.
</blockquote>
</p>
<p>
I dug through the my usual channels looking for someone else who had the same experience. All I was able to find really was this one
<a href="http://stackoverflow.com/questions/19710092/log4j-syslogappender-stack-trace-output-not-on-the-same-line/21268884#21268884">Stack
Overflow</a> question.
</p>
<p>
Not really being a Java guy, I assumed I was just missing something. In fact, the folks at Loggly told me I was missing something too. :)
I first started looking into the rsyslog documentation regarding multi-line log entries (like stack traces). I noticed that the
<a href="http://www.rsyslog.com/doc/imfile.html">file input module</a> supports a <b>ReadMode</b> attribute, but the
<a href="http://www.rsyslog.com/doc/imudp.html">udp input module</a> does not.
</p>
<p>
I added a <a href="http://logging.apache.org/log4j/extras/apidocs/org/apache/log4j/rolling/RollingFileAppender.html">rolling file
appender</a> to my log4j.properties file, I configured the imfile in our rsyslog config file, and we were off to the races. Good looking
easy to read stack traces. It's worth noting I'm using the <i>paragraph</i> ReadMode because not all lines in a Java stack trace are
indented. Thus, I needed a full blank line between log entries. This is more or less our log4j.properties configuration for the
rolling file appender:
</p>
<pre name="code" class="java">
log4j.appender.ROLLING=org.apache.log4j.rolling.RollingFileAppender
log4j.appender.ROLLING.File=/tmp/log4j.log
log4j.appender.ROLLING.rollingPolicy=org.apache.log4j.rolling.TimeBasedRollingPolicy
log4j.appender.ROLLING.rollingPolicy.FileNamePattern=/tmp/log4j.log.%d{yyyy-MM}.gz
log4j.appender.ROLLING.layout=org.apache.log4j.PatternLayout
log4j.appender.ROLLING.layout.ConversionPattern=%n%d{yyMMdd.HHmmss,SSS} %t %C{1}.%M %p: %m%n
</pre>
<p>
This was well and good but I wanted to make sure I wasn't missing a simpler alternative. The docs said it would work via rsyslog udp and
I wanted to know why I was having such trouble. I looked at the network traffic coming from my dev instances. I noticed that my messages didn't
include any stack trace information at all. It wasn't just a multiline issue really. It was that stack traces were never being logged
by syslog4j. I looked into the ConsoleAppender and found that it gets its append behavior from
<a href="http://grepcode.com/file/repo1.maven.org/maven2/log4j/log4j/1.2.17/org/apache/log4j/WriterAppender.java#WriterAppender.subAppend%28org.apache.log4j.spi.LoggingEvent%29">WriterAppender.subAppend</a>.
That method is how the console gets stack traces after the message has been written using the layout.
</p>
<pre name="code" class="java">if(layout.ignoresThrowable()) {
String[] s = event.getThrowableStrRep();
if (s != null) {
int len = s.length;
for(int i = 0; i < len; i++) {
this.qw.write(s[i]);
this.qw.write(Layout.LINE_SEP);
}
}
}</pre>
<p>
I started looking at the <a href="http://grepcode.com/file/repo1.maven.org/maven2/org.syslog4j/syslog4j/0.9.30/org/productivity/java/syslog4j/impl/log4j/Syslog4jAppenderSkeleton.java#Syslog4jAppenderSkeleton.append%28org.apache.log4j.spi.LoggingEvent%29">Syslog4jAppenderSkeleton.append</a> method.
I noticed that this appender never logs stack traces at all.
</p>
<pre name="code" class="java">protected void append(LoggingEvent event) {
if (!this.initialized) {
_initialize();
}
if (this.initialized) {
int level = event.getLevel().getSyslogEquivalent();
if (this.layout != null) {
String message = this.layout.format(event);
this.syslog.log(level,message);
} else {
String message = event.getRenderedMessage();
this.syslog.log(level,message);
}
}
}</pre>
<p>
It was no wonder I wasn't getting stack traces into loggly. I looked back at the ConsoleAppender and wondered what the layout.ignoresThrowable
was. I looked at the docs and found that the
<a href="http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html#ignoresThrowable()">PatternLayout.ignoresThrowable</a>
method always returns true because it can't handle throwables. In fact, the same page reads:
<blockquote>
A flexible layout configurable with pattern string. This code is known to have synchronization and other issues which are not present in
org.apache.log4j.EnhancedPatternLayout. EnhancedPatternLayout should be used in preference to PatternLayout. EnhancedPatternLayout is distributed
in the log4j extras companion.
</blockquote>
</p>
<p>
Easily convinced, I switched to the <a href="https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/EnhancedPatternLayout.html">EnhancedPatternLayout</a>.
I configured the ConsoleAppender to use the enhanced pattern layout and I added the %throwable configuration item to it. Viola! Stack traces . . . by
my own doing. I copied the configuration to the Syslog4jAppender section, started the long deployment process, and watched in disappointment as I still had
no stack traces.
<p>
<p>
I started messing with the layout on my Syslog4j appender and noticed nothing I did changed the resulting log message. That seemed a little odd, so I dug into that
a little. I dug back into the <a href="http://grepcode.com/file/repo1.maven.org/maven2/org.syslog4j/syslog4j/0.9.30/org/productivity/java/syslog4j/impl/log4j/Syslog4jAppenderSkeleton.java#Syslog4jAppenderSkeleton.append%28org.apache.log4j.spi.LoggingEvent%29">Syslog4jAppenderSkeleton.append</a> again. I set a breakpoint on this.layout.format(event); and attached the debugger. My breakpoint
was never hit. The layout was always null!
</p>
<p>
I set out in search of where that field was getting set. We're using the log4j.properties file so our configuration is parsed by the <a href="http://grepcode.com/file/repo1.maven.org/maven2/log4j/log4j/1.2.17/org/apache/log4j/PropertyConfigurator.java#PropertyConfigurator.parseAppender%28java.util.Properties%2Cjava.lang.String%29">PropertyConfigurator parseAppender</a> method.
This is a pretty big method but here's what I noticed:
</p>
<pre name="code" class="java">if(appender.requiresLayout()) {
Layout layout = (Layout) OptionConverter.instantiateByKey(props, layoutPrefix, Layout.class, null);
if(layout != null) {
appender.setLayout(layout);
LogLog.debug("Parsing layout options for \"" + appenderName +"\".");
//configureOptionHandler(layout, layoutPrefix + ".", props);
PropertySetter.setProperties(layout, props, layoutPrefix + ".");
LogLog.debug("End of parsing for \"" + appenderName +"\".");
}
}</pre>
<p>
So again I looked at the implementation of Syslog4JAppenderSkeleton and found that <a href="http://grepcode.com/file/repo1.maven.org/maven2/org.syslog4j/syslog4j/0.9.30/org/productivity/java/syslog4j/impl/log4j/Syslog4jAppenderSkeleton.java#Syslog4jAppenderSkeleton.requiresLayout%28%29">requiresLayout</a>
always returns false!
</p>
<pre name="code" class="java">public boolean requiresLayout() {
return false;
}</pre>
<p>
This struck me as odd and it made me wonder how anyone ever was able to use the layout property with syslog4j. It turns out that
<a href="http://grepcode.com/file/repo1.maven.org/maven2/log4j/log4j/1.2.17/org/apache/log4j/xml/DOMConfigurator.java#DOMConfigurator.parseAppender%28org.w3c.dom.Element%29">DOMConfigurator.parseAppender</a>
ignores requiresLayout and sets the layout any time there's a layout tag in the log4j.xml configuration file.
</p>
<pre name="code" class="java">// Set appender layout
else if (currentElement.getTagName().equals(LAYOUT_TAG)) {
appender.setLayout(parseLayout(currentElement));
}</pre>
<p>
Having a fundamental opposition to switching to XML, I needed another way to get the Syslog4jAppender to respect my layout desires. To my
chagrin, the best approach I could come up with was to subclass the Syslog4jAppender and override the requiresLayout method:
</p>
<pre name="code" class="java">public class Syslog4jLayoutAppender extends Syslog4jAppender {
@Override
public boolean requiresLayout() {
return true;
}
}</pre>
<p>
I switched my configuration to use this appender, redeployed, and my stack traces started to appear in loggly. Unfortunately, they were all on one line
with character codes in place of linefeeds. Preferring easy to read stack traces, I flipped the rolling file appender back on and considered this the
best alternative. In short, here's how I get stack traces into loggly:
</p>
<ol>
<li>Configure a RollingFileAppender</li>
<li>Configure rsyslog to use the imfile module to monitor the file with paragraph read mode</li>
<li>Profit</li>
</ol>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com3tag:blogger.com,1999:blog-5003807027724289640.post-50559786139108378232014-01-25T11:30:00.000-08:002014-01-25T11:32:47.631-08:00How to Market a Mobile App: Knowledge is Power<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyVIRcqNohDCXhruFoAUkfAqtLWBnL-FgGqARBHVwjhzzt3L3SAg_Hdx8EFcoy7693owlmPv_zehF1j52usQsmmv9uYbpqbXPiJQee1LMW50VbnDbftAWDhSYg9jFkzUkjBsZx5IFzKBg/s320/data_at_computer.png" alt="Data!!" style="float: left; margin-right: 7px;" class="postimage" />
<p>
If you're going to understand how changes you make to your application affect your users or how effective your marketing strategy is,
you're going to need data. All of the app markets provide decent data about downloads, active users, ad impressions, etc., but these
are no match for deliberate data collection and usage statistics.
</p>
<p>
In the early days of <a href="https://itunes.apple.com/app/id499422300?mt=8">This to That for iOS</a>, we didn't include any
analytics. We relied heavily on iTunesConnect to provide the statistics we needed to determine the effects of our changes.
<a href="https://play.google.com/store">Google Play</a> provides much better analytics out of the box including active installs,
device statistics, OS versions, etc., so we had to make a lot more inferences.
</p>
<p>
For example, we could see daily downloads but we had no idea how many users we were retaining. At that point, we were releasing
often so we could compare our download statistics to our upgrade statistics to get a feel for that. We further refined our estimates
when we added Game Center support. Comparing the new user statistics to the Game Center names I didn't recognize (seriously), I
estimated about half of the users use Game Center. Thus, we could infer that for every player who reported a score to the leaderboard,
there was a roughly equivalent user without a Game Center account.
</p>
<p>
We also planned to be ad supported out of the gate, so we were able to get some usage information from ad impressions. Our app has
a roughly 98% fill rate and filled ads cycle about every 3 minutes. Combining that information with estimated users per day, we could
guess how much time people spent playing our game.
</p>
<p>
Several versions back, we started using <a href="http://www.google.com/analytics/">Google Analytics</a> to collect anonymous (we're
not the NSA after all) usage statistics. Now we get a much more detailed view of our users and how they play This to That. I recommend
developers consider analytics to be an inextricable part of any <a href="http://en.wikipedia.org/wiki/Minimum_viable_product">minimum
viable product</a>. Without analytics, there's no way I could write the
<a href="http://dpatrickcaldwell.blogspot.com/2014/01/how-to-market-mobile-app-where-to-start.html">How to Market a Mobile App</a> series
as I'd have no way of knowing how these experiments impact our application's performance in the app market.
</p>
<h2>Starting Point as of Version 4.2</h2>
<p>
<ul>
<li>910 sessions lasting an average 3.5 minutes per month</li>
<li>108 of these sessions are new users (about half are iOS 7)</li>
<li>About 80% of the sessions end on the first screen and last less than 10 seconds</li>
<li>About half of the users play at least 10 times per month</li>
<li>About 90% of users play This to That at least every other day</li>
</ul>
</p>
<p>
I think these statistics tell us a few meaningful things about This to That. The first and I think most obvious inference, which is
the reason I'm writing this series (and is likely the reason you're reading it): people can't find and don't hear about This to That.
To resolve this, we've added social features, game center, multiplayer gameplay, and have tried advertising. As of yet, to no avail.
</p>
<p>
Second, I think, is that many users never get past the first screen. I think that's likely because the look and feel of the default
game theme may not be appealing (I'm not a designer after all). iOS 7 users are less likely to engage than iOS 6 users; it might be
worth updating the default look and feel of the application for iOS 7 users.
</p>
<p>
I think there are some positive observations in these data too. Users who play the game tend to play it often (at least for a while).
They tend to play several times a day for short periods. This gives me insight that the game should be quick to get into. Perhaps,
for example, the game should start in the game screen and have a menu option for going to what's now the home screen.
</p>
<h2>Things to do in Version 5.0</h2>
<p>
<ul>
<li>Create an iOS 7 default theme</li>
<li>Clean up the default color scheme for all new users</li>
<li>Make a better app description for iTunes</li>
<li>Make more compelling graphics for the images in iTunes</li>
<li>Clean up the icon</li>
</ul>
</p>
<p>
I'm hoping we'll see a larger number of new users by having more compelling marketing in our
<a href="https://itunes.apple.com/app/id499422300?mt=8">This to That iTunes profile</a>. I'm also hoping that the improved icon and
cleaner default theme will make new users more likely to complete their first game (most users who complete the first game complete
many subsequent games). I'm hoping having more users and retaining a higher percentage of them will also result in a higher likelihood
that users will share the game with friends.
</p>
<p>
There was a slight drop in retention after we released the multiplayer version of This to That. I think that users were frustrated
when they tried to connect to a multiplayer game, but nobody else was playing. That frustration overpowered the interest in playing
the original single player version. When we released that version, we were hoping it would encourage users to invite friends and
family to play. Ideally, the new design and higher retention rate would also result in more available online users for random game
center match creation.
</p>
<h2>What's it Going to Take?</h2>
<p>
Well, one thing I like to do as an independent developer is work on the cheap. I think that I've managed, on my own, to make a fun
and entertaining application; however, I admit it lacks a certain je ne sais quoi. Perhaps it lacks the polish of the kinds of games
people make when they have designers on staff. Perhaps it feels unprofessional. Of all of the negative feedback I've ever gotten,
I don't think anyone has said, "the game isn't fun." So, I'm going to have some artwork done professionally. The game, in its current
state (with the extensive theming) would be difficult to overhaul, so I'm going to focus on 3 components: the icon, the screenshots in
iTunes, and the default theme. I'm hoping to get all of this done for about $500 - $750. I have a few quotes in and I think this is a
reasonable expectation.
</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com3tag:blogger.com,1999:blog-5003807027724289640.post-30435061093563077362014-01-24T19:22:00.000-08:002014-01-25T11:37:32.313-08:00How to Market a Mobile App: Where to Start<img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQuctEfoiqMhMKKajil4xCQDZRgmILsVI88o1lYZl-56Ko8iQSYkMqbEE5vYTLT3m1EoAPA2RnCtwkgSvoqKgIeC-dYN6YfVUePGzntHlt91XYhPd0LeebYgEnJIDGfP9U8lVUB5Nr6kg/s320/confused_emoticon.png" alt="confused emoticon O.o" style="float: left; margin-right: 7px;" class="postimage" />
<p>
<a href="http://mobilemagicdevelopers.com/">Mobile Magic Developers</a> has several applications on various
mobile marketplaces now. My 2 year old daughter loves
<a href="http://mobilemagicdevelopers.com/kids/cow_says_moo">Cow Says Moo</a>. My mother in law has been playing
<a href="https://itunes.apple.com/app/id499422300?mt=8">This to That</a> for years. Our friends get goodnatured
laughs with <a href="https://itunes.apple.com/app/id599850381?mt=8">Said Obama</a>. All in all, developing mobile
applications has been a really positive experience.
</p>
<p>
In graduate school marketing classes, we spent a lot of energy discussing myriad ways large organization with
massive marketing budgets could broaden their customer bases. I'm sure that information is still somehow useful
but it surely doesn't help independent developers like Mobile Magic Developers who don't have a marketing budget,
let alone enough to hire an MBA marketing consultant. In fact, our marketing campaigns like the rest of our
expenses, are still being paid out of our pockets.
</p>
<p>
So, where do you start on the quest to release a popular mobile application?
</p>
<p>
<h2>I Have No Clue <span style="float: right; text-transform: none;">O.o</span></h2>
I sincerely have no idea. I've read a lot of good blog posts about marketing mobile applications. I've purchase
some really interesting books on the topic. My brother has a BA in marketing. I did get some marketing exposure
in college. Despite all of this, I still have some really great apps nobody uses.
</p>
<p>
That's why I'm starting the <a href="http://dpatrickcaldwell.blogspot.com/search/label/How%20to%20Market%20a%20Mobile%20App">How
to Market a Mobile App</a> series (well, that and I haven't blogged in almost 2 years and it's high
time I get back to it). I'm going to start by introducing, with complete candor, where we are with
<a href="https://itunes.apple.com/app/id499422300?mt=8">This to That for iOS</a> today and how we spent the
last few years getting to what is effectively a good starting point.
</p>
<p>
I have a new plan for spreading the word about This to That with a limited budget over the next few
months. I'll blog about every strategy I try and how each idea fails and succeeds (including all of the nitty-gritty
and sometimes embarrassing details). Every time I add a new post, I'll include it in the index below so bookmark this
page for a convenient starting point.
</p>
<p>
<h2>Worst Case Scenario? <span style="float: right;">:*(</span></h2>
Well, as I've shared, I don't really know much about successfully marketing a mobile application, but I
have learned a handful of things not to do. That tells me that the worst case scenario of writing this
series both for me the writer and you the reader (and subscriber hopefully) is that together we learn a
few more things not to do. But, who knows? Perhaps we'll figure out the right formula for getting our
wonderful apps into the hands of the target audience who can get the most value out of them.
</p>
<h2>How to Successfully Market a Mobile Application</h2><ul id="labelPosts"><li><a href="http://dpatrickcaldwell.blogspot.com/search/label/How%20to%20Market%20a%20Mobile%20App">All posts in this series</a></li>
</ul><script type="text/javascript">
function showFeedOnLoad(json) {
$(function(){
$labelPosts = $("#labelPosts");
$.each(json.feed.entry, function(i, o) {
$labelPosts.prepend(getFeedListItem(o));
});
});
}
function getFeedListItem(entry) {
var a = $("<a />").attr("href", findPostLink(entry.link)).append(entry.title.$t);
var li = $("<li />").append(a);
return li;
}
function findPostLink(links) {
for (var i = 0; i < links.length; i++)
if (links[i].rel == "alternate")
return links[i].href;
}
</script>
<script type="text/javascript" src="http://dpatrickcaldwell.blogspot.com/feeds/posts/default/-/How%20to%20Market%20a%20Mobile%20App?alt=json-in-script&callback=showFeedOnLoad"></script> D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com2tag:blogger.com,1999:blog-5003807027724289640.post-42725634670731354762012-03-21T06:43:00.000-07:002012-03-21T06:43:05.026-07:00First Small Business, Big Difference for the NBCF a Success!<a href="http://www.stayclassy.org/fundraise?fcid=184241"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitAIimvlKWLwlXlu-93ChJlMDnj1Sp1YfR1929jT_hNLkmdBQxgQUncJ8cHukY3RpveRx8p6yd8NusJC4TD1bfbQGi1HDmiaTDM6_aTimMQfUoidhEMljCOFpoJrAPa5N5i8x1vSsQtiw/s320/SBBD%2520Banner%2520Ad.png" alt="National Breast Cancer Foundation Banner" style="float: left; margin-right: 7px;" class="postimage" /></a>
<p>If you followed our <a href="http://dpatrickcaldwell.blogspot.com/search/label/Small%20Business-Big%20Difference">Small Business, Big Difference</a> program, you know that our first Small Business, Big Difference to support the <a href="http://nbcf.org">National Breast Cancer Foundation</a> ended Februrary 18th, 2012.</p>
<h2>Thus, there are two pressing topics:</h2>
<p>First, I'd like to apologize for taking this long to get these results out there. I know it's been a while, but I've been a little busy. On February 17th, 2012 my wife delivered our first child and we've been enjoying some family time.</p>
<p>Second, when we set out to start the Small Business, Big Difference program, we knew it'd take a few tries for it to catch on. We figured our first attempt would produce modest results; however, we were so excited that we were able to generate such interest in the program that we rounded our donation up to <b>$100.00</b>.</p>
<p>We'd like to thank everyone who donated or participated in the program and we'd like to thank the NBCF for letting us raise funds for them with the Small Business, Big Difference program. We're looking forward to working with them again in a future promotion.</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com1tag:blogger.com,1999:blog-5003807027724289640.post-24223544269621483372012-02-10T17:17:00.000-08:002012-04-16T05:01:17.235-07:00JavaScript for Photoshop - When Macros Aren't Enough<a href="http://mobilemagicdevelopers.com/#this2that"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjIm_ikq-gI9zavJTp9j4gNblpiX-9oo0FzCJ4iceFd5zsw8abyWh6nllZUboVmGCz4XSU2BsITr7A6p5_uYe94bKveMYEMbSqbzAT9LMOURIX6M1YcNukcgBf9cbgMGXJ4IIkUKeS_mqw/s288/This2That%20Tile%20Image%20For%20BlogPost.png" alt="This2That Tile Sample" style="float: left; margin-right: 7px;" class="postimage" /></a>
<p>A few years ago, a fellow named <a href="http://www.cs.hartford.edu/~dorn/">Brian Dorn</a> got in touch with me. He was working on his doctoral dissertation at Georgia Tech and he needed participants. I met him in Atlanta and found out that his study was about scripting with Photoshop. That was the first I'd ever heard of <a href="http://www.adobe.com/devnet/photoshop/scripting.html">Adobe Photoshop Scripting</a>.</p>
<p>A few weeks ago when I started working on a new word game <a href="http://mobilemagicdevelopers.com/games/this2that">This2That</a> for Mobile Magic Developers, I needed to generate 38 tiles with letters, numbers, and punctuation. The task was tedious and the macros just couldn't make it any easier. Then I realized I had the embossing all wrong and I had to start over!</p>
<p>Frustrated, I tried to think of a better way. I remembered Brian Dorn and I started looking into this Adobe Photoshop JavaScript thing. I was very pleased when 15 minutes of script became an easily reusable tile generating utility. Now, I can take any PSD, open it up, select any text layer, and have the script generate a PNG for each letter I need</p>
<p>I thought this little known feature would make for interesting reading for both programmers and designers so here's my script:
<pre name="code" class="js">
// call the method that does all of the work
main();
// wrap the code in a method to make it easier to debug
function main() {
// make sure you're working in a document and have a text layer selected
if (!activeDocument || !activeDocument.activeLayer || activeDocument.activeLayer.kind != LayerKind.TEXT)
{
alert("Please select a document and a target text layer.");
return;
}
// set up some information about the current file
var textLayer = activeDocument.activeLayer;
var path = activeDocument.path;
var fileName = activeDocument.name;
// remove the extension on the file name
var extensionPosition;
if (extensionPosition = fileName.lastIndexOf('.'))
fileName = fileName.substr(0, extensionPosition);
// get a good place to put the file
var outputFolder = Folder.selectDialog("Select a target folder.", path);
// set up the letters we want images for
var characterMap = [
["question", "?"]
];
// and add the lowercase alphabet and numbers
characterMap = characterMap.concat(getAsciiRange(97, 26), getAsciiRange(48, 10));
// for each character, update the selected text layer and save a file
for (var i = 0; i < characterMap.length; i++)
{
var character = characterMap[i][1];
var fileSuffix = characterMap[i][0];
textLayer.textItem.contents = character;
var file = new File(outputFolder + "/" + fileName + "_" + fileSuffix + ".png");
var options = new PNGSaveOptions();
options.interlaced = false;
activeDocument.saveAs(file, options, true, Extension.LOWERCASE);
}
};
// a little helper method to make a range of letters and their
// filename extensions
function getAsciiRange(from, count) {
var result = [];
for (var i = 0; i < count; i++)
{
var character = String.fromCharCode(i + from);
result.push([character, character.toUpperCase()]);
}
return result;
}
</pre>
</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com0tag:blogger.com,1999:blog-5003807027724289640.post-61278831582973252522012-02-08T15:32:00.000-08:002012-02-08T15:33:52.666-08:00Small Business, Big Difference - NBCF Approved<a href="http://www.stayclassy.org/fundraise?fcid=184241"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitAIimvlKWLwlXlu-93ChJlMDnj1Sp1YfR1929jT_hNLkmdBQxgQUncJ8cHukY3RpveRx8p6yd8NusJC4TD1bfbQGi1HDmiaTDM6_aTimMQfUoidhEMljCOFpoJrAPa5N5i8x1vSsQtiw/s320/SBBD%2520Banner%2520Ad.png" alt="National Breast Cancer Foundation Banner" style="float: left; margin-right: 7px;" class="postimage" /></a>
<p>Last week I wrote a <a href="http://dpatrickcaldwell.blogspot.com/2012/02/national-breast-cancer-foundation-small.html">blog post</a> introducing our <a href="http://dpatrickcaldwell.blogspot.com/search/label/Small%20Business-Big%20Difference">Small Business, Big Difference</a> program to raise money for the <a href="http://nbcf.org">National Breast Cancer Foundation</a>. This week, I got in touch with them and they gave us the go-ahead to run the fundraiser. We're very excited about the news!</p>
<p>So, we want to make sure you know how you can help! There are three ways that you can help us raise money to give to the NBCF.
<h3>Spread the Word</h3>
<ul>
<li>Tell people about this fundraiser and encourage them to do the same.</li>
<li>
Share the link to the <a href="http://dpatrickcaldwell.blogspot.com/search/label/National%20Breast%20Cancer%20Foundation">Small Business,
Big Difference - National Breast Cancer Foundation</a> fundraiser.
</li>
<li>
Like our <a href="http://www.facebook.com/pages/Mobile-Magic-Developers/269802299710003">Mobile Magic Developers</a> page
on <a href="http://www.facebook.com">Facebook</a>.
</li>
<li>Follow <a href="http://twitter.com/mobilemagicdevs">@MobileMagicDevs</a> on <a href="http://www.twitter.com">twitter</a>.</li>
</ul>
<br />
<h3>Get the App</h3>
<ul>
<li>Download the <a href="http://market.android.com/details?id=com.wtfware.janeausten">Jane Austen Quote of the Day Widget</a> application.</li>
<li>
Enjoy the application! We'll donate all ad revenue from the Jane Austen widget during the promotion period of <b>Sunday,
February 12th, 2012</b> and ending <b>Saturday, February 18th, 2012</b>.
</li>
<li>
If you love the app, buy the <a href="https://market.android.com/details?id=com.wtfware.janeaustenupgrade">Jane Austen Widget Upgrade</a>
for less than a buck and we'll donate that too!
</li>
</ul>
<br />
<h3>Want to Give More?</h3>
You're more than welcome to give as much as you want! You can visit the <a href="http://www.stayclassy.org/fundraise?fcid=184241">Mobile Magic Developer's Small Business, Big Difference for NBCF</a> fundraising site powered by <a href="http://stayclassy.org">StayClassy</a>.
</p>
<p>If you have any questions at all, please feel free to contact us through our facebook page or twitter account. We'd love to address any concerns you may have.</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com0tag:blogger.com,1999:blog-5003807027724289640.post-25326626636253738262012-02-04T07:14:00.000-08:002012-02-08T14:45:46.960-08:00National Breast Cancer Foundation - Small Business, Big Difference<a href="http://nbcf.org/"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh9jidrvB9u-L1p-3iJ4GjPa_nWSDoL9Ab-ln2EzYHj93awHgas2DY0Ln88bQRAwCOUxm11TtQBTX7qhiP8sak8LB7EWymhxrDoZDF5i1o3yAj-3hYLudKKbyvNmXt5FT8tHv-yQPl29Tg/s220/220x220%20HOPE%20Pink.jpg" alt="National Breast Cancer Foundation Banner" style="float: left; margin-right: 7px;" class="postimage" /></a>
<p>James and I recognize that we get a great deal of benefit out of writing mobile applications with <a href="http://www.mobilemagicdevelopers.com">Mobile Magic Developers</a>. We enjoy the time together, the camaraderie, the learning experiences, and the opportunity to bring a little joy to a much larger audience. Along the way we've had a lot of support from friends, family, and the numerous customers who've downloaded our apps.</p>
<p>We wanted to find a way to give something back as an enthusiastic "Thank You" from the Mobile Magic Developers team to you. We've decided we're going to try something we call <b><a href="http://dpatrickcaldwell.blogspot.com/search/label/Small%20Business-Big%20Difference">Small Business, Big Difference</a></b>. Periodically, we're going to choose one of our applications and for a period of time, all proceeds from that application will be donated to a charitable organization. That includes sales and ad revenue.</p>
<p>For our inaugural Small Business, Big Difference attempt, we've decided that we'll donate all revenue generated by our <a href="http://mobilemagicdevelopers.com/#jane_austen_quotes">Jane Austen Quote of the Day Widget</a> available on the <a href="https://market.android.com/details?id=com.wtfware.janeausten">Android market</a> to the <a href="http://www.nationalbreastcancer.org/">National Breast Cancer Foundation</a>. We've even added a new background for your widget just for this occasion!</p>
<p><h3>Why the National Breast Cancer Foundation</h3>
We reviewed dozens of charitable organizations dedicated to promoting breast cancer research, helping breast cancer survivors, and comforting those fighting with breast cancer today. We considered each organization's rating on <a href="http://www.charitynavigator.com">Charity Navigator</a> and the <a href="http://www.charitynavigator.org/index.cfm?bay=search.summary&orgid=8776">NBCF scores on Charity Navigator</a> were remarkable including a maximum score for accountability and transparency, 83.1% of revenue dedicated to program expenses, and a solid plan for growth.
</p>
<p>We also felt strongly for the foundation's mission to save lives through early detection by providing education and mammography, particularly to women in need. The NBCF works with almost 100 clinics in all 50 states to provide free mammograms and diagnostic services to underserved women. Each sponsored facility is required to have the capacity to provide continued treatment to women after an abnormality is found or breast cancer is diagnosed.</p>
<p><h3>How You Can Help</h3>
Your role is easy. Download the <a href="https://market.android.com/details?id=com.wtfware.janeausten">Jane Austen Widget</a> from the Android market, use the "Share" buttons to share your quotes on Twitter and Facebook, encourage your friends to get the app. If you really like it, purchase the <a href="https://market.android.com/details?id=com.wtfware.janeaustenupgrade">Jane Austen Widget Upgrade</a>, the ads will go away and we'll donate the proceeds from your upgrade too.</p>
<p>The program will run for 1 week starting <b>Sunday, February 12th, 2012</b> and ending <b>Saturday, February 18th, 2012</b>. Download the free application today and help us spread the word about the application and the promotion. The more people who have the free app, the more ad revenue we'll be able to donate. Don't buy the upgrade though until the week of Valentines Day so we can donate your upgrade too.</p>
<p>We hope to make this program a huge success because we're already looking into our next charity (hopefully promoting children's literacy with proceeds from our <a href="http://mobilemagicdevelopers.com/#kids">educational apps for kids</a>). We're excited about this opportunity and with your help we can prove what a big difference a small business can make.</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com2tag:blogger.com,1999:blog-5003807027724289640.post-54208345098076791172011-11-26T12:29:00.001-08:002011-11-26T13:45:45.917-08:00Mobile Magic Developers Co-founded by Autopilot Founder<a href="http://mobilemagicdevelopers.com"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgo-evZ6ncY6IT9_AvlBr5G7zszDcM-G11Q01mXh7_Rb2UlvvR8oB62Hr2gKxkfbdCtLG0KfhE7I7UYEbNppW_dYjHQ0DLi9PE3meY_TPb1SwRsdaFLy_se6vIt-5svGnEO19o7R3m0AxY/s220/Mobile%252520Magic%252520Developers.png" alt="Mobile Magic Developers" style="float: left; margin-right: 7px;" class="postimage" /></a>
<p>
<a href="http://autopilotllc.com">Autopilot Consulting, LLC</a> founder Patrick Caldwell has joined forces with long time friend and coworker James Brechtel to found <a href="http://mobilemagicdevelopers.com">Mobile Magic Developers, LLC</a>.
</p>
<p>
James and I worked together for a few years several years ago and have been close friends ever since. It was an easy decision to get together and start developing mobile applications.
</p>
<p>
Mobile Magic Developers focuses on developing fun and exciting mobile products like <a href="http://mobilemagicdevelopers.com/#kids">educational mobile applications for kids</a>, <a href="http://mobilemagicdevelopers.com/#widgets">amusing home screen widgets</a>, and <a href="http://mobilemagicdevelopers.com/#utilities">mobile utilities</a>. Autopilot Consulting focuses on … well consulting.
</p>
<p>
When your company is ready for your own custom mobile application, come talk to <a href="http://autopilotllc.com/services.html#mobile_applications">Autopilot</a> about how we can help you wow your customers and outshine your competitors with a mobile application of your own. We'll apply our mobile experience as well as the talents of Mobile Magic to make sure you get the biggest mobile bang for your buck.
</p>
<p>
Mobile applications come in several varieties. Whether you're interested in native mobile applications or mobile skins for your web applications, we have the experience to get you there, save you money, and help you make the best decisions for your business, your customer, and your future.
</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com0tag:blogger.com,1999:blog-5003807027724289640.post-55740139152726854432011-11-16T05:35:00.001-08:002011-11-16T19:26:11.138-08:00Mainframe Migrations: Source Code Translation Tools<a href="http://en.wikipedia.org/wiki/Mainframe_computer"><img src="http://upload.wikimedia.org/wikipedia/commons/4/49/Ibm704.gif" alt="Mainframe" style="float: left; margin-right: 7px;" class="postimage" /></a>
<p>I was working on a mainframe migration project a while back to move an application from <a href="http://en.wikipedia.org/wiki/ADABAS">Adabas</a> and <a href="http://en.wikipedia.org/wiki/NATURAL">Natural</a> to Sql Server and C#. <a href="http://www.softwareag.com/corporate/default.asp">Software AG</a> developed Adabas and Natural back in the 70's so I was very pleased to have some Software AG developers and managers on the team.</p>
<p>Shortly into the project, one of the managers recommended a product Software AG has that translates Natural code into C#. Much to the surprise of my teammate, I recommended strongly against the software. In fact, if you've read any of my other <a href="http://dpatrickcaldwell.blogspot.com/2010/06/mainframe-migrations-tips-tricks-and.html">mainframe migration</a> articles, you know I was recommending against a "migration" strategy altogether (a lift-and-shift my teammate called it) vs. a guided redevelopment strategy.</p>
<p>In the process of making my argument, I spent a fair amount of time positing that a source code translation tool is very little more than hiring a bad programmer because he's fast and cheap. The customer did decide not to use the translation tool, but in the process of making my argument, my curiosity was piqued (and I do have something of a strong research background) so I put together the <a href="http://dpatrickcaldwell.blogspot.com/2010/09/what-really-makes-good-programmer.html">What Really Makes a Good Programmer Survey</a>.
<br /><span style="font-size: 10px; font-style: itallic; color: #666;">For an in depth discussion of the results, see my <a href="http://dpatrickcaldwell.blogspot.com/2010/10/results-of-what-really-makes-good.html">Results of the "What Really Makes a Good Programmer" Survey</a> post.</span></p>
<p>For this post, we'll be looking primarily at these data:
<script type="text/javascript" src="//ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js"> {"chartType":"LineChart","chartName":"What Really Makes a Good Programmer?","dataSourceUrl":"//spreadsheets.google.com/tq?key=0At0EPL2ZBHgPdDN3RVJYLVJ2dXpoS3FTdDNXRVc4RVE&range=A31%3AC48&gid=2&transpose=0&headers=1&pub=1","options":{"hAxis":{"showTextEvery":1,slantedText:true,slantedTextAngle:50},
"reverseCategories":true,"pointSize":"2","is3D":true,"logScale":false,"wmode":"opaque","title":"What Really Makes a Good Programmer?","height":500,"pointSizeOther":"2","mapType":"hybrid","isStacked":false,"showTip":true,"displayAnnotations":true,"nonGeoMapColors":["#ff9900","#0000ff","#FF9900","#109618","#990099","#0099C6","#DD4477","#66AA00","#B82E2E","#316395"],"dataMode":"markers","titleY":"Importance","maxAlternation":1,"colors":["#ff9900","#073763","#FF9900","#109618","#990099","#0099C6","#DD4477","#66AA00","#B82E2E","#316395"],"smoothLine":true,"width":700,"lineWidth":"2","labelPosition":"right","hasLabelsColumn":true,"legend":"right","allowCollapse":false,"reverseAxis":true},"packages":"corechart","refreshInterval":5} </script>
<br />If you can't view this chart, I managed to keep a copy of the <a href="https://spreadsheets.google.com/oimg?key=0At0EPL2ZBHgPdDN3RVJYLVJ2dXpoS3FTdDNXRVc4RVE&oid=4&zx=t7na7t-4scqjd">What Really Makes a Good Programmer Chart</a> from the legacy charts API.</p>
<p>Because most of my respondents were programmers, I like to look at the group averages to eliminate selection bias. I group the "traits" into the following three categories:
<ul>
<li>
Traits that contribute to being a good programmer
<ol>
<li>Has good problem solving skills</li>
<li>Learns from experience</li>
<li>Interested in learning</li>
<li>Passionate</li>
<li>Sees the big picture</li>
<li>Recognizes patterns</li>
<li>Communicates effectively</li>
<li>Tries new approaches</li>
<li>Detail oriented</li>
<li>Seeks help when needed</li>
<li>Interested in helping others</li>
</ol>
</li>
<li>
Traits that are nice to have if you can get them
<ol>
<li>Fast</li>
<li>Co-located</li>
</ol>
</li>
<li>
Traits with no effect on programmer quality
<ol>
<li>Has a college degree</li>
<li>Has a computer science degree</li>
<li>Cheap</li>
<li>Has certifications</li>
</ol>
</li>
</ul>
</p>
<p>So, back to my original stipulation that "a source code translation tool is very little more than hiring a bad programmer because he's fast and cheap." While I still believe that statement to be true, when I conducted my survey, I asked the question in a more positive way so I can't actually claim that the tool is a bad programmer. I believe that it is analogous to a bad programmer; I just can't support that claim with these data. I'll explain why, but first, for the pedants, statements like "the community represented in my responses appears to believe that having good problem solving skills contributes to being a good programmer" will be abbreviated to "having good problem solving skills contributes to being a good programmer."</p>
<p>That being said, the data suggest that having good problem solving skills contributes to being a good programmer. If that stipulation is true, then the contrapositive must also be true that not being a good programmer means not having good problem solving skills. The inverse, however, is not necessarily true so I cannot claim that not having good problem solving skills contributes to not being a good programmer. I'll let you decide on the validity of that statement, but for the purposes of this post, I'll change my argument to, "using a source code translation tool is little more than hiring a programmer, who lacks the traits of a good programmer, simply because he's fast and cheap."</p>
<p>Think of a translation tool and go through the list of traits. The translation tool does not have problem solving skills at all, can't learn from experience and certainly isn't interested in doing so, has no passion, and doesn't even know what a big picture is let alone the big picture with your application in your environment. It may be able to recognize patterns technically, but it likely won't recognize patterns that are meaningful to your application. It's also unlikely that the tool will communicate effectively, create and try new approaches, or be interested in helping others.</p>
<p>I suppose you could say it's detail oriented (as long as the details are explicit and don't require problem solving) and it seeks help when needed (likely by way of errors). It's probably faster than a good programmer, though this may be negated if it's lack of the aforementioned traits produces less than useful results; that is to say as long as you don't need a programmer to fix the translation once it's finished the speed may be beneficial. Another good thing about the tool is that it's possibly cheaper than a good programmer (see previous caveat) and it's arguably well educated, but that doesn't seem to constitute a good programmer according to the survey.</p>
<p>Software development is an art almost as much as it is an engineering field. You hear many people talk of <a href="http://en.wikipedia.org/wiki/Software_craftsmanship">software craftsmanship</a>. Allowing a craftsman to rewrite your application in the new framework from scratch using the old application as a guide will allow her to provide a unique expertise that translation tools currently don't have. She'll be able to apply a high level human analysis that the tool cannot.</p>
<p>For example, imagine there's a bug in the old code? The programmer can spot and fix the bug, but the tool will translate it into a prettier and newer bug. What if the old code is doing something unique to that language because the language doesn't have the features of the current language. A programmer can take advantage of these features and the tool cannot. A programmer will naturally analyze your business process as the application comes together and will make recommendations that may make your new application even better than the old one was in its heyday; the tool will not.</p>
<p>I can think of dozens of examples like this and I'm sure you can as well. Ultimately, I believe that allowing a tool to convert your legacy code into newer legacy code is a waste of time, money, and opportunity and if it doesn't cost more in the short run, it will cost more in the long run.</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com0tag:blogger.com,1999:blog-5003807027724289640.post-25362497202933670372011-11-15T17:43:00.001-08:002011-11-15T18:20:03.045-08:00Switching on Enums: A Style Observation<a href="http://www.jetbrains.com/decompiler/"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhi_ny5jahxfT8G3wbncv6mO7ePnrK94Z5FNJ-c_2ycLgd0KwfDNX7Jod_-md88kR9pnhl4el_wOjjUWOCfIXjYtQK0uAYAAPMwKUC9OqeuNm72_Ew3WOJTxubjDOGIMLfnKlhszbHlplE/s288/logo_dotpeek.gif" alt="JetBrains dotPeek Logo" style="float: left; margin-right: 7px;" class="postimage" /></a>
<p>I was digging through the framework today looking for another good <a href="http://dpatrickcaldwell.blogspot.com/2011/08/dotpeek-of-week.html">dotPeek of the Week</a> topic. I was perusing the Lazy<T> class and found an interesting snippet.</p>
<p>This post isn't quite like my previous dotPeek of the Weeks insofar as this is more of an example of what not to do. This is certainly merely my opinion, but one rule I try to follow when writing code is that more expressive is almost always better than less expressive. When I was looking at the Lazy class, I found a great example of this.</p>
<p>Here's the code (abridged for clarity … and also because the threading in this class will make for better discussion later):
<pre name="code" class="c#">private T LazyInitValue()
{
switch (this.Mode)
{
case LazyThreadSafetyMode.None:
// set the value
break;
case LazyThreadSafetyMode.PublicationOnly:
// CompareExchange the value
break;
default:
// lock and set values
break;
}
}</pre></p>
<p>Is there anything you notice about this code? Perhaps any unanswered questions as you read it and try to figure out what it does? Specifically, what exactly constitutes the default case?</p>
<p>As I read through this code, learning about some of the interesting thread safety techniques, I found myself pondering, "why would locking be the default behavior? In fact, what is the default case? Do the default values have something in common?"</p>
<p>I looked at the enum LazyThreadSafetyMode and found this:
<pre name="code" class="c#">public enum LazyThreadSafetyMode
{
None,
PublicationOnly,
ExecutionAndPublication,
}</pre></p>
<p>That's when I decided that, in most cases, when you're switching on a reasonably small set of values, it's best to express these values explicitly so that the people (including you) who have to maintain the code can better understand why the default case is the default case … even if there are no comments.</p>
<p>For example, the following code is functionally equivalent:
<pre name="code" class="c#">private T LazyInitValue()
{
switch (this.Mode)
{
case LazyThreadSafetyMode.None:
// set the value
break;
case LazyThreadSafetyMode.PublicationOnly:
// CompareExchange the value
break;
case LazyThreadSafetyMode.ExecutionAndPublication:
// lock and set values
break;
}
}</pre></p>
<p>Personally, I find the latter example much more expressive. It's obvious to me what the three cases are and I don't have to wonder what possible values can become the default case. In fact, I may even go so far as having the default case throw an exception in this class. I'd do this so that, for whatever reason, if someone were to change the LazyThreadSafetyMode enum and not implement that case for the new values in Lazy<T>.LazyInitValue(), they'd get an exception in testing instead of incorrectly using the default functionality.</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com0tag:blogger.com,1999:blog-5003807027724289640.post-27257488915303635852011-11-12T06:23:00.001-08:002011-11-12T06:50:11.813-08:00Elon University has #1 MBA Program<a href="http://www.elon.edu"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi95CIy8eL2sITNq4UxzdP-cO-bcuYNzP4eWl13FuJoyPjNdy2TU6OEbBZiqvlA8BmhNdFFcIME-3Psyl78Bua2SZtrB5mcy7e4dc9qd_8FUDbLchLa5kOdNETGdOolVjqdcWgh5yOKcyQ/s288/ElonLogo.jpg" alt="Elon University Logo" style="float: left; margin-right: 7px;" class="postimage" /></a>
<p><a href="http://www.elon.edu/e-web/academics/business/mba/">Elon University's part-time MBA program</a> deserves a lot of credit for the success of Autopilot Consulting. <a href="http://www.businessweek.com/">Bloomberg Businessweek</a> recently gave the <a href="http://www.elon.edu/e-web/academics/business/">Martha and Spencer Love School of Business</a> (LSB) much deserved recognition.</p>
<p>In <a href="http://www.businessweek.com/magazine/the-best-business-schools-of-2011-11102011.html">The Best Business Schools of 2011</a>, Elon's MBA program was ranked first among competitors like UCLA, Carnegie Mellon, UC-Berkley, and Rice.</p>
<p>When I was looking for an MBA program to attend, I compared Elon's program with Duke University's and UNC's programs and was blown away by the comparative quality of the LSB program; the experience, friendliness, and helpfulness of the staff and faculty; and the extraordinarily low price tag. </p>
<p>Congratulations to the Elon LSB Faculty, Staff, Students, and Alum on a job well done.</p>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com0tag:blogger.com,1999:blog-5003807027724289640.post-12681811576793784632011-09-21T09:22:00.000-07:002011-11-26T12:44:44.364-08:00Cow Says Moo for Android - Teach Your Kids Farm Animal Sounds<a href="http://mobilemagicdevelopers.com/#cow_says_moo"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhRWvUwdPAa_64BPfS1Qm8-BEUHe8N-G5zQ0dFBbdGDo6xO0YouguJ751V9ofuL80COZ7eTme8p6w6KjL-uXkaLMMn3lNINTmjjUwY7GvqPBt5b_UdVY_LQU2SY2BhMahaXlEKKQ1h9mGE/s288/Cow%252520Says%252520Moo%252520Feature%252520Graphic.png" alt="Cow Says Moo Feature Image" style="float: left; margin-right: 7px;" class="postimage" /></a>
<p>
James, my partner in <a href="http://mobilemagicdevelopers.com">Mobile Magic Developers</a>, and I have a handful of <a href="https://market.android.com/developer?pub=Mobile+Magic+Developers">Android applications</a> under our belts now. We loved working on <a href="http://mobilemagicdevelopers.com/#chinese_whispers">Chinese Whispers</a>, an idea my wife gave me for a new twist on The Telephone Game. James wrote an immensely handy application for getting your <a href="http://mobilemagicdevelopers.com/#wifi_passwords">WiFi Passwords</a> from your phone.
</p>
<p>
I wrote the <a href="http://dpatrickcaldwell.blogspot.com/search/label/Chuck%20Norris%20Facts%20Widget">Chuck Norris Fact of the Day Widget</a> and the <a href="http://mobilemagicdevelopers.com/#jane_austen_quotes">Jane Austen Fact of the Day Widget</a>, and James wrote a really nice widget that shows you your last few installed applications called <a href="http://mobilemagicdevelopers.com/#recently_installed_apps">Latest Apps</a>.
</p>
<h2>But… Then we Wrote Cow Says Moo</h2>
<p>
<a href="http://mobilemagicdevelopers.com/#cow_says_moo">Cow Says Moo</a> is definitely my favorite so far. Many of my friends and family are having babies now and my wife and I have one on the way. I noticed that kids are growing more and more interested in technology and are remarkably capable with it. My niece loved playing with my Motorola Xoom and I loved the way her face lights up when she touches the screen and something happens. That's where we got the idea for Cow Says Moo.
</p>
<p>
Cow Says Moo is an educational game to teach young kids and toddlers about farm animals and the sounds they make. Each picture is hand illustrated by Emily Upchurch Robinson and when pressed plays farm animal sounds. You can use the front facing camera on your device (or the rear camera if you prefer) to record video and sound while your child interacts with your mobile device or while you simply delight him or her with the sounds. You can use the built in media gallery to share your videos with friends and family. You'll be surprised how much fun you'll have playing Cow Says Moo with your kids and you'll get a lot of cute video footage like I did "User Acceptance Testing", i.e., <a href="http://www.youtube.com/watch?v=oPgCZmZ4z70">playing Cow Says Moo with my godson AJ</a>.
</p>
Check out <a href="http://mobilemagicdevelopers.com/#cow_says_moo">Cow Says Moo</a>, and as always, feel free to contact us with any comments or bugs.D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com1tag:blogger.com,1999:blog-5003807027724289640.post-43197470235070039032011-09-02T08:18:00.000-07:002011-11-15T18:11:43.916-08:007-bit Encoding with BinaryWriter in .Net<a href="http://www.jetbrains.com/decompiler/"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhi_ny5jahxfT8G3wbncv6mO7ePnrK94Z5FNJ-c_2ycLgd0KwfDNX7Jod_-md88kR9pnhl4el_wOjjUWOCfIXjYtQK0uAYAAPMwKUC9OqeuNm72_Ew3WOJTxubjDOGIMLfnKlhszbHlplE/s288/logo_dotpeek.gif" alt="JetBrains dotPeek Logo" style="float: left; margin-right: 7px;" class="postimage" /></a>At work this week, I had the need to serialize objects and encrypt them while trying to keep the smallest data footprint I could. I figured the easiest thing to do would be to binary serialize them with the <a href="http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatters.binary.binaryformatter.aspx">BinaryFormatter</a>. It was, indeed, the easiest thing to do; however, the BinaryFormatter seems to come with a fair amount of overhead. By the time the BinaryFormatter was finished listing all of the necessary assembly-qualified type names, the 60 bytes of data I wanted to preserve were more than a kilobyte!
<br /><br />
I needed another way so I extended <a href="http://msdn.microsoft.com/en-us/library/system.io.binarywriter.aspx">BinaryWriter</a> (mostly to get it to serialize the types I needed it to) and now my 60 bytes take 68 bytes to serialize. In the process of writing this class, I looked through the disassembled BinaryWriter using <a href="http://www.jetbrains.com/decompiler/">JetBrains's dotPeek</a> and found this little gem (<a href="http://msdn.microsoft.com/en-us/library/system.io.binarywriter.write7bitencodedint.aspx">Write7BitEncodedInt(int)</a>) and decided it'd make a great <a href="http://dpatrickcaldwell.blogspot.com/2011/08/dotpeek-of-week.html">dotPeek of the Week</a>:<pre name="code" class="c#">protected void Write7BitEncodedInt(int value)
{
uint num = (uint) value;
while (num >= 128U)
{
this.Write((byte) (num | 128U));
num >>= 7;
}
this.Write((byte) num);
}</pre>
This is how the <a href="http://msdn.microsoft.com/en-us/library/yzxa6408.aspx">BinaryWriter.Write(string)</a> encodes the length of the string. Thus, if you have a string with fewer than 128 characters, it only takes one byte to encode the length. In my implementation, I used this to specify the length of collections too. In fact, when writing a 32 bit signed integer, you should be able to save space for all positive numbers less than 2 ^ 22 and break even (require four bytes) for 2 ^ 29. Encoding an Int32 that is greater than or equal to 2 ^29 will require five bytes. Thus, if your integers tend to be smaller than 536,870,912, you'll probably save space encoding this way. A similar function could be used for a long where all positive values less than 2 ^ 57 will result in at least breaking even.
<br /><br />
Here's how it works:
<ol>
<li>Convert the number to an unsigned int so you can do arithmetic on positive numbers (after all, you're just writing bits)</li>
<li>
While the converted value is greater than or equal to 128 (i.e., 8 bits)
<ol>
<li>Write low 7 bits and put a 1 in the high bit (to indicate to the decoder more bytes are coming)</li>
<li>Shift the 7 bits you just wrote off the number</li>
</ol>
</li>
<li>When the loop finishes, there will be 7 or fewer bits to write so write them</li>
</ol>
I think this is really clever and very easy. Reading the data back is a smidgen more complicated (<a href="http://msdn.microsoft.com/en-us/library/system.io.binaryreader.read7bitencodedint.aspx">Read7BitEncodedInt()</a>):<pre name="code" class="c#">protected internal int Read7BitEncodedInt()
{
// some names have been changed to protect the readability
int returnValue = 0;
int bitIndex = 0;
while (bitIndex != 35)
{
byte currentByte = this.ReadByte();
returnValue |= ((int) currentByte & (int) sbyte.MaxValue) << bitIndex;
bitIndex += 7;
if (((int) currentByte & 128) == 0)
return returnValue;
}
throw new FormatException(Environment.GetResourceString("Format_Bad7BitInt32"));
}</pre>
Here's how this works:
<ol>
<li>Set up an int to accumulate your data as you read them</li>
<li>Set up a place to keep track of which 7-bit block you're reading</li>
<li>While your bit index is less than 35 (i.e., you've read no more than 5 bytes comprising 1 "more bytes" indicator and 7 data bits)
<ol>
<li>Read a byte</li>
<li>Take the byte you just read and <a href="http://en.wikipedia.org/wiki/Logical_conjunction">logical conjuction</a> (bitwise and) it with sbyte.MaxValue (127 or in binary 0111 1111)</li>
<li>Left shift those 7 bits to the position in which they belong</li>
<li>Use a <a href="http://en.wikipedia.org/wiki/Logical_disjunction">logical disjunction</a> (bitwise or) to write those seven bits into your accumulator int</li>
<li>Add 7 to your bit index for the next 7 bits you read</li>
<li>If the current byte you read does not have a 1 in its <a href="http://en.wikipedia.org/wiki/Most_significant_bit">most significant bit</a> (i.e, byte & 1000 0000 == 0)
<ol>
<li>There are no more bytes to read so just return the current accumulator value</li>
</ol>
</li>
</ol>
</li>
<li>If you get to this point, you've read a 6th of 5 bytes and it's time to let the user know there was a formatting problem</li>
</ol>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com1tag:blogger.com,1999:blog-5003807027724289640.post-31077843372978444152011-08-25T19:09:00.000-07:002011-09-18T08:17:45.943-07:00.Net GetHashcode Functions<a href="http://www.jetbrains.com/decompiler/"><img src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhi_ny5jahxfT8G3wbncv6mO7ePnrK94Z5FNJ-c_2ycLgd0KwfDNX7Jod_-md88kR9pnhl4el_wOjjUWOCfIXjYtQK0uAYAAPMwKUC9OqeuNm72_Ew3WOJTxubjDOGIMLfnKlhszbHlplE/s288/logo_dotpeek.gif" alt="JetBrains dotPeek Logo" style="float: left; margin-right: 7px;" class="postimage" /></a>Last week, I wrote a post about a <a href="http://dpatrickcaldwell.blogspot.com/2011/08/net-method-to-combine-hash-codes.html">.Net method to combine hashcodes</a>. In the process of designing that method, I looked into dozens of .Net's GetHashcode implementations.<br />
<br />
If you'd like to know the principles of hashcodes, take a look at the aforementioned article. This one is part of the <a href="http://dpatrickcaldwell.blogspot.com/2011/08/dotpeek-of-week.html">dotPeek of the Week</a> series so I'll just be sharing the insight I got from the framework's implementations here.<br />
<br />
First, some really basic ones:<pre name="code" class="c#">// from Int32
public override int GetHashCode()
{
return this;
}
// from Int16
public override int GetHashCode()
{
return (int) (ushort) this | (int) this << 16;
}
// from Int64
public override int GetHashCode()
{
return (int) this ^ (int) (this >> 32);
}</pre><br />
The Int32 implementation easily meets the hashcode requirements by returning itself. The Int16 copies itself to the top half of the Int32 and returns that. The Int64 takes the bottom half of itself and XORs it with the top half. <br />
<br />
This XOR is the first valuable piece of information. If you look at the logical functions, the XOR produces the best bit twiddling for hashing. Basically, the <a href="http://en.wikipedia.org/wiki/Truth_table#Exclusive_disjunction">XOR</a> produces a relatively even distribution of bits as opposed to the <a href="http://en.wikipedia.org/wiki/Truth_table#Logical_disjunction">OR</a> and the <a href="http://en.wikipedia.org/wiki/Truth_table#Logical_conjunction">AND</a> operations which will be biased 3 to 1 in one direction or the other. <br />
<br />
Some more complicated implementations:<pre name="code" class="c#">// from String
public override unsafe int GetHashCode()
{
fixed (char* chPtr = this)
{
int num1 = 352654597;
int num2 = num1;
int* numPtr = (int*) chPtr;
int length = this.Length;
while (length > 0)
{
num1 = (num1 << 5) + num1 + (num1 >> 27) ^ *numPtr;
if (length > 2)
{
num2 = (num2 << 5) + num2 + (num2 >> 27) ^ numPtr[1];
numPtr += 2;
length -= 4;
}
else
break;
}
return num1 + num2 * 1566083941;
}
}
// from Tuple<>
internal static int CombineHashCodes(int h1, int h2)
{
return (h1 << 5) + h1 ^ h2;
}</pre>These two methods have some really good information in them. The String.GetHashcode implementation has what's called a <a href="http://en.wikipedia.org/wiki/Rolling_hash">rolling hash</a>. It loops through the characters, does a <a href="http://en.wikipedia.org/wiki/Barrel_shifter">barrel shift</a> by 5, and then XORs with the next character. <br />
<br />
While I liked this, I preferred the simplicity of the <a href="http://en.wikipedia.org/wiki/List_of_hash_functions#Non-cryptographic_hash_functions">Bernstein Hash</a>. The main component of the Bernstein Hash is the (i << 5) + i. i << 5 == i * 32 (except that bit shifting is much faster than multiplying). <br />
<br />
Thus, i << 5 + i == 32i + i == 33i. The Bernstein Hash just takes the current hash value, multiplies it by 33, and XORs in the new value.<br />
<br />
I didn't use 33 in my hash function because I feel like using prime numbers is healthy. Thus, instead of adding I subtract so my hashcode method is (hash << 5) - hash ^ value (or 31 * hash ^ value). This, by the way, is the way Java tends to do it.<br />
<br />
Here are some interesting ones from System.Drawing:<pre name="code" class="c#">// from Size
public override int GetHashCode()
{
return this.width ^ this.height;
}
// from Rectangle
public override int GetHashCode()
{
return this.X ^ (this.Y << 13 | (int) ((uint) this.Y >> 19)) ^ (this.Width << 26 | (int) ((uint) this.Width >> 6)) ^ (this.Height << 7 | (int) ((uint) this.Height >> 25));
}
// from Point
public override int GetHashCode()
{
return this.x ^ this.y;
}</pre>D. Patrick Caldwellhttp://www.blogger.com/profile/09236952464473670820noreply@blogger.com0