<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">

  <title><![CDATA[Dan Adams]]></title>
  
  <link href="http://mrdanadams.com/" />
  <updated>2013-01-16T10:19:41-05:00</updated>
  <id>http://mrdanadams.com/</id>
  <author>
    <name><![CDATA[Dan Adams]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/MisterDanAdams" /><feedburner:info uri="misterdanadams" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><entry>
    <title type="html"><![CDATA[How to Work with Consultants Successfully]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/k_FH32V4K4E/" />
    <updated>2013-01-01T12:56:00-05:00</updated>
    <id>http://mrdanadams.com/2013/how-to-work-with-consultants-successfully</id>
    <content type="html">&lt;p&gt;&lt;a href="http://www.toothpastefordinner.com/index.php?date=100210" target="blank" rel="nofollow" title="Toothpaste for Dinner" class="aligncenter"&gt;&lt;img src='http://mrdanadams.com/images/consulting-consultant_400x400.gif' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A bit of advice on how to work successfully with a consultant (or consulting firm).&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;Get an inside look at their portfolio&lt;/h2&gt;

&lt;p&gt;Consultancies usually put up a public portfolio. This is the work they want you to see or the work they are allowed to show publicly. You want to see what they&amp;#8217;ve done in the last year. This will give you best sense of their capabilities and the direction of the firm. They can often show recent work in private, if asked, that they can&amp;#8217;t promote publicly.&lt;/p&gt;

&lt;h2&gt;Talk to the people who do the work&lt;/h2&gt;

&lt;p&gt;While I&amp;#8217;m not a huge fan of actually interviewing every consultant as you would a full time hire (you really shouldn&amp;#8217;t have to), talk to people at the firm that actually do the work (i.e. not the sales guy). Have a chat about your project and what it seems like to them. They should not only understand it quickly but be excited about it and have new ideas and suggestions.&lt;/p&gt;

&lt;p&gt;Some firms will sell you their A team and give you the B (or C) squad. It&amp;#8217;s not that common but it happens. These guys are jerks.&lt;/p&gt;

&lt;h2&gt;Keep the owner in the loop&lt;/h2&gt;

&lt;p&gt;You have every right as a client to give feedback to the person who cares the most: the owner. If you have a problem or feel like the project team isn&amp;#8217;t pulling through get on the phone and make your concerns known in no uncertain terms.&lt;/p&gt;

&lt;p&gt;This may sound harsh but it&amp;#8217;s the best thing for both you and the firm. They can&amp;#8217;t fix problems they don&amp;#8217;t know exist. It&amp;#8217;s easier to make it work with a firm you&amp;#8217;ve already selected than finding another one.&lt;/p&gt;

&lt;h2&gt;Go local. Get them in-house.&lt;/h2&gt;

&lt;p&gt;The more closely you collaborate and the more often you talk the lower your risk. You&amp;#8217;ll more quickly find issues and get things running smoothly. If possible, get a shop in town and have them live with you. They&amp;#8217;ll spend less time with communications overhead (and more time on real work) and you can get updates from them directly instead of reading some sort of status report.&lt;/p&gt;

&lt;h2&gt;Have an out. Be protected.&lt;/h2&gt;

&lt;p&gt;Don&amp;#8217;t leave yourself with all the risk &lt;a href="http://mrdanadams.com/2012/why-your-startup-shouldnt-hire-consultants"&gt;hiring a consultant&lt;/a&gt;. One of the benefits not available with full-time hires is that consultants come with contracts or Statements of Work. This means either resource for hire or fixed bid (see below). Even in the case of team augmentation, encode something in the contract promising at least a minimum set of deliverables.&lt;/p&gt;

&lt;p&gt;Don&amp;#8217;t pay (everything) up front. Tie payment to deliverables or spread out over the schedule.&lt;/p&gt;

&lt;h2&gt;My experience hiring consultants&lt;/h2&gt;

&lt;p&gt;I&amp;#8217;m a consultant. I&amp;#8217;d like to think I&amp;#8217;m one of the good ones. But I&amp;#8217;ve also played this game from the other side.&lt;/p&gt;

&lt;p&gt;I was the Head of Engineering in a product company creating a new product (essentially a startup from the inside). In order to serve both our old client / agency work and our new product development (while having no up-front capital investment) we had to get creative with engineering resources.&lt;/p&gt;

&lt;p&gt;We worked with outside firms in Argentina, Connecticut, New York City, India, Vietnam, and elsewhere.&lt;/p&gt;

&lt;p&gt;We worked with independent contractors / consultants in just as many places.&lt;/p&gt;

&lt;p&gt;Eventually, we even set up an off-shore development team in Omsk, Russia.&lt;/p&gt;

&lt;p&gt;In the first two cases (using contractors / consultancies) my success rate was maybe 25%. That&amp;#8217;s rough. But once we found a person or shop that worked we set up a long-term relationship with them. In fact, one of the best contractors I ever worked with was just &amp;#8220;some dude&amp;#8221; out in Ohio with three kids getting his PhD. I never even met him.&lt;/p&gt;

&lt;p&gt;These relationships were a valuable asset and complimented our sizable internal team. It allowed our business to react to resource needs without reducing internal staff.&lt;/p&gt;

&lt;h2&gt;Appendix: Consulting Pricing Models&lt;/h2&gt;

&lt;p&gt;(Is putting an appendix on a blog post a sign it&amp;#8217;s too long? Oh, well.)&lt;/p&gt;

&lt;p&gt;If you are unclear with how pricing works with a consultant, here&amp;#8217;s the gist. Each model has it&amp;#8217;s own risk and benefits and one is not clearly better than another.&lt;/p&gt;

&lt;h3&gt;Fixed Bid&lt;/h3&gt;

&lt;p&gt;You tell them what you want, they ask some questions, make some assumptions, and give you a document telling you what they will do and how much it costs. If you change your mind during the project, you get a change order (changing the contract and probably asking for more money). By signing the contract, you are in for the whole amount (assuming they deliver).&lt;/p&gt;

&lt;p&gt;As a client, you get a promise of what they&amp;#8217;ll deliver. In many ways the consultant holds the most risk here because if the project goes over budget (and they don&amp;#8217;t manage the changes in cost) they are left eating the overage. Since they are holding the cost risk any fixed bid contract will include some degree of risk buffer.&lt;/p&gt;

&lt;p&gt;These contracts are appropriate for projects that are fairly well defined and well bounded.&lt;/p&gt;

&lt;h3&gt;Staff Augmentation&lt;/h3&gt;

&lt;p&gt;You pay a certain rate by the person for as long as you have them. There is no document saying what they will deliver. There is no risk of going over budget for the consultant but the client has no guarantee what will be delivered. However, the client can change their mind every day if they wish. Also, the clients financial obligation is less than a fixed bid contract since the staff augmentation can more or less be stopped at any time.&lt;/p&gt;

&lt;p&gt;This model is appropriate when the problem being solved isn&amp;#8217;t well defined such as &lt;a href="http://mrdanadams.com/2012/tips-startups-products-off-ground"&gt;working with early stage startups&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;Hybrid&lt;/h3&gt;

&lt;p&gt;While more of a contract detail, these two models can be combined such that the client gets a bit more guarantee of what will be delivered while at the same time not having to deal with change orders every week.&lt;/p&gt;

&lt;p&gt;Identify a few high-level, critical deliverables and encode those in the contract. You can still change direction during the project although, if it&amp;#8217;s a big enough change, you might have to do a change order.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=k_FH32V4K4E:lLConn3xzRM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=k_FH32V4K4E:lLConn3xzRM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=k_FH32V4K4E:lLConn3xzRM:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=k_FH32V4K4E:lLConn3xzRM:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/k_FH32V4K4E" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2013/how-to-work-with-consultants-successfully/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Why Your Startup Shouldn't Hire Consultants]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/SyhG3hJJ8ig/" />
    <updated>2012-12-30T11:13:00-05:00</updated>
    <id>http://mrdanadams.com/2012/why-your-startup-shouldnt-hire-consultants</id>
    <content type="html">&lt;p&gt;&lt;img src="http://mrdanadams.com/images/dont-worry-consultant.jpg" alt="Don't worry I'm a consultant" /&gt;&lt;/p&gt;

&lt;p&gt;Hired guns. Code mercenaries. Consultants.&lt;/p&gt;

&lt;p&gt;Whether you are early stage or have an established product, bringing a consultant onto your startup can be a dicey prospect. Here are reasons not to and some things you may not have thought of.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;p&gt;While there is truth to all of these, there&amp;#8217;s more to be considered. Reality is, many startups are &lt;a href="http://mrdanadams.com/2013/how-to-work-with-consultants-successfully/"&gt;successfully using consultancies&lt;/a&gt; (although they may not advertise it). In a world where good people are hard to hire, time to market is tight, and product quality is critical, hiring a consultant is not always the right thing to do. However, it&amp;#8217;s also not always not either (if you&amp;#8217;ll pardon the double negative).&lt;/p&gt;

&lt;h2&gt;Reason 1: They&amp;#8217;re Expensive&lt;/h2&gt;

&lt;p&gt;If you need to hire a design / engineering consultant (in Boston, at least) you are likely looking at $100 - $250 per hour depending on the shop and what you need. That&amp;#8217;s $4,000 - $10,000 per week per full time person. Assuming you had a full time hire that had a total cost of $150k per year (after taxes, benefits, overhead, etc) that&amp;#8217;s around $72 / hour.&lt;/p&gt;

&lt;p&gt;Ouch.&lt;/p&gt;

&lt;p&gt;Of course, this assumes we are comparing cost for the same value which probably isn&amp;#8217;t true. Not only might you be getting something better or different than you have internally but you don&amp;#8217;t have the same costs associated. A consultant pays their own taxes, vacations, benefits, etc.&lt;/p&gt;

&lt;p&gt;You usually don&amp;#8217;t hire a consultant for a whole year. You bring them on for something specialized or because they offer something you don&amp;#8217;t have in your internal team or can&amp;#8217;t hire. They&amp;#8217;ve got experience and skills that can prove to be invaluable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Strategic insight&lt;/em&gt;. A general knowledge of the industry, best practices, and the ability to pinpoint the best technical or product direction.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;They&amp;#8217;re battle tested&lt;/em&gt;. They&amp;#8217;ve been through the full project life cycle across different team sizes, contexts, and technologies. This adds crucial perspective that only comes by experience. Especially knowledge that only comes from previous failures.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;They&amp;#8217;re good. Maybe really good.&lt;/em&gt; That&amp;#8217;s why they are &amp;#8220;expensive&amp;#8221; (usually). They&amp;#8217;ve got the skills you need and can get things done quickly.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Consultants come with friends.&lt;/em&gt; Hiring a consultant from a consultancy means you&amp;#8217;ve got a pool of similar resources you can draw from quickly without going through the hiring process for each one. Even for independent consultants, they often have a wider network to draw from when you need someone based on personal recommendation.&lt;/li&gt;
&lt;li&gt;Consultants are easier to let go.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;You may bring on a consultant simply to augment your team and add momentum. Add a consultant or two where it makes sense, crank for a while, and then scale down to a slower pace after the push.&lt;/p&gt;

&lt;p&gt;In a perfect world, when you needed to grow your team you&amp;#8217;d be able to hire passionate, invested people with the skills you need when you need them. That&amp;#8217;s a fantasy. The reality is that hiring (both salary and consultant) is hard; it takes a lot of your time and involves significant risk.&lt;/p&gt;

&lt;h2&gt;Reason 2: They Walk Away with Knowledge&lt;/h2&gt;

&lt;p&gt;History matters in a startup. The reasons why things are what they are (both good and bad) are important. A consultant may come in, do some great work, and then walk out with all the good bits in their head. This is a legitimate risk for anyone hiring a consultant.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s not, however, a show stopper:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Integrate them in the team so there is knowledge sharing during the work. Make sure other people at least know, and ideally have worked on, their stuff. Do code reviews. Seriously, just do them.&lt;/li&gt;
&lt;li&gt;Have them put together documentation during the course of the work (not at the last minute) and use it themselves.&lt;/li&gt;
&lt;li&gt;Embed them in team discussions so they know why the team did what they did and vice-versa. The random, informal conversations are often the most valuable.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;If this is a concern, don&amp;#8217;t give a consultant the most important piece of your product and then have nobody else know about it. Add some redundancy or minimize risk by putting them on something else.&lt;/p&gt;

&lt;h2&gt;Reason 3: They&amp;#8217;re Not Invested&lt;/h2&gt;

&lt;p&gt;Your core team is passionate and invested, you hope. (Let&amp;#8217;s assume they are.) A consultant is usually seen (rightly) as an outsider who comes in, does something, and walks away with no strings attached. The assumption is that they won&amp;#8217;t deliver the same quality or be as proactive at addressing problems as your core team.&lt;/p&gt;

&lt;p&gt;Again, a legitimate concern.&lt;/p&gt;

&lt;p&gt;Get a consultant with a good reputation, a good body of work, and seems like someone you can trust. Protect yourself by getting them as closely integrated with you and your team as possible; tight collaboration will quickly shed light on lurking concerns.&lt;/p&gt;

&lt;p&gt;It&amp;#8217;s incorrect to assume that consultants don&amp;#8217;t have stake in your project. Consultants live and die by their portfolio and having happy clients is critical. Finding new clients is expensive and difficult. Happy clients come back for more work. Especially when working with startups, it&amp;#8217;s foolish for me not to do everything I can to help them succeed.&lt;/p&gt;

&lt;h2&gt;Allow Me to Illustrate&lt;/h2&gt;

&lt;p&gt;Here are a few scenarios where bringing in a consultant may make a lot of sense.&lt;/p&gt;

&lt;h3&gt;Get Your Product Off the Ground&lt;/h3&gt;

&lt;p&gt;As a founder in an early stage startup, you face many challenges in &lt;a href="http://mrdanadams.com/2012/tips-startups-products-off-ground"&gt;getting your product off the ground&lt;/a&gt;. Furthermore, many founders have never designed a product, built a software platform from the ground up, hired a creative team, scaled anything, worked with big data, and might be working with emerging technologies such as mobile.&lt;/p&gt;

&lt;p&gt;That&amp;#8217;s a big, steaming pile of risk. Building the wrong thing, building it poorly, or taking too long can be a killer.&lt;/p&gt;

&lt;p&gt;Bring in a (very) small team of experienced consultants who can not only build the technology but help you understand the product, the technical landscape, and build the right product. Furthermore, while working with the consultant you can be working on hiring that long-term team. The product gets built in the time needed and you get some breathing room to put your team in place.&lt;/p&gt;

&lt;h3&gt;Add Momentum or Features to an Existing Product&lt;/h3&gt;

&lt;p&gt;You&amp;#8217;ve got your core team established and product off the ground. You have a well defined roadmap. Problem is, you simply don&amp;#8217;t have the man power to get it all done in the time needed.&lt;/p&gt;

&lt;p&gt;Bring in a handful of consultants, integrated them tightly into your team, and hit the ground running.&lt;/p&gt;

&lt;h3&gt;Bring in Some Expertise&lt;/h3&gt;

&lt;p&gt;This is the likely the most common case for a consultant; you need a special skill you don&amp;#8217;t have in-house and doesn&amp;#8217;t make sense to develop. Find a consultant that can do it but can also do a lot more; integrate them as much as possible and get as much out of them as you can. For instance, if you bring in a firm to do some mobile work get them to add value in other places such as touching the REST APIs, helping you create a mobile roadmap for after they are gone, or giving advice on to what degree your web apps need to be responsive.&lt;/p&gt;

&lt;h2&gt;Consultant != Contractor&lt;/h2&gt;

&lt;p&gt;I&amp;#8217;m making an assumption here in the difference between a consultant and a contractor (this is my definition). Consultants (disclaimer: like me) are often brought on for their thoughts and experience in addition to getting the work done. Consultants may even challenge their clients with new ideas and approaches.&lt;/p&gt;

&lt;p&gt;Contractors are brought on to perform a task, build a feature, do something specific.&lt;/p&gt;

&lt;h2&gt;Success or Horror?&lt;/h2&gt;

&lt;p&gt;Have you considered using a consultant at your startup? If you&amp;#8217;ve used one, how was your experience?&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=SyhG3hJJ8ig:kEcRFi97y5c:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=SyhG3hJJ8ig:kEcRFi97y5c:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=SyhG3hJJ8ig:kEcRFi97y5c:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=SyhG3hJJ8ig:kEcRFi97y5c:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/SyhG3hJJ8ig" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/why-your-startup-shouldnt-hire-consultants/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[5 Tips for Getting Your Startups Product Off The Ground]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/z8IBZCe-e7U/" />
    <updated>2012-12-28T21:27:00-05:00</updated>
    <id>http://mrdanadams.com/2012/tips-startups-products-off-ground</id>
    <content type="html">&lt;p&gt;&lt;img src="http://mrdanadams.com/images/getting-your-startup-off-the-ground.jpg" alt="Lift off getting your startup product off the ground" /&gt;&lt;/p&gt;

&lt;p&gt;For most of the past year, I&amp;#8217;ve been embedded in &lt;a href="http://dogpatchlabs.com"&gt;Dogpatch Labs&lt;/a&gt; working with early stage startups, first &lt;a href="http://www.bytelight.com/" title="Indoor Location"&gt;ByteLight&lt;/a&gt; and then &lt;a href="http://www.blockavenue.com/" title="Neighborhood Reviews"&gt;BlockAvenue&lt;/a&gt;, to create their core product and get them to the next level. Both faced the challenges of a pre-MVP product and small team. Here&amp;#8217;s some of what&amp;#8217;s involved, the challenges faced, and approaches to tackle them.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;#1 Establish Vision, Create Focus&lt;/h2&gt;

&lt;blockquote&gt;&lt;p&gt;What defines success?&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;The answer to that question will change. Possibly often. You should ask it regularly and make sure your whole team understands the answer and the reasoning behind it. The answer will likely change based on how far out you look: what defines success for the next month will be different than 3 months or 12.&lt;/p&gt;

&lt;p&gt;You may struggle to identify a single definition of success but it&amp;#8217;s important that you do so (or attempt to as best as possible). Back up the goal with ways to measure it. Benchmark team progress regularly against it.&lt;/p&gt;

&lt;p&gt;Give your team a clear sense of why you are there, how you are going to take over the world, and that they are expected to be a big part of that.&lt;/p&gt;

&lt;p&gt;Some examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Being customer ready (if you sell a customer you can actually support them)&lt;/li&gt;
&lt;li&gt;A product that demonstrates your direction strongly enough to incite new interest from press and investors&lt;/li&gt;
&lt;li&gt;Getting enough app usage to demonstrate that, yes, the market really is there&lt;/li&gt;
&lt;li&gt;Getting the &amp;#8220;hockey stick&amp;#8221;: non-linear growth in app usage, downloads, conversions, sales, or whatever else is the most important success metric for your app.&lt;/li&gt;
&lt;li&gt;Generating enough revenue to get you bootstrapped&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;#2 Continuously Re-Define Your Product&lt;/h2&gt;

&lt;p&gt;While keeping your eye on what defines success, re-evaluate your product design. Evolve. Tweak. Change. Nothing is sacred. Get as much feedback and input from press, investors, peers, and users as possible. Then evaluate and digest it. Let it challenge your vision but don&amp;#8217;t go chasing every idea or feature thrown at you. Resist all impulse to get lost in feature design. Stay focused on who your users are, what they want to do, and what you want them to do.&lt;/p&gt;

&lt;h2&gt;#3 Start Hiring. Now.&lt;/h2&gt;

&lt;p&gt;When hiring for a small team you want to hire heavy hitters who can help you drive towards the goal line. Unfortunately, so does every other company. Expect it to take at least three months to hire someone (especially the right someone). For engineers, designers, and front-end devs the market is shot. It&amp;#8217;s been that way for a while and it&amp;#8217;s only going to get worse.&lt;/p&gt;

&lt;p&gt;Establish a hiring pipe (personal networks, word of mouth, meetups, even recruiters) and keep it full. Someone who&amp;#8217;s not available (or interested) now might be a few months down the road. Don&amp;#8217;t wait for positions to really open up; starting filling the pipe as if the position were open now (though I&amp;#8217;m not advocating leading people on).&lt;/p&gt;

&lt;p&gt;Even for a small team, &lt;a href="http://mrdanadams.com/2012/mentoring-apprenticeship-software-engineers"&gt;grow them through mentoring&lt;/a&gt;. You want them &lt;a href="http://mrdanadams.com/2012/career-story-trajectory-context/"&gt;stepping up to new challenges&lt;/a&gt; on a regular basis and &lt;a href="http://mrdanadams.com/2012/its-your-company-act-like-an-owner/"&gt;taking ownership&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If time is against you, consider hiring a consultant (shameless plug). It&amp;#8217;s by no means a silver bullet but the right consultant can add acceleration and experience where it&amp;#8217;s sorely needed.&lt;/p&gt;

&lt;h2&gt;#4 Understand Your Team&lt;/h2&gt;

&lt;p&gt;My first priority with a new project is to get my bearings. To a frustrating extent, nearly everything that comes out of my mouth early on will be a question. I want to know the state of things and how they got that way. This extends far beyond technology.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who are the founders? Who are the key influencers (founders or not)? What is the dynamic between them and the rest of the team? Who makes the decisions? Who really leads?&lt;/li&gt;
&lt;li&gt;What technology is in place now? Why was it selected? What issues have they had with it so far?&lt;/li&gt;
&lt;li&gt;What are the skills of the current team both technically and non-technically? Do they have good communication skills? Do they understand application architecture? Have they ever scaled anything or worked with large data sets? How do they handle conflict and stress?&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Keep asking &amp;#8220;why&amp;#8221; until you really understand your context. If needed, take someone out to lunch or ask small questions in the break room. Tease out perspectives you can&amp;#8217;t get in a group setting. Paradoxically, humans are what make software difficult and understanding the team is critical.&lt;/p&gt;

&lt;p&gt;While this is especially true for an outsider it&amp;#8217;s often true for the core team as well. The better a team understands itself the more it will function as a cohesive unit. A founder who understands his team can utilize their strengths while both protecting against and working on their weaknesses.&lt;/p&gt;

&lt;h2&gt;#5 Focus on Quality&lt;/h2&gt;

&lt;p&gt;Quality is the visible, tangible thing that gives your users, the press, and investors a perception of value. Make people want to use it and share it. Quality also shows that you&amp;#8217;re serious and should be taken seriously.&lt;/p&gt;

&lt;p&gt;Make it work. Make it beautiful. Make it fast.&lt;/p&gt;

&lt;p&gt;Which factors have been important getting your product off the ground?&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=z8IBZCe-e7U:UxrXVPD8YIA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=z8IBZCe-e7U:UxrXVPD8YIA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=z8IBZCe-e7U:UxrXVPD8YIA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=z8IBZCe-e7U:UxrXVPD8YIA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/z8IBZCe-e7U" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/tips-startups-products-off-ground/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Mentoring &amp; Apprenticeship for Software Engineers]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/5c9-yZUUdFc/" />
    <updated>2012-12-07T18:26:00-05:00</updated>
    <id>http://mrdanadams.com/2012/mentoring-apprenticeship-software-engineers</id>
    <content type="html">&lt;p&gt;&lt;img src="http://mrdanadams.com/images/mentoring.jpg" alt="blacksmith glassblower software engineer mentoring apprentice" class="aligncenter" /&gt;&lt;/p&gt;

&lt;p&gt;What we software engineers (and designers) do is very creative and, in general, best learned through experience. Computer science and color theory can be groked from a book but client communication, handling conflict, and other less tangible disciplines are gained in the trenches. Learning on your own is slow and error-prone; you can miss out on stuff or learn something wrong based on the project work you get or who you happen to work with.&lt;/p&gt;

&lt;p&gt;I view the apprenticeship mentoring model for software engineers (as with a blacksmith, glass blower, ceramics or any of the high-skill, difficult to master traditional crafts) to be the best way of accelerating growth, increasing retention, and creating the kind team your organization really needs. What follows is my approach to such an apprenticeship model that works in both client services and product teams.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;p&gt;For what it&amp;#8217;s worth, I have a good bit of experience with this: I was head of engineering during company growth from 25 to 50 (with a corresponding growth in revenues and projects). This meant going from an engineering team of 6 to an internal team of 15-20 and an off-shore team of about 10. It also required increasing the number of technical project leads from 1 (me) to 5 or 6 through direct mentoring. Those tech leads then themselves became mentors to the new hires as we grew. In my consulting work, I work with early stage startups, such as &lt;a href="http://www.bytelight.com/" title="Indoor Location"&gt;ByteLight&lt;/a&gt; and &lt;a href="http://www.blockavenue.com/" title="Neighborhood Reviews"&gt;BlockAvenue&lt;/a&gt;, to design and execute hiring as well as plan and stabilize development teams for long-term product development.&lt;/p&gt;

&lt;h2&gt;Why Apprenticeship Makes Sense&lt;/h2&gt;

&lt;p&gt;Apprenticeship takes effort and dedication and does not come naturally to most individuals or organizations. It requires time and effort that may need to be justified:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It accelerates learning on the part of the apprentice in technical and non-technical skills including everything from code quality and language nuances to object design to client communication.&lt;/li&gt;
&lt;li&gt;It enables molding junior folks into what you need them to be. With a strong mentor model, you can widen your hiring field since you can bring on candidates that can &lt;a href="http://mrdanadams.com/2012/career-story-trajectory-context" title="software engineer career growth"&gt;grow more quickly&lt;/a&gt; into something really useful to the business. Associates without mentoring can become a big hiring and project risk and kind of a &amp;#8220;boat anchor&amp;#8221; that weighs more senior folks down instead of providing valuable support and fresh thinking.&lt;/li&gt;
&lt;li&gt;It challenges the mentor to grow as well. Mentoring grows technical, communication, and management skills beyond the limits of normal project work and open up opportunities for getting your people to &lt;a href="http://mrdanadams.com/2012/its-your-company-act-like-an-owner"&gt;act like owners&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;It encourages dialog and thought around &lt;a href="http://mrdanadams.com/2012/your-career-is-a-business/" title="Software Engineers: You Career is a Business"&gt;actively managing their careers&lt;/a&gt;. This is good for both the team and management; when growth happens from the bottom up it&amp;#8217;s healthier and reduces the overhead load on management.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Success Factors&lt;/h2&gt;

&lt;p&gt;There are a few factors that heavily influence the success of mentoring.&lt;/p&gt;

&lt;h3&gt;Clearly Defined Roles &amp;amp; Goals&lt;/h3&gt;

&lt;p&gt;The mentor and the apprentice should both understand each others roles. It should be something verbally stated to remove any awkwardness or barriers. The mentor is responsible for providing not only support (general advice, answering questions, etc.) but also constructive criticism and recognition for good work. Both of these last two are critical to balance each other out and create a good relationship between mentor and apprentice. The apprentice is responsible to raise their hand when they need help and be pro-active about it. They should be actively looking for areas where they feel weak and pull in the mentor.&lt;/p&gt;

&lt;p&gt;Mentoring needs to be clearly defined between the mentor and apprentice. The more ambiguity and awkwardness there is, the more that becomes a barrier to successful mentoring.&lt;/p&gt;

&lt;p&gt;Mentoring should be seen as a largely voluntary responsibility. They need to be self-motivated and see themselves in the role.&lt;/p&gt;

&lt;h3&gt;Proximity&lt;/h3&gt;

&lt;p&gt;Proximity, or lack thereof, is a deal breaker. I prefer that mentor and apprentice be &amp;#8220;attached at the hip&amp;#8221;. It&amp;#8217;s important that they feel very comfortable with each other and have a meaningful context that provides fodder for mentoring. They should talk often, even informally, so that there is a consistent dialog. Ideally the apprentice should sit near the mentor so that when a teaching moment arises the barrier to making that happen is as low as possible. Having to go find the mentor and interrupt them could be a high barrier, especially for junior folks.&lt;/p&gt;

&lt;p&gt;They need to have something they both know so they can talk about it. Having to context switch from project work to a side project could be a significant barrier. Project work usually provides the best &amp;#8220;fodder&amp;#8221; for mentoring. People are already thinking about it and engaged. It naturally provides many &amp;#8220;teaching moments&amp;#8221; in a way that manufactured mentoring throughout side projects does not.&lt;/p&gt;

&lt;p&gt;In either case, if you don&amp;#8217;t make time to be working on things in pairs, the mentoring won&amp;#8217;t really happen. Mentorship is prohibitively disruptive when it&amp;#8217;s a constant interruption to &amp;#8220;real&amp;#8221; work and involves at least two mental context switches. It requires some dedication to a schedule to make it happen effectively.&lt;/p&gt;

&lt;p&gt;This kind of thing can work without being spatially proximate. Pair programming through screenshares and audio chat can be an effective method of working through problems and passing along knowledge. You sometimes lose the non-verbal communications, i.e., being able to read the apprentice&amp;#8217;s general level of frustration, but for many folks it can work. With remote team members working from home, knowing how to be effective as a mentor remotely can be challenging but critical. There are a lot of tools at your disposal to make this work; use every one of them.&lt;/p&gt;

&lt;p&gt;Whether remote or not, make time for low-intensity touch points. Go grab a coffee or go out to lunch. This helps highlight issues you might be having that otherwise don&amp;#8217;t get communicated.&lt;/p&gt;

&lt;h3&gt;Support for Mentors&lt;/h3&gt;

&lt;p&gt;Mentoring is not a natural thing for most tech leads. It&amp;#8217;s a skill that needs to be developed. They should have someone that can provide support. This could be someone with some experience in it or even other mentors.&lt;/p&gt;

&lt;p&gt;Acknowledgment that the mentoring investment is worth it needs to start at the top. Management support in the form of explicit goals, advice, time, and resources is critical.&lt;/p&gt;

&lt;h3&gt;Every Apprentice is Different&lt;/h3&gt;

&lt;p&gt;Apprentice experience level, skill level, interests, and personality and some of the factors requiring that each mentoring situation be customized to the mentor and apprentice in order to be most affective. For instance, with one apprentice they might be really strong code wise and need to focus on client skills and another might be totally different. It&amp;#8217;s a balance of what that person needs and what the project context provides opportunity for.&lt;/p&gt;

&lt;h2&gt;Doing It&lt;/h2&gt;

&lt;h3&gt;The Kickoff&lt;/h3&gt;

&lt;p&gt;Any apprenticeship should start with a structured meeting between the mentor, the apprentice, and whoever is heading up the apprenticeship program. The meeting should cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What mentoring is and why you are doing it (answer: because it makes you better, will make you more successful in your career, will help you enjoy and be more successful in your projects, and helps the business as it grows)&lt;/li&gt;
&lt;li&gt;Where they are now in their skills and where they want to go&lt;/li&gt;
&lt;li&gt;What everyone wants to get out of it&lt;/li&gt;
&lt;li&gt;How it&amp;#8217;s going to work: project set up, logistics, communication, going out to lunch together, etc&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Mentors must know that the hour / day / week they would spend to do the best job of training / pairing with an apprentice is working towards some series of agreed upon goals. Knowing those goals would allow them to make the judgment call whether teaching or doing are the correct goals at any given point.&lt;/p&gt;

&lt;h3&gt;On the Same Project&lt;/h3&gt;

&lt;p&gt;In this model, mentor and apprentice are on the same project. This is usually a situation where the mentor is the project tech lead and delegates work. They sit near each other so the apprentice can ask questions and get help easily. The mentor frequently checks quality of work and provides feedback (for instance, code review a feature branch before merge).&lt;/p&gt;

&lt;p&gt;The mentor should look for opportunities to push the apprentice outside their comfort zone using project work. Give them responsibility on something that is just outside their current abilities and has real consequences. This could be technical such as building a large feature, researching a technology, designing an object model or non-technical such as leading a client meeting, writing a spec, estimating something, or leading some aspect of the project process.&lt;/p&gt;

&lt;h4&gt;Challenges&lt;/h4&gt;

&lt;p&gt;This requires projects that can support both the mentor and the apprentice in terms of project budget and having work that can be affectively delegated.&lt;/p&gt;

&lt;p&gt;One of the biggest challenges is in the staffing plan for projects. Not all projects will be a fit for their respective skill sets. Many teams don&amp;#8217;t have a lot of flexibility in mixing and matching project teams for an ideal makeup and staff largely based on current availability. This could be a source of friction to the process and one you&amp;#8217;ll need to be diligent about.&lt;/p&gt;

&lt;p&gt;It also can change how you sell projects as you need to plan for mentorship hours and some inefficiencies, especially in the work done by the apprentice: all growth requires some degree of failure. Re-examine your task estimates for things that may be delegated to the apprentice.&lt;/p&gt;

&lt;p&gt;Be careful to strike a balance between the cost of ramping up apprentice resources and the cost of delivering project features. Budgets may creep to account for &amp;#8220;apprentice time&amp;#8221; to a point where it effects project budget significantly if left unchecked. Crunch-time pressures also present a challenge to on-project apprenticeship.&lt;/p&gt;

&lt;p&gt;One model is to attach the mentor / apprentice and then look for projects for the pair. The opposite approach is also possible in order to lower the resource challenges and budget waste; to find a project fit for the apprentice and then start mentorship with the tech lead on that project. I&amp;#8217;m making a big assumption here that apprentices are not put on projects by themselves; having them on a project by themselves can be really risky.&lt;/p&gt;

&lt;h3&gt;On Different Projects&lt;/h3&gt;

&lt;p&gt;In this model, mentor and apprentice are on different projects but maintain a relationship across projects. They need something they can use as mentor-fodder:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The project the apprentice is on. This requires the mentor to stay up on it enough to look for opportunities to mentor with it.&lt;/li&gt;
&lt;li&gt;A side project the apprentice is passionate about. The mentor would also have to agree that the side project provided the right opportunity for growth (it&amp;#8217;s likely something that encourages technical growth).&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Using a real project helps the apprentice be more passionate / engaged / successful in their project (which is more valuable to the business than a side project). It also provides scenarios and challenges to the apprentice that a side project doesn&amp;#8217;t such as client communication and project process. Working on a side project requires time and energy both of which are usually limited during project work.&lt;/p&gt;

&lt;p&gt;These two models could be mixed in order to provide more resourcing options. For instance, you could start in the first model and then, as resource allocation demands, change to the second model to keep up the relationship between mentor / apprentice.&lt;/p&gt;

&lt;p&gt;Bootcamp style projects, a non-billable project geared towards getting new recruits ramped up on our practices, can work in the right context but require a fair bit of management, mentorship, and coordination outside of billable work. The &amp;#8220;shadow&amp;#8221; apprenticeship model on real billable work may be more practical for most organizations.&lt;/p&gt;

&lt;h3&gt;Resource Allocation &amp;amp; Project Pressures&lt;/h3&gt;

&lt;p&gt;Each project has it&amp;#8217;s particulars and, just as you consider the situation for who would be the best fit as a team, consider whether it&amp;#8217;s a fit for mentoring; the risk level and what you could get out of it. It could be very tough to mentor on a &amp;#8220;heads down&amp;#8221; project but there may be teaching opportunities there as well. In fact, a heads down project may be just the kind of catalyst for growth in a more senior person where it wouldn&amp;#8217;t be a fit for apprenticeship of younger staff. The decision to go heads down and crank (and how to do and handle that) is, in itself, something a junior person could learn. Learning to manage project pace and intensity is a valuable skill.&lt;/p&gt;

&lt;p&gt;Mentorship is a distraction. It is a context switch and that&amp;#8217;s okay. Teaching and doing are very different goals and, at any given moment, one goal or the other should be the highest priority. If it&amp;#8217;s crunch time, and delivering the product is the highest priority, you might put teaching on the back burner and focus on doing good work quickly. But at other times you would prioritize training and acknowledge the slower production time in favor of allowing the apprentice to work at his own pace.&lt;/p&gt;

&lt;p&gt;The ratio of mentoring to project time could be estimated (and should be capped) but likely won&amp;#8217;t be consistent. Mentoring with the context of a project (such as code reviews) can be a really effective method of upping quality, lowering risk, and lowing cost so it doesn&amp;#8217;t always need to be seen as waste or overhead. For instance, code reviews are an excellent way of teaching while catching bugs before they make it into production code.&lt;/p&gt;

&lt;p&gt;Mentoring is much less of a distraction if it&amp;#8217;s in the context of normal project work. If two people are jogging together, it&amp;#8217;s much more natural for one person to teach the other person better jogging form while they keep running. It would be highly disruptive if you were jogging and suddenly one of you said &amp;#8220;now stop so I can show you how to do good pushups&amp;#8221;.&lt;/p&gt;

&lt;p&gt;Expect mentoring to need case-by-case consideration with resourcing to ensure that it&amp;#8217;s done affectively without significantly negatively effecting project budget, quality, or overall success. Mentoring (and having junior folks in general) does take investment on the part of the company but that investment should be as known and controlled as possible.&lt;/p&gt;

&lt;h2&gt;The End Game: Make the Mentor Redundant&lt;/h2&gt;

&lt;p&gt;A mentor should, in most cases, be growing the apprentice in whatever way is necessary that the apprentice could eventually do their job. In many cases, this actually happens: the mentor starts as the project lead and by the end of the project the apprentice has taken the reins. A good example of this is a new project that has a heavy, green field development phase followed by a slower maintenance phase. The first provides an opportunity for the apprentice to lead the process, the product, and the technical machinery and possibly take the lead for the maintenance phase. This will be a challenge for them but is much simpler than green field development.&lt;/p&gt;

&lt;p&gt;This isn&amp;#8217;t always the case; you could also have an end game of an apprentice simply getting better at something specific. But, in general, you should be growing them to be well-rounded contributors. This typically is better for them and for the business.&lt;/p&gt;

&lt;h2&gt;Growth is Not Just for Apprentices&lt;/h2&gt;

&lt;p&gt;A well-rounded view of growth affords everyone opportunities for growth and improvement. For more senior folks, this means they don&amp;#8217;t need apprenticeship but rather support in other ways from their peers and management. In many ways, growing senior folks is more difficult than apprenticeship since, as people grow, the challenges they need to encounter in order to keep them growing become very specific to that person.&lt;/p&gt;

&lt;h2&gt;Your Mileage May Vary&lt;/h2&gt;

&lt;p&gt;The form that mentorship takes (or should take) can vary significantly by the team and individual. What has and hasn&amp;#8217;t worked for you? If you were without mentoring, is it something you could have benefited from?&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=5c9-yZUUdFc:KndlOcnYjpI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=5c9-yZUUdFc:KndlOcnYjpI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=5c9-yZUUdFc:KndlOcnYjpI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=5c9-yZUUdFc:KndlOcnYjpI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/5c9-yZUUdFc" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/mentoring-apprenticeship-software-engineers/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Trello for Agile Development at BlockAvenue]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/EnIC--wEmfM/" />
    <updated>2012-12-05T16:40:00-05:00</updated>
    <id>http://mrdanadams.com/2012/trello-agile</id>
    <content type="html">&lt;p&gt;One of the my roles &lt;a href="http://blog.blockavenue.com/corporate/meet-blockavenues-very-own-mvp-dan-adams/"&gt;acting as interim-CTO&lt;/a&gt; at &lt;a href="http://www.blockavenue.com/" title="Neighborhood Reviews"&gt;BlockAvenue&lt;/a&gt; is to manage a &lt;a href="http://blog.blockavenue.com/corporate/blockavenue-your-neighborhood-socialized/"&gt;high velocity, agile development process&lt;/a&gt;. &lt;a href="https://trello.com/"&gt;Trello&lt;/a&gt; is my new favorite tool when it comes to ticket management but it&amp;#8217;s not without limitations. Here&amp;#8217;s how we used Trello at BlockAvenue and some of the challenges working with it.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;The Basics&lt;/h2&gt;

&lt;p&gt;Cards are arranged into buckets (Trello lists) each bucket representing how far in the future it&amp;#8217;s cards will get done. The further out the bucket, the larger and less well-groomed it may be.&lt;/p&gt;

&lt;p&gt;Lists are arranged left-to-right so that the most frequently used lists are on the first screen for most displays, the less used lists being to the right on the second screen. They also follow a logical progression of &lt;em&gt;queued -&gt; doing -&gt; done&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://mrdanadams.com/images/trello/board.png"&gt;&lt;img src='http://mrdanadams.com/images/trello/board_600x400.png' /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Dec 2012:&lt;/strong&gt; All of the priority cards for this month based on business priorities and other factors. This will map to multiple staging or production releases (even multiple releases each week) but all of the development in this month will be pulled from this bucket. Cards in this list are sorted roughly in order of priority. Most are assigned owners.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Jan 2013:&lt;/strong&gt; Similar to the Dec list, this is for the following month but less well defined.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;General Backlog:&lt;/strong&gt; This list represents a larger backlog to be pulled from into the monthly lists. It contains all things that should get done at some point but didn&amp;#8217;t make the cut. It can be thought of as the mid-term roadmap.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Future:&lt;/strong&gt; A holding area for future ideas. When you have an idea, don&amp;#8217;t be afraid to make a card for it. File it away so it doesn&amp;#8217;t get lost. This is list is groomed the least often.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;In addition to the time frame-based lists, there are a few special ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Inbox:&lt;/strong&gt; All new cards go here. Cards are held until they can get triaged and moved somewhere else. This keeps the dev team sane and ensures the non-devs know their cards aren&amp;#8217;t getting lost. It should be empty most of the time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;On Deck:&lt;/strong&gt; Cards pulled from the current monthly milestone that are next up for development. You can think of this roughly as a weekly iteration list. This list should be fairly short and reviewed by the team every couple days (or even daily).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Doing:&lt;/strong&gt; Cards pulled from On Deck that are being worked on.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Done:&lt;/strong&gt; Completed cards. Note that we don&amp;#8217;t have lists indicating the status of a done card (in testing, deployed, etc). This is only because we haven&amp;#8217;t needed it but for some teams managing the work flow around Done cards is probably a good idea. For instance, you may need explicit design QA on a feature and there could be a list for those cards.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The Done list gets some special treatment: whenever a production release is made we rename the &amp;#8220;Done&amp;#8221; to something like &amp;#8220;Dec 3, 2012 Release&amp;#8221;, archive it, and create a new &amp;#8220;Done&amp;#8221; list. This captures for posterity what was released and allows anyone to know what&amp;#8217;s done so far in the current release just by looking at the Done list. You may also want to tag the card with the git release tag.&lt;/p&gt;

&lt;h2&gt;Anatomy of a Good Card&lt;/h2&gt;

&lt;p&gt;Well-formed cards encourage getting the work done smoothly and accurately. While you may not need all of the following on each card, I find myself using using all of these to some degree overall.&lt;/p&gt;

&lt;h3&gt;Card Title, Tags, and Labels&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/trello/card-top.png" alt="A good Trello card title" /&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep the title succinct. Put the long stuff in the card description. A long title will severly reduce how many cards you can see at once in your lists.&lt;/li&gt;
&lt;li&gt;Tags for things like which users it affects, browser, platform, if it&amp;#8217;s an epic or story, etc&lt;/li&gt;
&lt;li&gt;Use labels to visually indicate traits of the card and allow card filtering&lt;/li&gt;
&lt;li&gt;Work estimate. A good convention is &amp;#8220;(5) Do something&amp;#8221; especially when it&amp;#8217;s supported by &lt;a href="https://chrome.google.com/webstore/detail/trello-scrum/jdbcdblgjdpmfninkoogcfpnkjmndgje"&gt;Trello Scrum&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;&lt;img src="http://mrdanadams.com/images/trello/story-title.png" alt="Trello card with estimate" /&gt;&lt;/p&gt;

&lt;h3&gt;Card Description&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Be sure to write one unless the title, tags, and labels provide everything needed to get it done&lt;/li&gt;
&lt;li&gt;Take full advantage of markdown. Include code snippets, samples of log files, bulleted lists, headers, etc.&lt;/li&gt;
&lt;li&gt;Write a full spec for complex cards. Provide implementation hints / details.&lt;/li&gt;
&lt;li&gt;Include reasons &lt;em&gt;why&lt;/em&gt; the card is being done based on team discussion. This info tends to get lost and can be really valuable later.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Screenshots &amp;amp; Attachments&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/trello/attachments.png" alt="Trello attachments" /&gt;&lt;/p&gt;

&lt;p&gt;Don&amp;#8217;t try to describe a bug with words if it&amp;#8217;s visual. Just don&amp;#8217;t. Take a screenshot (or multiple screenshots) with something like &lt;a href="http://evernote.com/skitch/"&gt;Skitch&lt;/a&gt; and attach it. Especially during design QA, designers can get across exactly what they&amp;#8217;re are talking about.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/trello/card.png" alt="Trello card with image" class="alignright" /&gt; For new features, attach images of the designs for reference even if the PSDs live in Dropbox. A side effect is the visual appeal it can add to your lists.&lt;/p&gt;

&lt;h3&gt;Checklist(s)&lt;/h3&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/trello/checklists.png" alt="Trello card checklists" /&gt;&lt;/p&gt;

&lt;p&gt;Use checklists to guide work and track detail progress.&lt;/p&gt;

&lt;h2&gt;Trello and git&lt;/h2&gt;

&lt;p&gt;We have two primary git branches: &lt;em&gt;master&lt;/em&gt; (which always represents what&amp;#8217;s on production) and &lt;em&gt;development&lt;/em&gt; which is the current release under development and is frequently pushed to staging. Trivial and small cards can be done right on development but larger cards (or those involving multiple commits) get moved to their own feature branch. While not displayed prominently, Trello cards actually do have unique, auto-incrementing numbers:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/trello/trello_card_number.png" alt="Trello card numbers" /&gt;&lt;/p&gt;

&lt;p&gt;All feature branches map back to a Trello card based on the card number (ex: &lt;em&gt;322_my_awesome_new_feature&lt;/em&gt;). You could also include the initials of the person that created the branch in a larger team.&lt;/p&gt;

&lt;h2&gt;Labels, Tags, and Finding Your Cards&lt;/h2&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/trello/trello_labels.png" alt="BlockAvenue Trello labels" class="alignright" /&gt;&lt;/p&gt;

&lt;p&gt;Trello lets you easily filter by person or label. You only get six labels so use them wisely; it&amp;#8217;s a pain to go back and change later. Here&amp;#8217;s the labels we used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Data&lt;/strong&gt;, &lt;strong&gt;App&lt;/strong&gt;, &lt;strong&gt;API&lt;/strong&gt;, &lt;strong&gt;Admin&lt;/strong&gt;: Major system components&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bug&lt;/strong&gt;: Something that&amp;#8217;s broken.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Needs Review&lt;/strong&gt;: Needs clarification or discussion and isn&amp;#8217;t ready to be worked on. When cards are pulled into &lt;em&gt;On Deck&lt;/em&gt; none should have this label.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Keep in mind that one of the best thing about labels is that they are color coded making it good for visually distinguishing cards when the lists are unfiltered.&lt;/p&gt;

&lt;p&gt;You will inevitably come up with ways you want to filter your cards that don&amp;#8217;t fit owners, due dates, or labels. While Trello doesn&amp;#8217;t have direct support for tags it does have a nice text search. It only searches the title and ignores any non-alphanumeric characters. With &lt;em&gt;#sometag&lt;/em&gt; on the end of a card title you can see that a card is tagged and, as long as you pick a tag name that isn&amp;#8217;t common to your card titles, you can filter by it.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/trello/tags.png" alt="Trello tags" /&gt;&lt;/p&gt;

&lt;p&gt;Suggestions for tags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Browser-specific:&lt;/strong&gt; #ie9, #ff, #safari&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User group:&lt;/strong&gt; #dweller, #mover, #press&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Card types:&lt;/strong&gt; #epic, #story&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Checklists Rock&lt;/h2&gt;

&lt;p&gt;Lists are awesome. Sometimes it seems my whole life is managed by making lists of things. Trello is no different. Tips on using checklists:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When grooming the backlog, create the checklist for a card to get a sense of what&amp;#8217;s involved.&lt;/li&gt;
&lt;li&gt;Use multiple checklists with custom titles to separate between components of a card (frontend, backend) or between roles (dev, design).&lt;/li&gt;
&lt;li&gt;You can&amp;#8217;t (yet) drag TODOs betweens checklists. Bummer.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Dealing with Long Lists&lt;/h2&gt;

&lt;p&gt;Lists can get long, especially if you attach images or use long card titles. Unfortunately, there isn&amp;#8217;t a &amp;#8216;send to top&amp;#8217; for cards but if you drop a card on a list quickly it will shoot to the bottom of the list. Try to keep your titles succinct or even reducing the browser font size as small as you can stand it. It&amp;#8217;s a hack, but it works.&lt;/p&gt;

&lt;h2&gt;Card Estimates&lt;/h2&gt;

&lt;p&gt;This is, perhaps, the single biggest thing I miss in Trello; the ability to put a work estimate on a card and see estimate totals for each list. Even with a small backlog, tracking estimates can add much needed control to the process. &lt;a href="http://bluelinegamestudios.com/trello/"&gt;this tool&lt;/a&gt; allows you to track estimates and graph them but it&amp;#8217;s not a great solution: the estimates should be managed in Trello.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://chrome.google.com/webstore/detail/trello-scrum/jdbcdblgjdpmfninkoogcfpnkjmndgje"&gt;Trello Scrum&lt;/a&gt; looks promising although I don&amp;#8217;t believe it does any kind of burn visualization.&lt;/p&gt;

&lt;p&gt;&lt;img src="https://lh4.googleusercontent.com/U9TS8KIksA5LyG0u2AGSmB6HNBQuRS1Xx2Zsf5j74AW2FHCT4WKhnXG372FGqi0O7NE7qrgIew=s640-h400-e365" alt="Trello Scrum" /&gt;&lt;/p&gt;

&lt;h2&gt;Shortcuts&lt;/h2&gt;

&lt;p&gt;If you aren&amp;#8217;t using &lt;a href="https://trello.com/shortcuts"&gt;Trello shortcuts&lt;/a&gt;, you should be. They will save you a massive amount of time when grooming the backlog.&lt;/p&gt;

&lt;h2&gt;Team Friction&lt;/h2&gt;

&lt;p&gt;We struggled with effectively using Trello to make the business stakeholders self-sufficient in answering the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How much is left and when will it get done?&lt;/li&gt;
&lt;li&gt;Given our current team, how much can we get done in the next N weeks?&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;The following goes a long way to solving this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Good team communication and regular review of the lists&lt;/li&gt;
&lt;li&gt;Good card and list hygiene: well-formed cards and groomed lists&lt;/li&gt;
&lt;li&gt;Date or goal-oriented lists&lt;/li&gt;
&lt;li&gt;Card estimates and tracking team velocity&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;For Hackers: Card JSON and Trello API&lt;/h2&gt;

&lt;p&gt;Trello&amp;#8217;s beauty is in it&amp;#8217;s simplicity. When it doesn&amp;#8217;t allow you to do something you always have the option of using the &lt;a href="https://trello.com/docs/api/index.html"&gt;Trello API&lt;/a&gt;. Tucked away under the card details is a link to the card JSON:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/trello/card-json.png" alt="Trello card JSON" /&gt;&lt;/p&gt;

&lt;h2&gt;When I Wouldn&amp;#8217;t Use Trello&lt;/h2&gt;

&lt;p&gt;Trello is simple and necessarily limited. In a more complex project I could see easily outgrowing it. &lt;a href="http://www.atlassian.com/software/jira/overview/"&gt;OnDemand JIRA&lt;/a&gt; with &lt;a href="http://www.atlassian.com/software/greenhopper/overview"&gt;GreenHopper&lt;/a&gt; is a great alternative. Unlike Trello, it&amp;#8217;s not free but it&amp;#8217;s the Swiss Army knife of ticket tracking as far as I&amp;#8217;m concerned. Between Trello and JIRA I have a hard time recommending anything else (having used most of the others).&lt;/p&gt;

&lt;p&gt;An example where Trello might not fit is a software platform that needed to track things across versions of the software. A REST API with multiple versions could need maintenance fixes and releases against old versions. In JIRA, this is relatively trivial with version tracking. That&amp;#8217;s not to say you &lt;em&gt;couldn&amp;#8217;t&lt;/em&gt; do it with Trello but it could get messy very quickly.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=EnIC--wEmfM:icYNnUuzGj4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=EnIC--wEmfM:icYNnUuzGj4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=EnIC--wEmfM:icYNnUuzGj4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=EnIC--wEmfM:icYNnUuzGj4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/EnIC--wEmfM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/trello-agile/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[5-10x Speed Ups by Pipeling Multiple REDIS Commands in Ruby]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/BtFDdtk0e7o/" />
    <updated>2012-11-08T14:36:00-05:00</updated>
    <id>http://mrdanadams.com/2012/pipeline-redis-commands-ruby</id>
    <content type="html">&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This is now on github at &lt;a href="https://github.com/mrdanadams/redis_pipeliner"&gt;https://github.com/mrdanadams/redis_pipeliner&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://redis.io"&gt;REDIS&lt;/a&gt; is fast. Really fast. And awesome. But that doesn&amp;#8217;t mean you can get lazy with how you use it. &lt;a href="http://redis.io/topics/pipelining"&gt;Pipelining REDIS commands&lt;/a&gt; allows you to execute multiple commands in REDIS and return the results at once. I&amp;#8217;ve seen this provide a 5x-10x speedup. He&amp;#8217;s how to do it with the Ruby driver.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;Why You Should Pipeline&lt;/h2&gt;

&lt;p&gt;(If your code isn&amp;#8217;t performance sensitive the following is probably unnecessary.) When working with REDIS, look out for code blocks that look like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;ask REDIS for something&lt;/li&gt;
&lt;li&gt;do something with it&lt;/li&gt;
&lt;li&gt;repeat&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Depending on the number of calls you make, you could be paying a lot in over-the-wire and connection overhead (even within the same data center). A better model is to do something like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;collect all your commands&lt;/li&gt;
&lt;li&gt;send them to REDIS&lt;/li&gt;
&lt;li&gt;unpack the result and do something with it&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Even with REDIS being very, very fast and the server being close to the client, this can lead to significant improvements in speed. I&amp;#8217;ve seen a handful of commands (&amp;lt; 10) pipelined to drop a 250ms page render to 50ms.&lt;/p&gt;

&lt;h2&gt;Get Your Pipe On&lt;/h2&gt;

&lt;p&gt;Here&amp;#8217;s the general structure of using the Ruby driver to pipeline multiple REDIS commands. When you execute a command, rather than getting the result you get a &lt;a href="https://github.com/redis/redis-rb#futures"&gt;Future&lt;/a&gt; object which you can ask for the result when it&amp;#8217;s available.&lt;/p&gt;

&lt;p&gt;Using Future comes with a few challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you are executing the command based on some context (such as a related object) you need that context when you get the result&lt;/li&gt;
&lt;li&gt;Future doesn&amp;#8217;t provide a nice way of waiting for the value to be realized or even checking if it has been&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;In this example, we&amp;#8217;ll iterate through a number of objects each one needing a REDIS hash.&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;span class='line-number'&gt;19&lt;/span&gt;
&lt;span class='line-number'&gt;20&lt;/span&gt;
&lt;span class='line-number'&gt;21&lt;/span&gt;
&lt;span class='line-number'&gt;22&lt;/span&gt;
&lt;span class='line-number'&gt;23&lt;/span&gt;
&lt;span class='line-number'&gt;24&lt;/span&gt;
&lt;span class='line-number'&gt;25&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="c1"&gt;# we&amp;#39;ll collect a number of Futures with their related context&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="c1"&gt;# Let&amp;#39;s pretend have a number of objects and need to get their associated data&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;cmds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;objects&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="c1"&gt;# we&amp;#39;ll pretend this isn&amp;#39;t empty&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="c1"&gt;# Create the pipelined commands. Any REDIS commands executing in this block will return Futures rather than values&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pipelined&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;cmds&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hgetall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="c1"&gt;# Now that we have our commands executing we need to wait for the values to come back&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="n"&gt;cmds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# wait for each one to come back before proceeding. this is not ideal but simple and seems to work fine.&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:future&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Redis&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FutureNotReady&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:future&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:object&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="c1"&gt;# do something with the REDIS return value and our context&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;h2&gt;Improvements&lt;/h2&gt;

&lt;p&gt;You can see that the model here is pretty disjointed and makes for less-maintainable code. The problem is having to break your code up causing you to lose context. Something like this might make more sense (&lt;strong&gt;warning&lt;/strong&gt; untested ruby pseudocode ahead):&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="no"&gt;Pipeliner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enqueue&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hgetall&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;&lt;code&gt;Pipeline&lt;/code&gt; could look like this:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;span class='line-number'&gt;19&lt;/span&gt;
&lt;span class='line-number'&gt;20&lt;/span&gt;
&lt;span class='line-number'&gt;21&lt;/span&gt;
&lt;span class='line-number'&gt;22&lt;/span&gt;
&lt;span class='line-number'&gt;23&lt;/span&gt;
&lt;span class='line-number'&gt;24&lt;/span&gt;
&lt;span class='line-number'&gt;25&lt;/span&gt;
&lt;span class='line-number'&gt;26&lt;/span&gt;
&lt;span class='line-number'&gt;27&lt;/span&gt;
&lt;span class='line-number'&gt;28&lt;/span&gt;
&lt;span class='line-number'&gt;29&lt;/span&gt;
&lt;span class='line-number'&gt;30&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Pipeliner&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="vi"&gt;@redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="vi"&gt;@cmds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="vi"&gt;@cmds&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;future&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="vi"&gt;@cmds&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:future&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Redis&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;FutureNotReady&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;        &lt;span class="nb"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:callback&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:future&lt;/span&gt;&lt;span class="o"&gt;].&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="c1"&gt;# Executes callbacks with each result. This blocks.&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;pipeliner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Pipeliner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pipelined&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;      &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="n"&gt;pipeliner&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;    &lt;span class="n"&gt;pipeliner&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;

&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=BtFDdtk0e7o:ziU1crxQj40:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=BtFDdtk0e7o:ziU1crxQj40:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=BtFDdtk0e7o:ziU1crxQj40:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=BtFDdtk0e7o:ziU1crxQj40:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/BtFDdtk0e7o" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/pipeline-redis-commands-ruby/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Embedding Backbone Templates in Rails with CoffeeScript]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/BzO3oFI8ymY/" />
    <updated>2012-10-12T12:30:00-04:00</updated>
    <id>http://mrdanadams.com/2012/backbone-templates-rails-coffeescript</id>
    <content type="html">&lt;p&gt;&lt;a href="http://coffeescript.org/#strings"&gt;Multiline string support in CoffeeScript&lt;/a&gt; offers a great way to embed your &lt;a href="http://backbonejs.org/#View-render"&gt;Backbone templates&lt;/a&gt;. In this approach, you can easily edit your templates while still taking advantage of JS minification, combination, and client-side caching.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;Registering and caching templates&lt;/h2&gt;

&lt;p&gt;We&amp;#8217;ll create a top-level object in our namespace to hold our templates which are cached for efficient use across many view instances.&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/3880219.js?file=templates.coffee'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;# Our top-level namespace (which I'll just call NS for sample purposes)
NS = @NS
NS.Templates =
  templates: {} # empty object to cache compiled templates

  # gets a compiled template rendering by ID
  get: (id) -&amp;gt; @templates[id]

  # caches a new template
  register: (id, source) -&amp;gt;
    @templates[id] = _.template(source)

# Register a template embedding the sources in a multi-line string
NS.Templates.register &amp;quot;ourAwesomeTemplate&amp;quot;, &amp;quot;&amp;quot;&amp;quot;
&amp;lt;div class=&amp;quot;inner&amp;quot;&amp;gt;
  &amp;lt;h3&amp;gt;&amp;lt;%= name %&amp;gt;&amp;lt;/h3&amp;gt;
&amp;lt;/div&amp;gt;
&amp;quot;&amp;quot;&amp;quot;

NS.Templates.register &amp;quot;anotherAwesomeTemplate&amp;quot;, &amp;quot;&amp;quot;&amp;quot;
&amp;lt;span&amp;gt;&amp;lt;%= foo %&amp;gt;&amp;lt;/span&amp;gt;
&amp;quot;&amp;quot;&amp;quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;h2&gt;Using the templates&lt;/h2&gt;

&lt;p&gt;An example of using the template in a Backbone view:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/3880219.js?file=view.coffee'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;NS = @NS
NS.View.SomethingGreat = @Backbone.View.extend
  className:'my-awesome-thing'

  initialize: (options) -&amp;gt;
    # could also do this in render()
    @template = NS.Templates.get('ourAwesomeTemplate')
    this

  render: -&amp;gt;
    $(@el).html(@template(@model.toJSON()))
    this
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;In this case I&amp;#8217;m registering and caching our templates on load. If we had more templates, or they were used less often, we could register them to be compiled on-demand which might be more efficient.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=BzO3oFI8ymY:mNuyKUdwzGE:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=BzO3oFI8ymY:mNuyKUdwzGE:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=BzO3oFI8ymY:mNuyKUdwzGE:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=BzO3oFI8ymY:mNuyKUdwzGE:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/BzO3oFI8ymY" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/backbone-templates-rails-coffeescript/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[MongoHQ vs MongoLab: Selecting a Hosted MongoDB Provider]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/VzmciKlfCxg/" />
    <updated>2012-09-26T10:19:00-04:00</updated>
    <id>http://mrdanadams.com/2012/mongohq-mongolab-mongodb-customer-service</id>
    <content type="html">&lt;p&gt;&lt;img class="feature" src="http://mrdanadams.com/images/mongohq_mongolab.jpg" title="MongoLab vs MongoHQ" &gt;&lt;/p&gt;

&lt;p&gt;In the course of building &lt;a href="http://blockavenue.com"&gt;BlockAvenue&lt;/a&gt;, one of the decisions I made was whether to host our own &lt;a href="http://www.mongodb.org"&gt;MongoDB&lt;/a&gt; or use a hosted provider. Here&amp;#8217;s the short tale of what I selected and the importance of top-tier customer service.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;Self-hosted or managed hosting?&lt;/h2&gt;

&lt;p&gt;We were under intense deadline, had no resources to spend setting up or maintaining a MongoDB cluster, needed no heavy customizations, and needed the database up and running yesterday. Since the app was Heroku hosted, we also needed some control over which region it would be located in. The decision to use a hosted service was a no-brainer even if it meant higher costs for the EC2 instances in the short term. &lt;a href="https://www.mongohq.com"&gt;MongoHQ&lt;/a&gt; and &lt;a href="https://mongolab.com"&gt;MongoLab&lt;/a&gt; were clearly the top two choices. But which provider to use? And which size deployment?&lt;/p&gt;

&lt;h2&gt;A first-look at MongoHQ and MongoLab&lt;/h2&gt;

&lt;p&gt;My first impression comparing MongoHQ and MongoLab put me slightly in favor of MongoLab. In fact, we used the free plan on MongoLab for a short period (and I&amp;#8217;ve used them for small stuff on other projects). As a customer walking in the door, I found the MongoLab pricing and info about their offering much clearer and more informative. Even when choosing a database, having clear pricing and product info is important and eye candy makes some difference. The MongoHQ page provides the basics on pricing but not much else.&lt;/p&gt;

&lt;p&gt;With their dedicated plans, for instance, you know the pricing for a Large instance without a replica set ($525 / mo) and the Extra Large instance with replica set ($1,950 / mo). But what about starting with a single Extra Large instance and going to replica set later? How do I expand my cluster? What&amp;#8217;s the process and costs involved?&lt;/p&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/mongohq_pricing.png" title="MongoHQ pricing" &gt;&lt;/p&gt;

&lt;p&gt;The MongoLab documentation is much clearer and their technical architecture page (aka &amp;#8220;What will I get?&amp;#8221;) makes a &lt;strong&gt;lot&lt;/strong&gt; of sense is and very well done. Overall, MongoLab was more informative and, frankly, better looking. Even when choosing a back end technology, visual goodness conveys a sense of quality.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/mongolab_pricing.png" title="MongoLab pricing" &gt;&lt;/p&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/mongolab_arch.jpg" title="MongoLab architecture" &gt;&lt;/p&gt;

&lt;h2&gt;Interaction with a real human&lt;/h2&gt;

&lt;p&gt;At this point, I&amp;#8217;m on the fence and out of time; I need a deployment up, my data loaded, and to start setting up the production environment. I still have big questions about the specifics of the data we are handling; large amounts of geospatial data with very fast response times and a lot of queries.&lt;/p&gt;

&lt;p&gt;I email &lt;a href="http://www.10gen.com"&gt;10gen&lt;/a&gt; asking to get on the phone with someone to ask a few questions about index structure, the amount of memory needed and overhead to plan for, and other performance concerns. Within 30 minutes I am on the phone with a 10gen engineer. Awesome. I could not have had a better experience with 10gen and would absolutely recommend people with non-trivial deployments consider a support contract or initial consultation.&lt;/p&gt;

&lt;p&gt;Having my strategy worked out, I need to select a vendor and get the deployment up. I fire off emails to MongoHQ and MongoLab. Within 10 minutes, a real person with real experience and knowledge of MongoDB calls my cell (Chris from Birmingham). Chris is awesome; I explain our project particulars and he immediately comes back with solid questions and recommendations based on their experiences with other customers. 15 minutes on the phone and I am ready to pull the trigger. This was, without a doubt, one of the best customer experiences I&amp;#8217;ve ever had (I sent him an email later saying as much).&lt;/p&gt;

&lt;p&gt;I walk back to my desk, create my account, set up a new deployment, wait for the EC2 instances to come up (which took a few minutes), create my database, and am off and running. 30 minutes in and I have my instance with GBs of data and many millions of documents loaded and serving our Heroku-hosted app. Everything should be this easy. Rock on, MongoHQ.&lt;/p&gt;

&lt;p&gt;I hear back from MongoLab several hours later (which, admittedly, isn&amp;#8217;t &lt;strong&gt;that&lt;/strong&gt; bad) but MongoHQ beat them to it and totally delighted me with their customer service. My production db was up and serving our app by the time MongoLab responded.&lt;/p&gt;

&lt;h2&gt;Living with MongoHQ&lt;/h2&gt;

&lt;p&gt;A few days later, we need to make the move to a replica set in preparation for &lt;a href="http://blog.blockavenue.com/corporate/we-are-live-n-kickin/"&gt;go-live&lt;/a&gt;. I email support with specific questions about cost, process, time line, and scaling up / down as needed based on traffic (&lt;a href="http://techcrunch.com/2012/09/20/blockavenue-launch"&gt;we knew we were going to get TechCrunch&amp;#8217;d&lt;/a&gt;). Again, I have a response within minutes. A day or two later, on a Saturday, we circle back on this and are ready to make a decision. Email is sent to support giving them the go-ahead to both increase the instance size and create the replica set. Response was that they are on it but may not get to it by Monday. A few hours later, I&amp;#8217;m delighted to see an email that the new cluster is up with the URLs for each server. Again, MongoHQ goes above expectations for customer support.&lt;/p&gt;

&lt;p&gt;Once up and running, the clusters are largely self-service and we&amp;#8217;ve had no problems so far. An admin panel shows the status of each node, connection and database info, and what&amp;#8217;s happening on each instance now and in recent history. The old admin is a bit ugly and dated but the new admin (in alpha) is already awesome. It&amp;#8217;s also responsive meaning it works well as a large view showing and as a mini dashboard. In fact, on launch day, I left it running in the corner of my second monitor all day to keep an eye on things.&lt;/p&gt;

&lt;h2&gt;Final thoughts&lt;/h2&gt;

&lt;p&gt;In the end, the offerings between MongoHQ and MongoLab may be marginally different, at least in terms of pricing and technology. However, MongoHQ support has so far proven to be top notch. For many of my clients without IT or dev ops resources, this makes the price increase over running their own MongoDB cluster on EC2 well worth it.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=VzmciKlfCxg:iZ3vPWLLzo4:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=VzmciKlfCxg:iZ3vPWLLzo4:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=VzmciKlfCxg:iZ3vPWLLzo4:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=VzmciKlfCxg:iZ3vPWLLzo4:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/VzmciKlfCxg" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/mongohq-mongolab-mongodb-customer-service/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Creating a custom OpenLayers build by profiling usage]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/PDlD-8CV1fU/" />
    <updated>2012-08-18T12:45:00-04:00</updated>
    <id>http://mrdanadams.com/2012/custom-openlayers-build</id>
    <content type="html">&lt;p&gt;&lt;a href="http://openlayers.org/"&gt;OpenLayers&lt;/a&gt; is a great library for integrating maps especially when creating custom mapping interfaces. However, with great functionality can come great size. Weighing in at nearly 1MB for the full library, you should pair it down to only the modules used.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mrdanadams/openlayers-instrumenter"&gt;openlayers-instrumenter&lt;/a&gt; creates a custom OpenLayers build profile by instrumenting the JavaScript dynamically and tracking what&amp;#8217;s used.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;Instrument the OpenLayers JS&lt;/h2&gt;

&lt;p&gt;Load the instrumenter after OpenLayers but before using it:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='html'&gt;&lt;span class='line'&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;lib/OpenLayers-2.12/lib/OpenLayers.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;lib/openlayers-instrumenter.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;you-app-code.js&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;  &lt;strong&gt;Note:&lt;/strong&gt; It&amp;#8217;s not for production use so only include it while creating the build profile.&lt;/p&gt;

&lt;p&gt;Load your page. About 2 seconds after page load, you&amp;#8217;ll see something like this console.log&amp;#8217;d:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;span class='line-number'&gt;7&lt;/span&gt;
&lt;span class='line-number'&gt;8&lt;/span&gt;
&lt;span class='line-number'&gt;9&lt;/span&gt;
&lt;span class='line-number'&gt;10&lt;/span&gt;
&lt;span class='line-number'&gt;11&lt;/span&gt;
&lt;span class='line-number'&gt;12&lt;/span&gt;
&lt;span class='line-number'&gt;13&lt;/span&gt;
&lt;span class='line-number'&gt;14&lt;/span&gt;
&lt;span class='line-number'&gt;15&lt;/span&gt;
&lt;span class='line-number'&gt;16&lt;/span&gt;
&lt;span class='line-number'&gt;17&lt;/span&gt;
&lt;span class='line-number'&gt;18&lt;/span&gt;
&lt;span class='line-number'&gt;19&lt;/span&gt;
&lt;span class='line-number'&gt;20&lt;/span&gt;
&lt;span class='line-number'&gt;21&lt;/span&gt;
&lt;span class='line-number'&gt;22&lt;/span&gt;
&lt;span class='line-number'&gt;23&lt;/span&gt;
&lt;span class='line-number'&gt;24&lt;/span&gt;
&lt;span class='line-number'&gt;25&lt;/span&gt;
&lt;span class='line-number'&gt;26&lt;/span&gt;
&lt;span class='line-number'&gt;27&lt;/span&gt;
&lt;span class='line-number'&gt;28&lt;/span&gt;
&lt;span class='line-number'&gt;29&lt;/span&gt;
&lt;span class='line-number'&gt;30&lt;/span&gt;
&lt;span class='line-number'&gt;31&lt;/span&gt;
&lt;span class='line-number'&gt;32&lt;/span&gt;
&lt;span class='line-number'&gt;33&lt;/span&gt;
&lt;span class='line-number'&gt;34&lt;/span&gt;
&lt;span class='line-number'&gt;35&lt;/span&gt;
&lt;span class='line-number'&gt;36&lt;/span&gt;
&lt;span class='line-number'&gt;37&lt;/span&gt;
&lt;span class='line-number'&gt;38&lt;/span&gt;
&lt;span class='line-number'&gt;39&lt;/span&gt;
&lt;span class='line-number'&gt;40&lt;/span&gt;
&lt;span class='line-number'&gt;41&lt;/span&gt;
&lt;span class='line-number'&gt;42&lt;/span&gt;
&lt;span class='line-number'&gt;43&lt;/span&gt;
&lt;span class='line-number'&gt;44&lt;/span&gt;
&lt;span class='line-number'&gt;45&lt;/span&gt;
&lt;span class='line-number'&gt;46&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='html'&gt;&lt;span class='line'&gt;[first]
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;[last]
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;[include]
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Map.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/BaseTypes/Size.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/BaseTypes/Bounds.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Util.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/BaseTypes/Element.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Events.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Function.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Event.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/String.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Control/Navigation.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Control/Zoom.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Control/ArgParser.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Control/Attribution.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Handler/Click.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Control/DragPan.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Control/ZoomBox.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Handler/Drag.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Handler/Box.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Handler/MouseWheel.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Control/PinchZoom.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Handler/Pinch.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Events/buttonclick.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Layer/WMS.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Layer/Vector.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Renderer/SVG.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/StyleMap.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Style.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Projection.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Control/LayerSwitcher.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Lang.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Control/MousePosition.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Control/DrawFeature.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Handler/Point.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Handler/Path.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Handler/Polygon.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Handler/RegularPolygon.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/BaseTypes/LonLat.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/Animation.js
&lt;/span&gt;&lt;span class='line'&gt;OpenLayers/BaseTypes/Pixel.js
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;[exclude]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;This is the build profile used by OpenLayers to create a custom distribution for your app.&lt;/p&gt;

&lt;p&gt;If you load new OpenLayers classes during page use, exercise those functions then call &lt;code&gt;OLI.createProfile()&lt;/code&gt; in the console to dump the updated profile.&lt;/p&gt;

&lt;h2&gt;Using the OpenLayers build profile&lt;/h2&gt;

&lt;p&gt;Save the build profile into &lt;code&gt;OpenLayers-2.12/build/custom.cfg&lt;/code&gt;. OpenLayers ships with a script to load the profile and create the distribution:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;OpenLayers-2.12/build/
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;./build.py custom
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;The compiled file is put in the same directory as &lt;code&gt;OpenLayers.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The full build includes &lt;strong&gt;261 files&lt;/strong&gt; weighing in at whopping &lt;strong&gt;942 KB&lt;/strong&gt; (minified!). For most builds it will probably be reduced to about &lt;strong&gt;60 files&lt;/strong&gt; totaling &lt;strong&gt;271 KB&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;Room for improvement&lt;/h2&gt;

&lt;p&gt;A &amp;#8220;reduced&amp;#8221; build of nearly 300 KB minified is still quite large. I&amp;#8217;ve used it in a Rails app targeted for the iPad and it worked great. Using a custom build made a noticable difference both in page load speed and responsivess when using the UI. However, I&amp;#8217;d be surprised if we didn&amp;#8217;t see a mapping microlibrary or two give OpenLayers a run for it&amp;#8217;s money.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=PDlD-8CV1fU:Drlfbr2WLng:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=PDlD-8CV1fU:Drlfbr2WLng:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=PDlD-8CV1fU:Drlfbr2WLng:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=PDlD-8CV1fU:Drlfbr2WLng:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/PDlD-8CV1fU" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/custom-openlayers-build/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Migrating a WordPress custom theme to Octopress / Jekyll]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/A2OfE-BpS3Q/" />
    <updated>2012-07-26T16:51:00-04:00</updated>
    <id>http://mrdanadams.com/2012/migrating-wordpress-custom-theme-octopress-jekyll</id>
    <content type="html">&lt;p&gt;I decided to move from a VPS-hosted Wordpress with a custom theme to Octopress. Here&amp;#8217;s how I did it, why, some things to watch out for, and a comparison of the experience.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;Why are you hating on Wordpress?&lt;/h2&gt;

&lt;p&gt;I&amp;#8217;m not. Wordpress is popular for good reason and I&amp;#8217;ve used it for a while. However, it&amp;#8217;s not without it&amp;#8217;s warts and I was often left wanting. Here&amp;#8217;s what I require from whatever is running my blog:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating posts should be easy&lt;/li&gt;
&lt;li&gt;Hosting should be &lt;a href="http://mrdanadams.com/2011/how-to-host-all-your-projects-for-6-per-month"&gt;very cheap&lt;/a&gt; ($s / month or even free)&lt;/li&gt;
&lt;li&gt;It should be fast. The output should be optimized for better user experience and SEO. Page load times should be low. &lt;a href="https://developers.google.com/speed/pagespeed/"&gt;Google PageSpeed&lt;/a&gt; rating should be &gt; 90%.&lt;/li&gt;
&lt;li&gt;I want to spend time authoring, not maintaining. I don&amp;#8217;t want to think about if my blog is running or not. I certainly don&amp;#8217;t want to spend my evening repairing a hacked Wordpress or fixing a failed upgrade.&lt;/li&gt;
&lt;li&gt;I want to be able to customize the theme or functionality if needed.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;It&amp;#8217;s easy to get most of these with Wordpress but difficult to get all. For instance, you could get a cheap and fast hosting service but you may be limited in what you can customize (themes and plugins). &lt;a href="http://wpengine.com/pricing/"&gt;Managed services&lt;/a&gt; that provide this often start in the range of &lt;a href="https://purchase.websynthesis.com/plans.aspx?o=non"&gt;$27 / mo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;A VPS with nice specs can be had for $6 / mo but you may still have performance issues in a shared environment. This also requires you to setup (and maintain) everything yourself.&lt;/p&gt;

&lt;p&gt;Octopress gets much closer to meeting all the requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hosting static files is fast, secure, and cheap or even free (with &lt;a href="http://pages.github.com/"&gt;github pages&lt;/a&gt; or the &lt;a href="http://aws.amazon.com/free/"&gt;AWS free usage tier&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Once it&amp;#8217;s up and running, it&amp;#8217;s set and forget&lt;/li&gt;
&lt;li&gt;Octopress themes are easy to customize and writing plugins for Jekyll or Liquid is easier than for Wordpress.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Optimization &amp;amp; performance&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://wordpress.org/extend/plugins/w3-total-cache/"&gt;W3 Total Cache&lt;/a&gt; is a great plugin. It&amp;#8217;s a one-stop-shop for best practices in client-side optimization. Despite this, getting performance on par with static files with a self-hosted Wordpress isn&amp;#8217;t trivial.&lt;/p&gt;

&lt;p&gt;Consider asset combination and minification, for instance. You need to know which scripts can be combined and which can&amp;#8217;t (such as the lightbox library that, when combined, simply broke).&lt;/p&gt;

&lt;p&gt;With a single download for each asset, using image sprites or data-uri images, the correct caching headers, and a CDN for static assets you&amp;#8217;d think that would be enough. But it&amp;#8217;s not. I found my VPS suffering odd latencies upwards of 500ms establishing a connection to the server. A simple test with &lt;a href="http://httpd.apache.org/docs/2.2/programs/ab.html"&gt;ab&lt;/a&gt; proved failure to handle large traffic spikes. The VPS provided &lt;a href="http://vpsfuze.com/pricing.html"&gt;great RAM for the money&lt;/a&gt; but the Wordpress content caching ate that up quickly.&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s back up for a second. The vast majority of Wordpress blogs consist of static content. Rendering PHP or hitting a MySQL db on each view is foolish. Why not use something like &lt;a href="http://wordpress.org/extend/plugins/static-html-output-plugin/"&gt;this plugin&lt;/a&gt; to export all content to S3 or some other service for cheap and scalable hosting? I could have thrown &lt;a href="https://www.varnish-cache.org/"&gt;varnish&lt;/a&gt; in the mix but that&amp;#8217;s a lot of moving parts for rendering static content. It&amp;#8217;s simply the wrong tool.&lt;/p&gt;

&lt;p&gt;Sometimes the old tech works best. Simplify. For blogs, serving static HTML is an elegant and simply solution. Most of the features Wordpress provides are focused on the editorial side. Why let that dictate how the content is served?&lt;/p&gt;

&lt;h3&gt;Post editing&lt;/h3&gt;

&lt;p&gt;When editing posts in Wordpress I would often write in HTML mode due to bugs in the WYSIWYG editor. For instance, it would sometimes insert errant P tags or start inserting DIVs instead of Ps altogether. It makes a difference in the rendered markup and how the post displays. It also makes the content less semantic.&lt;/p&gt;

&lt;p&gt;There are some nice features such as inserting and thumbnailing images. As you&amp;#8217;ll see, however, a more minimalist approach using Octopress is even easier.&lt;/p&gt;

&lt;h3&gt;Trackbacks&lt;/h3&gt;

&lt;p&gt;I don&amp;#8217;t really care about them. It was an interesting idea in the blog world for cross promotion but I care about real comments from real users. Trackbacks are spam at this point.&lt;/p&gt;

&lt;h3&gt;Remote / mobile posting&lt;/h3&gt;

&lt;p&gt;Not currently supported with Octopress. However, it&amp;#8217;s conceivable to have a REST API talking to authoring apps that does a git clone / pull, authors a single post, and publishes. Log in with your github account and use your existing editors. It could certainly be done. I never did this anyway so I don&amp;#8217;t miss it.&lt;/p&gt;

&lt;p&gt;Octopress, unlike Wordpress, does allow offline authoring thanks to git.&lt;/p&gt;

&lt;h3&gt;Post scheduling&lt;/h3&gt;

&lt;p&gt;The &lt;a href="http://wordpress.org/extend/plugins/editorial-calendar/"&gt;editorial calendar plugin&lt;/a&gt; is great if you author posts ahead of time and want them auto-published on a schedule.&lt;/p&gt;

&lt;p&gt;This is another instance where things often just don&amp;#8217;t work. After one of the Wordpress upgrades I noticed my posts weren&amp;#8217;t published as scheduled. Awesome. A little googling, cron jobs, and editing of PHP later it was working again. It&amp;#8217;s a great feature, when it works (which is true of a lot in Wordpress).&lt;/p&gt;

&lt;h2&gt;Making the move&lt;/h2&gt;

&lt;h3&gt;Wordpress custom theme to Octopress theme&lt;/h3&gt;

&lt;p&gt;Octopress ships with a default theme. At first glance, it feels like &lt;a href="http://mrdanadams.com/2011/tips-thematic-wordpress-theme-framework/"&gt;working with a Wordpress theme framework&lt;/a&gt;. Here, however, you copy the entire theme down and edit the files directly rather than overriding base files in the parent theme that can be upgraded later (essentially the way WP was before parent themes were introduced).&lt;/p&gt;

&lt;p&gt;The default Octopress theme is well coded and nicely structured. Like any theme, it has it&amp;#8217;s own markup structure and styles. Migrating from a custom theme (&lt;a href="http://mrdanadams.com/2011/fluid-grids-responsive-design-thematic-wordpress"&gt;child theme based on the Thematic theme framework&lt;/a&gt;), I already had my own markup and CSS that, since I&amp;#8217;m just moving the site, should ideally stay the same.&lt;/p&gt;

&lt;p&gt;So that&amp;#8217;s what I did.&lt;/p&gt;

&lt;p&gt;I started with the default theme and stripped it down to something minimal enough to fit my needs (you can also copy in files later if needed). Grabbing the rendered HTML for the pages on my WP site I made the page markup conform to that. I was able to &lt;a href="http://mrdanadams.com/2011/wordpress-theme-sass-compass/"&gt;move the SCSS&lt;/a&gt; in without any changes at all.&lt;/p&gt;

&lt;h3&gt;Featured images&lt;/h3&gt;

&lt;p&gt;Some posts have a featured image at the top. I include the images with a specific CSS class so they can be targeted:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;img featured /images/ios_xcode.jpg iOS universal frameworks with Xcode&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;h3&gt;Image thumbnails&lt;/h3&gt;

&lt;p&gt;Creating thumbnails in Wordpress is quite easy. With a little help, they are even easier in Octopress. I want to take a source image and say &amp;#8220;insert this image, optimized, into my content and make it a certain size while keeping the aspect ratio&amp;#8221;. Nothing I found did exactly that so I created a Jekyll plugin, &lt;a href="https://github.com/mrdanadams/jekyll-thumbnailer"&gt;jekyll-thumbnailer&lt;/a&gt;. Using it is quite simple:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class=''&gt;&lt;span class='line'&gt;thumbnail /path/to/local/image.png 50x50&amp;lt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;h3&gt;Compass sprites &amp;amp; image paths&lt;/h3&gt;

&lt;p&gt;If you want to use &lt;a href="http://mrdanadams.com/2011/wordpress-theme-sass-compass/"&gt;Compass sprites&lt;/a&gt; like this:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='scss'&gt;&lt;span class='line'&gt;&lt;span class="nv"&gt;$sprites&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;sprite-map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;sprites/*.png&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nc"&gt;.foo&lt;/span&gt; &lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;background&lt;/span&gt;&lt;span class="nd"&gt;:sprite&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;sprites&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;foo-icon&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;no-repeat&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;The generated sprite image will be put in the source directory. That&amp;#8217;s fine except the references in the CSS will look like &lt;code&gt;/source/images/sprites-2t3h2t31h3.png&lt;/code&gt;. To reference the images at the correct path add this to config.rb:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='scss'&gt;&lt;span class='line'&gt;&lt;span class="nt"&gt;http_generated_images_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;/images&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;h3&gt;exitwp&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/thomasf/exitwp/"&gt;exitwp&lt;/a&gt; did the general conversion (including grabbing the post images) but it was pretty rough.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I have pretty standard markup and it did things like convert lists into multiple lists (also indented incorrectly)&lt;/li&gt;
&lt;li&gt;I had to manually edit every single post&lt;/li&gt;
&lt;li&gt;It put in weird spacing&lt;/li&gt;
&lt;li&gt;It didn&amp;#8217;t preserve the more tag thus killing all my excerpts&lt;/li&gt;
&lt;li&gt;No support for images (or captions)&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;So I wouldn&amp;#8217;t say migrating the content is exactly seamless. In fact, this took the longest.&lt;/p&gt;

&lt;h3&gt;Minor observations&lt;/h3&gt;

&lt;p&gt;A few observations after doing the migration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I found broken and extra markup being generated by Wordpress&lt;/li&gt;
&lt;li&gt;I eliminated a &lt;strong&gt;ton&lt;/strong&gt; of extra CSS classes and ids from the markup to make it more minimal. Simply a side-effect of most theme frameworks.&lt;/li&gt;
&lt;li&gt;The delay to regenerate files when editing can be really, really long. Isolating files can help with that. Also, you may have to get out of the habitual saving after each edit.&lt;/li&gt;
&lt;li&gt;The default Octopress theme is well structured but verbose. I paired mine down to only a few files.&lt;/li&gt;
&lt;li&gt;I didn&amp;#8217;t care for the auto-capitalization&lt;/li&gt;
&lt;li&gt;The syntax highlighting (including the styles in the default theme) is great&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;The final setup&lt;/h2&gt;

&lt;p&gt;Once set up, Octopress is pretty great. Author a new post:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;rake new_post&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;My fancy new post&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Run pow.cx to serve the files (you only need to do this once):&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~/.pow/
&lt;/span&gt;&lt;span class='line'&gt;ln -s ~/path/to/my/octopress
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Watch for changes:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;rake watch
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Once you are ready to publish:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;rake generate
&lt;/span&gt;&lt;span class='line'&gt;rake deploy
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Don&amp;#8217;t forget to commit your source files and push to origin as well:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;git commit -a -m &lt;span class="s2"&gt;&amp;quot;i made some changes&amp;quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;git push origin &lt;span class="nb"&gt;source&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Octopress and Jekyll are both awesome. I&amp;#8217;m sure improvements could be made and I&amp;#8217;m excited to see (and help with) them in the future. Generating static sites is by no means new in the CMS world but it&amp;#8217;s a great, simple alternative to Wordpress.&lt;/p&gt;

&lt;p&gt;The sources for the blog are &lt;a href="https://github.com/mrdanadams/mrdanadams.github.com/tree/source"&gt;on github&lt;/a&gt;.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=A2OfE-BpS3Q:UZptRPWfdyI:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=A2OfE-BpS3Q:UZptRPWfdyI:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=A2OfE-BpS3Q:UZptRPWfdyI:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=A2OfE-BpS3Q:UZptRPWfdyI:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/A2OfE-BpS3Q" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/migrating-wordpress-custom-theme-octopress-jekyll/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Round-up of iOS universal frameworks with Xcode]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/mG0Eu82FKSQ/" />
    <updated>2012-04-18T13:38:39-04:00</updated>
    <id>http://mrdanadams.com/2012/ios-universal-frameworks-xcode</id>
    <content type="html">&lt;p&gt;&lt;img class="featured" src="http://mrdanadams.com/images/ios_xcode.jpg" title="iOS universal frameworks with Xcode" &gt;&lt;/p&gt;

&lt;p&gt;If you are looking to share code between projects or distribute a 3rd party framework (such as a product API) a bit of googling will find you a solution. Or two. Or three. The solutions are all similar with some subtle differences. Here I&amp;#8217;ve collected the best solutions I&amp;#8217;ve found (going back to 2008) and which one worked well for me.&lt;/p&gt;

&lt;p&gt;For the impatient, I recommend this &lt;a href="https://github.com/jverkoey/iOS-Framework"&gt;iOS universal framework&lt;/a&gt; solution.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;The problem: dynamic linked libraries, architectures and performance&lt;/h2&gt;

&lt;p&gt;All the system libraries for iOS are dynamically linked. This allows dynamic loading and sharing of libraries between applications for more efficient memory management. In order to simplify the handling of applications, this is not allowed for libraries shipped with an app even if two apps use the same library. These libraries must be statically linked. This is an issue simply because Xcode 4 does not directly support creating static iOS frameworks.&lt;/p&gt;

&lt;p&gt;Additionally, libraries need to support multiple architectures: arm6 / arm7 on the devices and&lt;a href="http://stackoverflow.com/questions/7874519/ios-simulator-on-mac-is-running-i386-architecture-not-armv7"&gt; i386 when run in the simulator&lt;/a&gt;. Distributing a fat binary allows developers to drop the framework into their project but embedding the whole fat binary in the application should be avoided as it can substantially increase the size of the deployed app.&lt;/p&gt;

&lt;p&gt;The developer experience is also important. For instance, if you are developing the framework in the context of an app that is using it, minimizing turn-around time for changes in the framework appearing in the app could be a big win (as it likely is for most).&lt;/p&gt;

&lt;h2&gt;Documented solutions&lt;/h2&gt;

&lt;h3&gt;iOS-Framework by Jeff Verkoeyen&lt;/h3&gt;

&lt;p&gt;My preferred &lt;a href="https://github.com/jverkoey/iOS-Framework"&gt;solution is by Jeff Verkoeyen&lt;/a&gt; (jverkoey). It&amp;#8217;s an evolution on the older solutions and focuses on an easier, faster build process while maintaining a good integration experience for users and optimal deployment. It still has some improvements to be made:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A manual process. It&amp;#8217;s easy, but not exactly a 1-step process. He&amp;#8217;s documented it well enough that you could do it for a new project quickly if you couldn&amp;#8217;t remember all the steps.&lt;/li&gt;
&lt;li&gt;Bundling resources requires more work on the part of both the framework author and users. Code-only frameworks shouldn&amp;#8217;t have to deal with this, luckily.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Blog post by Jonah Williams of Carbon Five&lt;/h3&gt;

&lt;p&gt;&lt;a href="http://blog.carbonfive.com/2011/04/04/using-open-source-static-libraries-in-xcode-4/"&gt;This post&lt;/a&gt; offers a well-written guide and explanation of the problem. However, it&amp;#8217;s not as comprehensive as the others. It also uses the framework in a way that isn&amp;#8217;t entirely representative of the way your users will. This increasing the risk that you might be missing something big when cutting a distribution. However, it&amp;#8217;s a good guide and shares a lot of similarities with jverkoey&amp;#8217;s approach.&lt;/p&gt;

&lt;h3&gt;iOS-Universal-Framework by Karl Stenerud&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/kstenerud/iOS-Universal-Framework"&gt;iOS-Universal-Framework&lt;/a&gt; offers a way of bending Xcode to your will so it will produce a static framework by introducing a new project template, possibly making life better if you need to do this a lot. The solution by jverkoey specifically improves on the method in a some marginal, yet important, ways such as resource handling and compile time. As with &lt;a href="http://www.chrisboyd.net/2011/06/creating-ios-framework-projects/"&gt;Chris Boyd&lt;/a&gt;, this method has made some people very happy.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://github.com/michaeltyson/iOS-Universal-Library-Template"&gt;iOS-Universal-Library-Template&lt;/a&gt; for a similar project not focused on frameworks.&lt;/p&gt;

&lt;h3&gt;Blog post by db-in&lt;/h3&gt;

&lt;p&gt;As noted by jverkoey, the approach in &lt;a href="http://db-in.com/blog/2011/07/universal-framework-iphone-ios-2-0/"&gt;this post&lt;/a&gt; is essentially the same as iOS-Universal-Framework but not as good.&lt;/p&gt;

&lt;h2&gt;Useful Links&lt;/h2&gt;

&lt;p&gt;An assortment of useful links related to this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/5584317/compile-build-or-archive-problems-with-xcode-4-and-dependancies"&gt;Compile, Build or Archive problems with Xcode 4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://stackoverflow.com/questions/989738/how-to-get-headers-into-framework-in-xcode"&gt;Getting headers into frameworks in xcode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/ios/#featuredarticles/XcodeConcepts/Concept-Workspace.html"&gt;Xcode workspaces&lt;/a&gt; and &lt;a href="http://developer.apple.com/library/mac/#recipes/xcode_help-structure_navigator/articles/adding_a_project_to_a_workspace.html"&gt;adding projects to workspaces&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/OverviewOfDynamicLibraries.html#//apple_ref/doc/uid/TP40001873-SW"&gt;Xcode docs on dynamic libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPFrameworks/Tasks/CreatingFrameworks.html#//apple_ref/doc/uid/20002258-BAJDHDAF"&gt;Creating Frameworks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPFrameworks/Concepts/WhatAreFrameworks.html#//apple_ref/doc/uid/20002303-BBCEIJFI"&gt;Framework Programming Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.apple.com/library/mac/#documentation/CoreFOundation/Conceptual/CFBundles/AboutBundles/AboutBundles.html"&gt;Bundle Programming Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.icodeblog.com/2011/04/07/creating-static-libraries-for-ios/"&gt;Creating static libraries for iOS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://spin.atomicobject.com/2011/12/13/building-a-universal-framework-for-ios/"&gt;Building a Universal Framework for iOS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;Older, probably totally out of date (but possibly informative):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.clintharris.net/2009/iphone-app-shared-libraries/"&gt;Sharing code across iPhone apps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.cocoanetics.com/2010/05/making-your-own-iphone-frameworks-in-xcode/"&gt;Making your own iPhone framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://accu.org/index.php/articles/1594"&gt;Creating a framework for the iPhone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.stormyprods.com/2008/11/using-static-libraries-with-iphone-sdk.html"&gt;Building static libraries for the iPhone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://the.ornyx.net/post/1222915969/universal-framework"&gt;Universal Framework&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://mobileorchard.com/sharing-compiled-code-building-static-libraries-in-xcode-for-the-iphone/"&gt;Building Static Libraries in Xcode for the iPhone&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://mobileorchard.com/code-sharing-via-static-libraries-and-cross-project-references/"&gt;Code sharing via static libraries and cross-project references&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=mG0Eu82FKSQ:2JEPVktTTZY:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=mG0Eu82FKSQ:2JEPVktTTZY:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=mG0Eu82FKSQ:2JEPVktTTZY:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=mG0Eu82FKSQ:2JEPVktTTZY:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/mG0Eu82FKSQ" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/ios-universal-frameworks-xcode/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[abnqe: A better Netflix queue experience]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/9J83OsvgooE/" />
    <updated>2012-04-02T11:29:37-04:00</updated>
    <id>http://mrdanadams.com/2012/abnqe</id>
    <content type="html">&lt;p&gt;&lt;img class="featured" src="http://mrdanadams.com/images/queue-300x164.jpg"&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; abnqe is &lt;a href="https://github.com/mrdanadams/abnqe"&gt;now on github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I&amp;#8217;m a long-time Netflix customer. As someone who uses the website to watch movies and shows connected to a &lt;a href="http://en.wikipedia.org/wiki/Home_theater_PC"&gt;HTPC&lt;/a&gt; it&amp;#8217;s become very clear that the website user experience is not a priority. Specifically, the instant watch queue management and ability to finding something to watch is just horrible. &lt;a href="http://abnqe.com/"&gt;abnqe&lt;/a&gt; (&lt;a href="http://abnqe.com/"&gt;A Better Netflix Queue Experience&lt;/a&gt;) is a small project to fix this.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;The missing experience&lt;/h2&gt;

&lt;p&gt;The queue on Netflix, as used by instant queue customers, is an interesting example of an interface providing the basic functionality required in a way that is nearly useless to users. Well, that may be unfair. If you want to manage the order of physical DVDs that will be shipped to you in a specific order, it&amp;#8217;s okay. However, for instant watching and finding something to watch from what is essentially my library of titles, it&amp;#8217;s awful. The order of your shows simply doesn&amp;#8217;t matter with instant as it did with shipping physical disks.&lt;/p&gt;

&lt;p&gt;Consider this: I&amp;#8217;m plop down on the couch after a long day, craft beer in hand and to watch something. Not sure what. But something. Fire up the instant queue. What do I get? I text list of all my items in no meaningful order without the ability to filter, sort or otherwise grok the 200+ items present. Ugh.&lt;/p&gt;

&lt;p&gt;abnqe, as first released, is the bare minimum set of features I wanted: a nice UI for visually seeing my queue with the ability to instantly search titles. I&amp;#8217;m already so much happier.&lt;/p&gt;

&lt;h2&gt;Future direction&lt;/h2&gt;

&lt;p&gt;I&amp;#8217;ve got a long list of things I&amp;#8217;d like to do to this thing. We&amp;#8217;ll see what gets done. Here are some of the things I have in mind:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filtering by type (TV / movies)&lt;/li&gt;
&lt;li&gt;Basic queue management (removing, adding)&lt;/li&gt;
&lt;li&gt;Get more detailed movie info&lt;/li&gt;
&lt;li&gt;Adding movies to lists / tags (dude movies, wife movies, kids shows, documentaries, foreign flicks).&lt;/li&gt;
&lt;li&gt;Filter by genre, rating, recently watched&lt;/li&gt;
&lt;li&gt;Discovering new titles (without suggesting titles I&amp;#8217;ve already seen; something that annoys me endlessly)&lt;/li&gt;
&lt;li&gt;Rotten tomatoes ratings&lt;/li&gt;
&lt;li&gt;See what&amp;#8217;s trending with other users, added recently and expiring&lt;/li&gt;
&lt;li&gt;Social sharing and recommendations from friends&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Under the hood&lt;/h2&gt;

&lt;p&gt;abnqe is a Rails 3 app hosted on Heroku using OAuth and the Netflix REST API to grab account and movie info. The first version was basically a saturday project.&lt;/p&gt;

&lt;h2&gt;Who is this for?&lt;/h2&gt;

&lt;p&gt;I made this for me (and my wife and kid). It&amp;#8217;s free to use. Use it if you want. Or don&amp;#8217;t. But if you do, drop me a comment. How are you using it? Have any problems? Any suggestions for improving the experience?&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=9J83OsvgooE:FhObx_zutJc:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=9J83OsvgooE:FhObx_zutJc:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=9J83OsvgooE:FhObx_zutJc:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=9J83OsvgooE:FhObx_zutJc:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/9J83OsvgooE" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/abnqe/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Heroku Postgres: Shared, Dedicated or use Amazon RDS?]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/X0Vjc6zlsNA/" />
    <updated>2012-03-28T12:20:44-04:00</updated>
    <id>http://mrdanadams.com/2012/heroku-postgres-shared-dedicated-amazon-rds</id>
    <content type="html">&lt;p&gt;&lt;img class="featured right" src="http://mrdanadams.com/images/heroku_rds_postgres.png"&gt;&lt;/p&gt;

&lt;p&gt;Heroku shared databases can&amp;#8217;t be beat for convenience or cost during development (assuming you are on Heroku already). What about when the app goes live? Here are some thoughts on whether the app should stay on a &lt;a href="https://postgres.heroku.com/"&gt;Heroku shared Postgres database&lt;/a&gt;, switch to a dedicated database or use something else entirely such as &lt;a href="http://aws.amazon.com/rds/"&gt;Amazon RDS&lt;/a&gt;.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;Basic Considerations&lt;/h2&gt;

&lt;p&gt;We have a startup Rails app hosted on Heroku getting ready to push for an early alpha release. For such an app, what are the questions you should be asking?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How will you handle traffic spikes?&lt;/li&gt;
&lt;li&gt;What determines growth in the size of the database?&lt;/li&gt;
&lt;li&gt;Is the data read only/read mostly or does it contain quick growth data like page views and social activity?&lt;/li&gt;
&lt;li&gt;How closely do costs need to be controlled?&lt;/li&gt;
&lt;li&gt;How much traffic and data do you need to support?&lt;/li&gt;
&lt;li&gt;How much maintenance will the solution cost and what staff is needed to support it?&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;For this app, the following were important to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since Heroku runs on EC2 they recommend (not surprisingly) that you grab a database plan where your entire DB can be in RAM. Dedicated plans range from 1.7 GB to 68 GB. Both our databases, being loaded with just testing data for alpha testing, were under 1 MB. That&amp;#8217;s not very helpful for evaluating the size we needed.&lt;/li&gt;
&lt;li&gt;We don&amp;#8217;t store anything growth- or data-heavy in our DB: we don&amp;#8217;t log analytics and all binary data is stored on S3. The data is going to grow with the number user accounts and related records. This is all very small, slow-growth data and even with a large number of new objects shouldn&amp;#8217;t blow out the database size. All the search indexes are stored in &lt;a href="http://websolr.com"&gt;WebSolr&lt;/a&gt; which will help keep the DB size down.&lt;/li&gt;
&lt;li&gt;While it&amp;#8217;s safer to go to a dedicated plan now, it&amp;#8217;s starts at $200 in the door and we don&amp;#8217;t appear to be anywhere near needing the storage or performance provided.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Heroku Database Migration Options&lt;/h2&gt;

&lt;p&gt;There are a few options for migrating databases if and when you want to go from shared to dedicated. Note that you should have a &lt;a href="http://onehub.com/blog/posts/rails-maintenance-pages-done-right/"&gt;maintenance page&lt;/a&gt; while migrating your database, especially if using a manual migration method. During migration your database won&amp;#8217;t accept any modifications (or if it does they will be lost).&lt;/p&gt;

&lt;h3&gt;Backup &amp;amp; Restore&lt;/h3&gt;

&lt;p&gt;This is the only option on Heroku for migrating from a shared to dedicated database. See &lt;a href="https://devcenter.heroku.com/articles/pgbackups"&gt;PG Backups&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works across all plans&lt;/li&gt;
&lt;li&gt;Can be slower than other migration methods (especially with large databases) since you need to export and re-import the entire database and transfer it across the network doing so. However, it&amp;#8217;s a pretty simple process and appears to be easy and fairly low risk.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;Fast Changeover&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://devcenter.heroku.com/articles/fast-database-changeovers"&gt;fast database changeover&lt;/a&gt;, you use followers (read-only slaves) or forking (snapshots) and then point the app at the new database. This isn&amp;#8217;t an option for migrating from a shared to dedicated database or moving to or from the Heroku platform in general. However, it&amp;#8217;s a good tool to know about.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works for large databases better than a manual migration&lt;/li&gt;
&lt;li&gt;Minimizes app downtime&lt;/li&gt;
&lt;li&gt;Migrating shouldn&amp;#8217;t require changes to the &lt;a href="http://websolr.com/"&gt;WebSolr&lt;/a&gt; index as the IDs should be the same&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Use Something Else (like Amazon RDS)&lt;/h2&gt;

&lt;p&gt;Rather than staying on &lt;a href="https://postgres.heroku.com/"&gt;Heroku Postgres&lt;/a&gt;, you could switch to something external such as Amazon RDS (which is clearly MySQL and not Postgres if that&amp;#8217;s an issue for you).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A switch from Postgres to MySQL might be an issue if you have lots of migrations and some use something database-specific&lt;/li&gt;
&lt;li&gt;Heroku offers Postgres as a service (outside hosting apps)&lt;/li&gt;
&lt;li&gt;Includes a nice admin panel and lots of scaling / migration features and support for scaling horizontally (if it should ever come to that)&lt;/li&gt;
&lt;li&gt;We could host a Postgres install on EC2 but we now have to roll all that scalability stuff ourselves (which we could do). We&amp;#8217;d probably consider using RDS rather than any self-run db cluster.&lt;/li&gt;
&lt;li&gt;For us it&amp;#8217;s mostly a matter of price and if the service Heroku provides is worth not doing it ourselves. Given that we don&amp;#8217;t have a dedicated IT team for this project the time spent maintaining the cluster is an important factor.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;At the point of putting out an alpha we didn&amp;#8217;t even need the 15 MB database which is $15 / month. Again, the DB should grow in proportion to the number of actually accounts, etc none of which should grow quickly (unless we are actually getting a ton of usage). If we started tracking analytics of any kind this would change.&lt;/p&gt;

&lt;h2&gt;Getting Heroku Database Size&lt;/h2&gt;

&lt;p&gt;You can use &lt;code&gt;heroku pg:info --remote=production&lt;/code&gt; to get the database size. For shared databases the output is not very interesting:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;heroku pg:info --remote&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;production&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="o"&gt;===&lt;/span&gt; SHARED_DATABASE &lt;span class="o"&gt;(&lt;/span&gt;DATABASE_URL&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;Data Size 648k
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;heroku pg:info --remote&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;staging&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="o"&gt;===&lt;/span&gt; SHARED_DATABASE &lt;span class="o"&gt;(&lt;/span&gt;DATABASE_URL&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;Data Size 688k
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;h2&gt;Where We Left It: EC2 for Cost Reduction&lt;/h2&gt;

&lt;p&gt;If this app really takes off (like huge) we will likely move to EC2 / RDS to reduce cost. Again, this depends on the numbers (particularly how many staff we need to support an AWS deployment). &lt;a href="http://www.quora.com/How-easy-is-it-to-get-off-Heroku-once-you-grow-out-of-it"&gt;This discussion&lt;/a&gt; has a good example of an experience comparing Heroku and RDS in the long-term:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;The only way to achieve any concurrency in Heroku is to turn up dynos and workers. These are 5c/hour each ~ $36/month. The first dyno (but not the worker) is free. Therefore, if you want a concurrency of 4 dynos and 1 worker, you are looking at $144/month. We have found that we can easily achieve 4 times this concurrency on one small EC2 instance. You would want load balancing and failover, so assuming that you are going to use 2 reserved small EC2 on the east coast (Heroku is on the east coast too), that comes to 2 X ($227.50 per year + 3c/hour) ~ $81/month. This can be equivalent of approximately $576/month on Heroku.&lt;/p&gt;

&lt;p&gt;Continuing with the $144/month figure for Heroku, shared database in Heroku is free, but the smallest dedicated instance is $200! Compare this with RDS, where an equivalent reserved multi-availability zone instance costs $455 yearly + 9.2c/hour ~ $104/month.&lt;/p&gt;

&lt;p&gt;In other words, at a concurrency of 4 dynos and 1 worker with a free shared database, Heroku costs $144 a month, while Amazon with a potential of atleast 4 times that concurrency with failover for the database and the server costs a total of $81 + $104 = $185/month. At the full capacity of the EC2 machines with a dedicated database instance, Heroku will cost approximately $776/month while Amazon will continue to run at under $200.&lt;/p&gt;

&lt;p&gt;&amp;#8230;&lt;/p&gt;

&lt;p&gt;The operational cost of maintaining the databases on Amazon is near zero, except for the initial setup, which can be super easy too if you have used it in the past. Web server operations can be a little more ongoing work, compared to zero for Heroku. However, once you have load balancing set up, and like in our case, capistrano + git based push system, you can be off and running at almost nil system administration time.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;What considerations and decisions have you made regarding where your database lives?&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=X0Vjc6zlsNA:IGXBOqK559I:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=X0Vjc6zlsNA:IGXBOqK559I:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=X0Vjc6zlsNA:IGXBOqK559I:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=X0Vjc6zlsNA:IGXBOqK559I:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/X0Vjc6zlsNA" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/heroku-postgres-shared-dedicated-amazon-rds/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Career story, trajectory and context]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/3YkcCE7Dvpc/" />
    <updated>2012-03-21T14:13:08-04:00</updated>
    <id>http://mrdanadams.com/2012/career-story-trajectory-context</id>
    <content type="html">&lt;p&gt;An important aspect of good &lt;a href="http://mrdanadams.com/2012/your-career-is-a-business/"&gt;career management&lt;/a&gt; is understanding your career story. Where you came from, where are you now and where you are going. Knowing this is critical to maximizing your market value and communicating that value to others (such as in interviews).&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;Career Trajectory&lt;/h2&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/career_trajectory.png"&gt;&lt;/p&gt;

&lt;p&gt;Let&amp;#8217;s assume you are considering buying a stock. The current price is $42 / share. Should you buy? I hope at this point you&amp;#8217;re thinking &amp;#8220;I have no idea!&amp;#8221;. Exactly.&lt;/p&gt;

&lt;p&gt;If I&amp;#8217;m interviewing software engineers I need to know not just where you are now but where you&amp;#8217;ve been and where you&amp;#8217;re going. Consider the following profiles for potential candidates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Middle-aged set in his ways who hasn&amp;#8217;t changed in years&lt;/li&gt;
&lt;li&gt;Middle-aged who just changed markets and is slightly behind but clearly has a passion and ability to learn&lt;/li&gt;
&lt;li&gt;Middle-aged who is mature, rock solid and still has a passion for the craft&lt;/li&gt;
&lt;li&gt;Junior with very little experience but incredible work ethic and hunger to grow&lt;/li&gt;
&lt;li&gt;Mid-twenties &amp;#8220;rock star&amp;#8221; who&amp;#8217;s smart with loads of talent but doesn&amp;#8217;t have the hunger or humility to grow quickly&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;This list is by no means comprehensive. The point is to show that someone&amp;#8217;s career trajectory is critical in understanding their real value. Are you a good investment? Or a liability?&lt;/p&gt;

&lt;h2&gt;Context is Critical&lt;/h2&gt;

&lt;p&gt;A subtlety of history and trajectory is context. Understanding the situations someone has been in and decisions they&amp;#8217;ve made is incredibly informative. Great people can come from all sorts of backgrounds and crush whatever preconceptions you had about where rock stars come from and what really makes one.&lt;/p&gt;

&lt;p&gt;For interviewers, the &lt;a href="http://en.wikipedia.org/wiki/5_Whys"&gt;5 Why&amp;#8217;s&lt;/a&gt; is an excellent way of discovering someone&amp;#8217;s depth. What was a big project you worked on or led? What were your responsibilities? What problems did you encounter and how did you solve them? Which design decisions did you make that you later discovered were wrong? How did you fix performance problems? What do you wish you had done? Dig down and find how they handle tough situations, how they think and their motivations.&lt;/p&gt;

&lt;p&gt;Be introspective. Consider your own history. What do you thrive on? What do you hate doing? Where are your opportunities for improvement?&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=3YkcCE7Dvpc:PW7RDrBdj80:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=3YkcCE7Dvpc:PW7RDrBdj80:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=3YkcCE7Dvpc:PW7RDrBdj80:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=3YkcCE7Dvpc:PW7RDrBdj80:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/3YkcCE7Dvpc" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/career-story-trajectory-context/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[What every software engineer should know: your career is a business]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/Or1V_Y1MfXU/" />
    <updated>2012-03-21T13:59:47-04:00</updated>
    <id>http://mrdanadams.com/2012/your-career-is-a-business</id>
    <content type="html">&lt;p&gt;Your career is a business and you should manage it like one. There is a lot to being a great software engineer and therein lies the problem: a focus on the technical (which is important, clearly) but a neglect of business, communication, and management skills needed to be your own career advisor. Without these skills many great, passionate engineers find themselves at a disadvantage working for too little pay or enjoyment. Here are some tips to set you in the right direction managing your career effectively.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;h2&gt;Business 101: Supply &amp;amp; Demand&lt;/h2&gt;

&lt;p&gt;You have something to offer. There is some demand out here for it. In working, you exchange your time and effort for money and other goodies. The more demand there is the more you get and vice versa.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/supply_demand.png"&gt;&lt;/p&gt;

&lt;p&gt;The above is a basic &lt;a href="http://en.wikipedia.org/wiki/Supply_and_demand"&gt;supply and demand&lt;/a&gt; graph. Price tracks the demand line. If supply increases above demand prices drop. When the reverse happens price goes up. Like a good stock portfolio, your value (or price) over time should be increasing:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://mrdanadams.com/images/career_growth.png" title="Which does your career value look like?" &gt;&lt;/p&gt;

&lt;p&gt;So what&amp;#8217;s your value? What do you have to offer? Here are a few examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Technical expertise:&lt;/strong&gt; You just know it better and deeper. Someone has a question about &lt;a href="http://www.adequatelygood.com/2010/2/JavaScript-Scoping-and-Hoisting"&gt;JavaScript hoisting&lt;/a&gt;? You the man.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Domain knowledge&lt;/strong&gt;: You really get your business and it&amp;#8217;s nuances.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependability:&lt;/strong&gt; People know when they give you something it well get done. Really done. If you run into a weird bug you don&amp;#8217;t give up; you pound on it until a solution is found.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Culture:&lt;/strong&gt; You make the environment around you better. People like coming to work a little  more when they work with you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leadership:&lt;/strong&gt; You can get people going in the same direction and get things done that require team effort.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Creativity and insight:&lt;/strong&gt; You understand what is really going on and can find novel solutions to the problems at hand.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Communication:&lt;/strong&gt; You work well with designers, project managers, clients, etc and can resolve conflict without freaking out.&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;I&amp;#8217;m sure you could come up with others. The critical point here is that a marketable skill set often extends way beyond the purely technical. Many recent graduates feel scammed by their Computer Science programs in being ill prepared for the &amp;#8220;real world&amp;#8221;.&lt;/p&gt;

&lt;h2&gt;Know Your Market(s)&lt;/h2&gt;

&lt;p&gt;First things first: what is your market?&lt;/p&gt;

&lt;p&gt;Your first market is simply where you work. The world that is your coworkers and clients / customers is a market all its own with a specific slant of supply and demand. What are the unmet needs? How can you increase your demand by improving existing skills or getting new ones? Can you get ahead of the curve by acquiring skills soon to be in demand? In some cases you can even create (artificial) demand by introducing a new technology or service. &lt;a href="http://mrdanadams.com/2012/its-your-company-act-like-an-owner/"&gt;Act like an owner&lt;/a&gt; and you&amp;#8217;ll discover a number of opportunities to increase your value.&lt;/p&gt;

&lt;p&gt;The wider market is harder to define. It could be in a specific technology (Rails or Java), a specific industry (healthcare, finance, retail), a certain kind of company culture (startups) or an odd niche (such a project manager specializing in bailing out projects in trouble: I actually know one of these). Within this market how do you stack up? Who are the leaders? What skills are most in demand?&lt;/p&gt;

&lt;p&gt;Identifying and really understanding your markets can take a lot of thought and investigation but is well worth the effort. Once you&amp;#8217;ve done this you can begin to strategically change your position. This means improving your current set of skills or changing your position altogether by picking up a new set.&lt;/p&gt;

&lt;h2&gt;Don&amp;#8217;t Be a T-Rex: Awesome and Extinct&lt;/h2&gt;

&lt;p&gt;Being a &amp;#8220;dinosaur&amp;#8221; has little to do with age except the longer you work the more opportunity you have to let this happen. Being &amp;#8220;dinosaured&amp;#8221; is the result of being left with skills that have no demand in the market. For instance, focusing your skills on a niche may be a good idea as it could make you more specialized. If that market grows you could be a leader. However, if that market evaporates you may find yourself with something to sell and no buyers. Welcome to dinosaur-hood.&lt;/p&gt;

&lt;h2&gt;Talking Turkey&lt;/h2&gt;

&lt;p&gt;Talking money is rarely fun for engineers. It just feels dirty. Or selfish. I have one thing to say to this: get over it.&lt;/p&gt;

&lt;p&gt;You may love what you do, the people you work with and your company (and that&amp;#8217;s awesome). You may even work for a company that really takes care of you. But don&amp;#8217;t forget for a second this is a business relationship; I assure you the business hasn&amp;#8217;t.&lt;/p&gt;

&lt;p&gt;This isn&amp;#8217;t to say you shouldn&amp;#8217;t love where you are (because I do and that&amp;#8217;s always been important to me) or that there are company owners who are good people and try to do right by their people (because I know a few). The business has a bottom line and so should you. They write you a check (and provide benefits, vacation, etc) and get something in return. What that &amp;#8220;something&amp;#8221; is and how much it&amp;#8217;s worth to them is the important bit.&lt;/p&gt;

&lt;p&gt;By advocating &amp;#8220;talking turkey&amp;#8221; I don&amp;#8217;t mean negotiation skills (although that&amp;#8217;s important). I&amp;#8217;m emphasizing being more conscious of your bottom line. Your company isn&amp;#8217;t doing you a favor by paying you and you owe them nothing. You have a responsibility to yourself (and others that depend on you) to maximize your value.&lt;/p&gt;

&lt;p&gt;Do you think about your career as a business? How do you manage your market value?&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=Or1V_Y1MfXU:_mKwaFSsoTU:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=Or1V_Y1MfXU:_mKwaFSsoTU:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=Or1V_Y1MfXU:_mKwaFSsoTU:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=Or1V_Y1MfXU:_mKwaFSsoTU:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/Or1V_Y1MfXU" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/your-career-is-a-business/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[It's your company too, act like an owner]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/rlPaYBoYvEU/" />
    <updated>2012-03-06T14:58:51-05:00</updated>
    <id>http://mrdanadams.com/2012/its-your-company-act-like-an-owner</id>
    <content type="html">&lt;p&gt;Company ownership is a funny thing. Who really owns the company? Well, it&amp;#8217;s probably the guy with risk in one pocket and a checkbook in the other. So your company isn&amp;#8217;t exactly a co-op. You don&amp;#8217;t have shares or equity. You aren&amp;#8217;t a partner. Let&amp;#8217;s assume you are at the &amp;#8216;bottom&amp;#8217; of the company. I propose there is tremendous value in an ownership mentality: this is my company and I&amp;#8217;m going to work to make it the kind of company I think it should be.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;p&gt;I am, without question, a small company person. Large companies, bureaucracy, and overly defined roles give me hives. I like open air, accountability, no place to hide. It&amp;#8217;s like a small town: everyone knows what everyone else is up to.&lt;/p&gt;

&lt;p&gt;In small companies (think consultancies, start-ups, small agencies) there simply isn&amp;#8217;t room for depending on someone else to pick up the slack. When everyone collectively takes responsibility for what&amp;#8217;s going on quality is higher, things get done, problems fixed.&lt;/p&gt;

&lt;p&gt;The company gets a lot out of the individuals acting like owners. Is the reverse true? Yes. Even in a company of great people, there will always be unmet needs. What can you do to add value? How can you make yourself indispensable? Perhaps something can be done better. Perhaps something isn&amp;#8217;t being done at all and should be. Step into the gap and become a leader. Find roles you can play in your team and in your company.&lt;/p&gt;

&lt;p&gt;Act like an owner and you will find yourself taking ownership: first of roles, perhaps tools, then teams, departments, and even companies.&lt;/p&gt;

&lt;p&gt;If, like me, you want to stay at the tech level and don&amp;#8217;t want to pursue company ownership, the rule is still the same: what can I do to add value that nobody else is doing?&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=rlPaYBoYvEU:DBJZlNg7UmM:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=rlPaYBoYvEU:DBJZlNg7UmM:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=rlPaYBoYvEU:DBJZlNg7UmM:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=rlPaYBoYvEU:DBJZlNg7UmM:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/rlPaYBoYvEU" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/its-your-company-act-like-an-owner/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Changing Dropbox location for 1Password storage]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/ihKqc0NPqLI/" />
    <updated>2012-02-13T06:41:47-05:00</updated>
    <id>http://mrdanadams.com/2012/change-dropbox-location-1password</id>
    <content type="html">&lt;p&gt;&lt;img class="featured" src="http://mrdanadams.com/images/dropbox_1password.png"&gt;&lt;/p&gt;

&lt;p&gt;Dropbox is awesome. 1Password is awesome. When their powers combine&amp;#8230; it&amp;#8217;s awesome. Except if you need to change where 1Password stores it&amp;#8217;s file in Dropbox. Here&amp;#8217;s a simple way to change that.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;p&gt;From the mouth of 1Password support:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;You have 1Password 3.9 which is from the Mac App Store. Apple requires (or will soon be requiring) all applications on the Mac App Store to conform to various Sandboxing rules. One of those rules is strict limitations on where the data files the application accesses are located.&lt;/p&gt;

&lt;p&gt;In this case we allow 1Password to read the file 1Password.agilekeychain inside of ~/Dropbox/1Password/&lt;/p&gt;

&lt;p&gt;So it will always prompt you to tell you that it is copying the file to that location and that the other one will remain in the prior location as a backup.&lt;/p&gt;

&lt;p&gt;Long story short, you&amp;#8217;ll have to keep your data file at ~/Dropbox/1Password/1Password.agilekeychain&lt;/p&gt;

&lt;p&gt;I do apologize for the inconvenience though. Hopefully this helps.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;Bummer. Here&amp;#8217;s how to change it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open 1Password and go to &lt;strong&gt;1Password &gt; Preferences&amp;#8230; &gt; General&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Disable using Dropbox. This will now store the files under /Users/yourname/Dropbox/Personal/&amp;#8230;&lt;/li&gt;
&lt;li&gt;Close 1Password&lt;/li&gt;
&lt;li&gt;Open a terminal and follow the script below&lt;/li&gt;
&lt;/ol&gt;


&lt;p&gt;Assuming you want to store your files in ~/Dropbox/Personal/1Password instead of ~/Dropbox/1Password (for instance, if you are using &lt;a href="http://mrdanadams.com/multiple-dropbox-accounts-same-machine-sharing/"&gt;multiple Dropbox accounts on the same machine&lt;/a&gt;) do the following in a Terminal:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='sh'&gt;&lt;span class='line'&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;mv ~/Library/Containers/com.agilebits.onepassword-osx-helper/Data/Documents ~/Dropbox/Personal/1Password
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ln -s ~/Dropbox/Personal/1Password ~/Library/Containers/com.agilebits.onepassword-osx-helper/Data/Documents
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;


&lt;p&gt;Enjoy.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=ihKqc0NPqLI:tzQuOrepqjg:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=ihKqc0NPqLI:tzQuOrepqjg:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=ihKqc0NPqLI:tzQuOrepqjg:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=ihKqc0NPqLI:tzQuOrepqjg:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/ihKqc0NPqLI" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/change-dropbox-location-1password/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Starting Emerging Business Tech meetup with first event on native / HTML5-based mobile apps]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/HEIlmdkoBzM/" />
    <updated>2012-02-03T06:23:15-05:00</updated>
    <id>http://mrdanadams.com/2012/emerging-business-tech-meetup-native-html5-mobile</id>
    <content type="html">&lt;p&gt;&lt;img class="featured" src="http://mrdanadams.com/images/logo.png"&gt;&lt;/p&gt;

&lt;p&gt;I&amp;#8217;m extremely excited to announce a new meetup, &lt;a href="http://www.meetup.com/emerging-business-technology/"&gt;Emerging Business Technology&lt;/a&gt;, lead by myself and sponsored by Cantina Consulting. We&amp;#8217;ve tried very hard to make this meetup one that provides a lot of value to it&amp;#8217;s members, extremely high quality events and content, and satisfies a need not currently met by other meetups in the Boston area. We&amp;#8217;ve already seen rapid growth and intense interest from a wide range of people working in technology.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;p&gt;From the meetup description:&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;This meetup provides engineers, practitioners and managers the context needed to evaluate and adopt rapidly evolving business technologies. Leave with an understanding of what the technology is, why it’s used, when to use it, and next steps to take. We’ll review use cases, processes, tools, and practices in a mini-conference format through short presentations, hands-on tutorials, Q&amp;A; and code walkthroughs.&lt;/p&gt;

&lt;p&gt;Topics may include Mobile app development, HTML5, responsive design, high-concurrency applications, interaction design, modern languages and frameworks, NoSQL databases, and mobile / tablet application design.&lt;/p&gt;&lt;/blockquote&gt;

&lt;p&gt;The first event, &lt;strong&gt;&lt;a href="http://www.meetup.com/emerging-business-technology/events/50411132/"&gt;Native and HTML5-based mobile: strategies for mobile app development&lt;/a&gt;&lt;/strong&gt;, has been scheduled and will be an awesome event. We&amp;#8217;ve got 3 or 4 sessions by some awesome speakers planned, both from inside Cantina and from outside vendors. It&amp;#8217;s a free event so check it out if you have any interest in strategies for developing mobile apps.&lt;/p&gt;
&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=HEIlmdkoBzM:nOLTvpafft8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=HEIlmdkoBzM:nOLTvpafft8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=HEIlmdkoBzM:nOLTvpafft8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=HEIlmdkoBzM:nOLTvpafft8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/HEIlmdkoBzM" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/emerging-business-tech-meetup-native-html5-mobile/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Beware using active_admin and Sunspot Rails gems together]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/RaBowxkJb7I/" />
    <updated>2012-02-02T11:00:00-05:00</updated>
    <id>http://mrdanadams.com/2012/beware-using-active_admin-and-sunspot-rails-gems-together</id>
    <content type="html">&lt;p&gt;If you are using &lt;a href="http://activeadmin.info/"&gt;Active Admin&lt;/a&gt; and the &lt;a href="http://sunspot.github.com/"&gt;Sunspot&lt;/a&gt; gem for Rails, beware: they conflict on the search method leading to some very confusing results.&lt;/p&gt;

&lt;!-- more --&gt;


&lt;p&gt;Active Admin has a dependency on &lt;a href="https://github.com/ernie/meta_search"&gt;meta_search&lt;/a&gt; which provides a &lt;code&gt;.search()&lt;/code&gt; method on Active Record classes. Sunspot attempts to provide the same method, aliased from &lt;code&gt;solr_search&lt;/code&gt;, but only if the method does not already exist.&lt;/p&gt;

&lt;p&gt;In short, searching can be done using &lt;code&gt;solr_search()&lt;/code&gt; rather than &lt;code&gt;search()&lt;/code&gt;:&lt;/p&gt;

&lt;figure class='code'&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;div class="highlight"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre class="line-numbers"&gt;&lt;span class='line-number'&gt;1&lt;/span&gt;
&lt;span class='line-number'&gt;2&lt;/span&gt;
&lt;span class='line-number'&gt;3&lt;/span&gt;
&lt;span class='line-number'&gt;4&lt;/span&gt;
&lt;span class='line-number'&gt;5&lt;/span&gt;
&lt;span class='line-number'&gt;6&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;td class='code'&gt;&lt;pre&gt;&lt;code class='ruby'&gt;&lt;span class='line'&gt;&lt;span class="vi"&gt;@search&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;solr_search&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;keywords&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:q&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;  &lt;span class="n"&gt;paginate&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:page&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;per_page&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;page_size&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;span class='line'&gt;
&lt;/span&gt;&lt;span class='line'&gt;&lt;span class="vi"&gt;@results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@search&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;&lt;/figure&gt;



&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=RaBowxkJb7I:RGr74nhQilA:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=RaBowxkJb7I:RGr74nhQilA:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=RaBowxkJb7I:RGr74nhQilA:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=RaBowxkJb7I:RGr74nhQilA:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/RaBowxkJb7I" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/beware-using-active_admin-and-sunspot-rails-gems-together/</feedburner:origLink></entry>
  
  <entry>
    <title type="html"><![CDATA[Using Sunspot, Websolr, and Solr on Heroku]]></title>
    <link href="http://feedproxy.google.com/~r/MisterDanAdams/~3/JlvqxZFj0uE/" />
    <updated>2012-02-01T11:00:00-05:00</updated>
    <id>http://mrdanadams.com/2012/sunspot-websolr-solr-heroku</id>
    <content type="html">&lt;p&gt;Having recently deployed a Rails app using Sunspot and Solr on Heroku, here are some tips for getting started, testing, searching, and deploying.&lt;/p&gt;

&lt;!--more--&gt;


&lt;h2&gt;Getting the right gem&lt;/h2&gt;

&lt;p&gt;As noted in &lt;a href="http://blog.derekperez.com/post/552826277/the-proper-websolr-gem-for-heroku" target="_blank"&gt;this post&lt;/a&gt;, getting the right gem for Websolr is important (yet confusing): the Heroku documentation is not consistent on which gem to use. In short, use the &lt;a href="https://github.com/onemorecloud/websolr-sunspot_rails" target="_blank"&gt;websolr-sunspot_rails&lt;/a&gt; gem which tracks the version of &lt;a href="http://sunspot.github.com/" target="_blank"&gt;Sunspot&lt;/a&gt; needed by &lt;a href="http://www.websolr.com/" target="_blank"&gt;Websolr&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following gems are used:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/2230763.js?file=Gemfile'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;gem 'sunspot_rails'
gem 'kaminari'
gem 'sunspot_with_kaminari'&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;UPDATE (per Nick Zadrozny):&lt;/p&gt;

&lt;blockquote&gt;&lt;p&gt;the docs at https://devcenter.heroku.com/articles/websolr are correct: You want to use Sunspot directly, not websolr-sunspot_rails. The only thing the devcenter document gets wrong right now is the version. The current version of Sunspot is 1.3.0. I&amp;#8217;ll add that to my todo list ;)&lt;/p&gt;

&lt;p&gt;The websolr-sunspot_rails gem was only needed for earlier versions of Sunspot in order to support the WEBSOLR_URL variable, which is supported natively in Sunspot as of its 1.2.0.&lt;/p&gt;

&lt;p&gt;So all you need is &lt;code&gt;gem 'sunspot_rails'&lt;/code&gt; in your Gemfile and you&amp;#8217;re all set!&lt;/p&gt;&lt;/blockquote&gt;

&lt;h2&gt;Configuration&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;config/sunspot.yml&lt;/em&gt; provides a base config file for development and production only. &lt;code&gt;ENV['SOLR_URL']&lt;/code&gt; and &lt;code&gt;ENV['WEBSOLR_URL']&lt;/code&gt; override any values in this file.&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/2230763.js?file=solr.yml'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;# Note: ENV['SOLR_URL'] and ENV['WEBSOLR_URL'] override any values here
# The defaults are same as what's below  so you only need to override as desired.
# Removed production since that should never be used in heroku.
# See Sunspot::Rails::Configuration.solr_url

development:
  solr:
    hostname: localhost
    port: 8982
    log_level: INFO

test:
  solr:
    hostname: localhost
    port: 8981
    log_level: WARNING
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;h2&gt;Starting Solr (locally)&lt;/h2&gt;

&lt;p&gt;Solr can be started (and stopped) locally with:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/2230763.js?file=run.sh'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;bundle exec rake sunspot:solr:start
bundle exec rake sunspot:solr:stop
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;h2&gt;Testing&lt;/h2&gt;

&lt;p&gt;If using rspec the following can get you started:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/2230763.js?file=rspec_solr.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;RSpec.configure do |c|
  c.before :suite do
    module Sunspot
      def self.stub_session
        @sub_session ||= Sunspot::Rails::StubSessionProxy.new self.session
      end
    end

  end

  c.before :each do
    Sunspot.session = Sunspot.stub_session
    Sunspot.session = Sunspot.session.original_session if example.metadata[:solr]

    Sunspot.remove_all!
  end
end
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;Your search spec may look like this:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/2230763.js?file=search_spec.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;describe 'Search' do
  def commit
    Sunspot.commit
  end

  def search(q)
    visit &amp;quot;/search?q=#{q}&amp;quot;
  end

  it 'does something interesting', :solr do
    Something.create!
    commit
    search 'foobar'
end
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;The following allows Cucumber features not to explode:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/2230763.js?file=cucumber_config.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;# Solr faking.
# see http://opensoul.org/blog/archives/2010/04/07/cucumber-and-sunspot/
# Note: we need to do more than this if we start doing search tests in cucumber
$original_sunspot_session = Sunspot.session

Before(&amp;quot;~@search&amp;quot;) do
  Sunspot.session = Sunspot::Rails::StubSessionProxy.new($original_sunspot_session)
end
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;h2&gt;Searching and paginating&lt;/h2&gt;

&lt;p&gt;Perform the search in the controller:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/2230763.js?file=search_controller.rb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;class SearchController &amp;lt; ApplicationController
    def search
        page_size = 10

        @search = Profile.solr_search do
            keywords params[:q] do
                boost_fields name: 10.0, user: 4.0
            end

            with :include_search, true
            paginate page: params[:page], per_page: page_size
        end

        @results = @search.results

        @total = @search.total
        @paging = @total &amp;gt; page_size
        @start = (@search.current_page - 1) * page_size + 1
        @end = [@start + page_size - 1, @total].min

        render (if @results.empty? then :no_results else :results end)
    end

end
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;


&lt;p&gt;In the template show results and paging:&lt;/p&gt;

&lt;div&gt;&lt;script src='https://gist.github.com/2230763.js?file=results.html.erb'&gt;&lt;/script&gt;
&lt;noscript&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;h3 class=&amp;quot;summary&amp;quot;&amp;gt;&amp;lt;%= t('.your-search-header'), start: @start, end: @end, total: pluralize(@total, 'result') %&amp;gt;&amp;lt;/h3&amp;gt;

&amp;lt;% @results.each do |p| %&amp;gt;
&amp;lt;div class=&amp;quot;result&amp;quot;&amp;gt;&amp;lt;!-- ... --&amp;gt;&amp;lt;/div&amp;gt;
&amp;lt;% end %&amp;gt;

&amp;lt;%= paginate @search, window: 1 %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/noscript&gt;&lt;/div&gt;



&lt;div class="feedflare"&gt;
&lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=JlvqxZFj0uE:0azx_bn25f8:yIl2AUoC8zA"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=yIl2AUoC8zA" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=JlvqxZFj0uE:0azx_bn25f8:qj6IDK7rITs"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?d=qj6IDK7rITs" border="0"&gt;&lt;/img&gt;&lt;/a&gt; &lt;a href="http://feeds.feedburner.com/~ff/MisterDanAdams?a=JlvqxZFj0uE:0azx_bn25f8:gIN9vFwOqvQ"&gt;&lt;img src="http://feeds.feedburner.com/~ff/MisterDanAdams?i=JlvqxZFj0uE:0azx_bn25f8:gIN9vFwOqvQ" border="0"&gt;&lt;/img&gt;&lt;/a&gt;
&lt;/div&gt;&lt;img src="http://feeds.feedburner.com/~r/MisterDanAdams/~4/JlvqxZFj0uE" height="1" width="1"/&gt;</content>
  <feedburner:origLink>http://mrdanadams.com/2012/sunspot-websolr-solr-heroku/</feedburner:origLink></entry>
  
</feed>
