<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title></title>
	<atom:link href="http://main.earlystageit.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://main.earlystageit.com</link>
	<description></description>
	<lastBuildDate>Sun, 04 Dec 2011 15:00:13 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='main.earlystageit.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://1.gravatar.com/blavatar/966ccbbc0eab06abc88a7c11f347cf46?s=96&#038;d=http%3A%2F%2Fs2.wp.com%2Fi%2Fbuttonw-com.png</url>
		<title></title>
		<link>http://main.earlystageit.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://main.earlystageit.com/osd.xml" title="" />
	<atom:link rel='hub' href='http://main.earlystageit.com/?pushpress=hub'/>
		<item>
		<title>Hands on Hadoop with Amazon EC2</title>
		<link>http://main.earlystageit.com/2011/09/06/hands-on-hadoop-with-amazon-ec2/</link>
		<comments>http://main.earlystageit.com/2011/09/06/hands-on-hadoop-with-amazon-ec2/#comments</comments>
		<pubDate>Tue, 06 Sep 2011 10:26:08 +0000</pubDate>
		<dc:creator>J</dc:creator>
				<category><![CDATA[Requirements]]></category>
		<category><![CDATA[Amazon AWS]]></category>
		<category><![CDATA[Cloud Computing]]></category>
		<category><![CDATA[Hadoop]]></category>
		<category><![CDATA[Map Reduce]]></category>

		<guid isPermaLink="false">http://main.earlystageit.com/?p=682</guid>
		<description><![CDATA[A few months ago, I gave a talk at the Chelmsford Technology Skill Share Group. It was focused on the whys and wherefores of NoSQL and Map/Reduce. If you are interested in a copy of the presentation, please contact me. &#8230; <a href="http://main.earlystageit.com/2011/09/06/hands-on-hadoop-with-amazon-ec2/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=682&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>A few months ago, I gave a talk at the Chelmsford Technology Skill Share Group. It was focused on the whys and wherefores of NoSQL and Map/Reduce. If you are interested in a copy of the presentation, please contact me.</p>
<p>Next week (<strong>9/14/11, 4:00 pm, Chelmsford Public Library</strong>, McCarthy Meeting Room. <a href="http://www.chelmsfordlibrary.org/library_info/directions">Directions.</a>), I&#8217;ll be giving a hands-on introduction to running Hadoop on Amazon EC2.</p>
<p>It will be as hands-on as the previous talk was conceptual. For the actual material, we will use the excellent <a href="http://www.michael-noll.com/tutorials/running-hadoop-on-ubuntu-linux-single-node-cluster/" target="_blank">tutorial by Michael Noll</a>. Michael first wrote it in 2007 and has kept it up to date. The tutorial helps you install map/reduce and use it for computing the count of every word in a large text. We will use Ulysses by James Joyce as our sample text. Can we do this in 90 minutes? Yes, we can. But the goal is to get the most out of the journey.</p>
<p>To get the most out of the talk, you should be prepared to sign up for an Amazon account. They require a credit card but the credit card won&#8217;t actually get charged because our usage will be a few cents and well under the free limit. If you don&#8217;t want to sign up for an Amazon account, that&#8217;s OK too — we&#8217;ll use one as the example and will project it on the screen.</p>
<p>Look forward to seeing you there.</p>
<br />Filed under: <a href='http://main.earlystageit.com/category/requirements/'>Requirements</a> Tagged: <a href='http://main.earlystageit.com/tag/amazon-aws/'>Amazon AWS</a>, <a href='http://main.earlystageit.com/tag/cloud-computing/'>Cloud Computing</a>, <a href='http://main.earlystageit.com/tag/hadoop/'>Hadoop</a>, <a href='http://main.earlystageit.com/tag/map-reduce/'>Map Reduce</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/earlystageit.wordpress.com/682/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/earlystageit.wordpress.com/682/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/earlystageit.wordpress.com/682/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/earlystageit.wordpress.com/682/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/earlystageit.wordpress.com/682/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/earlystageit.wordpress.com/682/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/earlystageit.wordpress.com/682/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/earlystageit.wordpress.com/682/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/earlystageit.wordpress.com/682/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/earlystageit.wordpress.com/682/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/earlystageit.wordpress.com/682/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/earlystageit.wordpress.com/682/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/earlystageit.wordpress.com/682/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/earlystageit.wordpress.com/682/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=682&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://main.earlystageit.com/2011/09/06/hands-on-hadoop-with-amazon-ec2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">J.Singh</media:title>
		</media:content>
	</item>
		<item>
		<title>The 7 signs of failure for internet startups</title>
		<link>http://main.earlystageit.com/2011/06/02/the-7-signs-of-failure-for-internet-startups/</link>
		<comments>http://main.earlystageit.com/2011/06/02/the-7-signs-of-failure-for-internet-startups/#comments</comments>
		<pubDate>Thu, 02 Jun 2011 18:42:54 +0000</pubDate>
		<dc:creator>J</dc:creator>
				<category><![CDATA[Raison d'etre]]></category>
		<category><![CDATA[Service Offering]]></category>

		<guid isPermaLink="false">http://main.earlystageit.com/?p=663</guid>
		<description><![CDATA[Blackbox has just launched their first Startup Genome Report. It&#8217;s been getting quite a buzz in the community, including VentureBeat and Steve Blank&#8217;s website. The report speaks for itself. If Max and Co. at Blackbox achieve half of what they &#8230; <a href="http://main.earlystageit.com/2011/06/02/the-7-signs-of-failure-for-internet-startups/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=663&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Blackbox has just launched their first <a title="Startup Genome Report" href="http://startupgenome.cc/" target="_blank">Startup Genome Report</a>. It&#8217;s been getting quite a buzz in the community, including <a title="VentureBeat" href="http://venturebeat.com/2011/05/30/the-7-signs-of-failure-for-internet-startups/" target="_blank">VentureBeat</a> and <a title="Tune In, Turn On, Drop Out – The Startup Genome Project" href="http://steveblank.com/2011/05/29/tune-in-turn-on-drop-out-the-startup-genome-project/" target="_blank">Steve Blank&#8217;s website</a>.</p>
<p>The report speaks for itself. If Max and Co. at Blackbox achieve half of what they have set out to do, it&#8217;ll change the way investing and entrepreneurship gets done.</p>
<p>I mention the report and the brouhaha accompanying it on this blog for two reasons:</p>
<ol>
<li>It tracks our experience well.</li>
<li>It fits our business model: Early Stage IT is a partner to start-up teams and can bring your team that much needed technology focus, or add to it.</li>
</ol>
<br />Filed under: <a href='http://main.earlystageit.com/category/raison-detre/'>Raison d'etre</a>, <a href='http://main.earlystageit.com/category/service-offering/'>Service Offering</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/earlystageit.wordpress.com/663/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/earlystageit.wordpress.com/663/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/earlystageit.wordpress.com/663/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/earlystageit.wordpress.com/663/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/earlystageit.wordpress.com/663/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/earlystageit.wordpress.com/663/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/earlystageit.wordpress.com/663/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/earlystageit.wordpress.com/663/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/earlystageit.wordpress.com/663/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/earlystageit.wordpress.com/663/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/earlystageit.wordpress.com/663/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/earlystageit.wordpress.com/663/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/earlystageit.wordpress.com/663/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/earlystageit.wordpress.com/663/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=663&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://main.earlystageit.com/2011/06/02/the-7-signs-of-failure-for-internet-startups/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">J.Singh</media:title>
		</media:content>
	</item>
		<item>
		<title>Amazon&#8217;s EC2 Disaster</title>
		<link>http://main.earlystageit.com/2011/04/23/amazons-ec2-disaster/</link>
		<comments>http://main.earlystageit.com/2011/04/23/amazons-ec2-disaster/#comments</comments>
		<pubDate>Sat, 23 Apr 2011 16:37:40 +0000</pubDate>
		<dc:creator>J</dc:creator>
				<category><![CDATA[Requirements]]></category>

		<guid isPermaLink="false">http://main.earlystageit.com/?p=648</guid>
		<description><![CDATA[Technology news and the Blogosphere are all a-twitter with Amazon&#8217;s recent cloud stumble. Given the stakes, expect the next few months to be a slugfest. The unbelievers will talk about how this cloud stuff is all hot air. The believers &#8230; <a href="http://main.earlystageit.com/2011/04/23/amazons-ec2-disaster/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=648&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Technology news and the Blogosphere are all a-twitter with Amazon&#8217;s <a href="http://news.cnet.com/8301-1009_3-10413951-83.html" target="_blank">recent cloud stumble</a>.</p>
<p>Given the stakes, expect the next few months to be a slugfest. The unbelievers will talk about how this cloud stuff is all hot air. The believers will speak of it as &#8220;a learning moment&#8221; and make suggestions so this type of thing &#8220;never happens again&#8221;.</p>
<p>Both sides are right in their own way but I think the stumble as well as the reaction to it is quite normal. Malcolm Gladwell wrote <a href="http://www.gladwell.com/1996/1996_01_22_a_blowup.htm" target="_blank">this essay</a> about the Challenger disaster a quarter-century ago. To quote his introduction,</p>
<blockquote><p>Who can be blamed for a disaster like the Challenger explosion, a decade ago? No one, according to the new risk theorists, and we&#8217;d better get used to it</p></blockquote>
<p>Can this stumble be characterized similarly? I think so. And we&#8217;d better get used to it.</p>
<p>The force driving the push to the cloud is economic. This event does not really change the economics of the cloud. It brings the risks into sharper relief but that can&#8217;t be bad for the industry, can it? It will help us tone down the rhetoric.</p>
<p>We need to fix some problems in this business. How cloud vendors can get away with not publishing an SLA is hard to understand. Hopefully this event will increase the pressure for that.</p>
<p>Perhaps a new vendor will emerge that will offer an SLA to differentiate themselves. Priced a bit higher than the Amazons and Rackspaces of the world, it will help us determine the extra premium that an SLA commands.</p>
<p>There is another way to solve this dilemma: through an <em>insurance</em> model. Insurance companies measure risk and put a value on it. Their model is to evaluate your home/business/car and place a premium on its risk. And make specific suggestions for mitigating that risk. And reducing your premium if you implement those suggestions.</p>
<p>But we need to be concerned about over-fixing the problems. Inevitably, there will be suggestions for avoiding this type of an issue in the future: more automation to compensate for the failures of automation. That&#8217;s the type of thing Gladwell cautions against. Beyond a certain level, mindless automation doesn&#8217;t help much. And sometimes it gives us books priced at <a href="http://www.michaeleisen.org/blog/?p=358" target="_blank">over a million bucks</a>!</p>
<br />Filed under: <a href='http://main.earlystageit.com/category/requirements/'>Requirements</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/earlystageit.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/earlystageit.wordpress.com/648/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/earlystageit.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/earlystageit.wordpress.com/648/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/earlystageit.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/earlystageit.wordpress.com/648/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/earlystageit.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/earlystageit.wordpress.com/648/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/earlystageit.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/earlystageit.wordpress.com/648/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/earlystageit.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/earlystageit.wordpress.com/648/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/earlystageit.wordpress.com/648/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/earlystageit.wordpress.com/648/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=648&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://main.earlystageit.com/2011/04/23/amazons-ec2-disaster/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">J.Singh</media:title>
		</media:content>
	</item>
		<item>
		<title>How to Get Professional Assistance When You Can&#8217;t Afford It</title>
		<link>http://main.earlystageit.com/2011/04/17/how-to-get-professional-assistance-when-you-cant-afford-it/</link>
		<comments>http://main.earlystageit.com/2011/04/17/how-to-get-professional-assistance-when-you-cant-afford-it/#comments</comments>
		<pubDate>Mon, 18 Apr 2011 00:07:17 +0000</pubDate>
		<dc:creator>J</dc:creator>
				<category><![CDATA[Customer Relationship]]></category>
		<category><![CDATA[Raison d'etre]]></category>
		<category><![CDATA[Technology Strategy]]></category>

		<guid isPermaLink="false">http://earlystageit.wordpress.com/?p=615</guid>
		<description><![CDATA[Some thoughts while preparing for  this panel discussion on Tuesday. First, a little bit about Early Stage IT. We do IT for Early Stage companies. No, not installing machines type of stuff. More like IT strategy and execution, especially web &#8230; <a href="http://main.earlystageit.com/2011/04/17/how-to-get-professional-assistance-when-you-cant-afford-it/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=615&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Some thoughts while preparing for  <a title="How to Get Professional Assistance When You Can't Afford It" href="http://www.meetup.com/EntreTechForum/events/17074961/">this panel discussion</a> on Tuesday.</p>
<p>First, a little bit about Early Stage IT. We do IT for Early Stage companies. No, not installing machines type of stuff. More like IT strategy and execution, especially web applications. Our positioning is exactly in this space: startups. What attracts me to this space is that it&#8217;s very dynamic, it attracts a lot of very smart people who are not only smart, but can get things done and make concepts come to life. We started a couple of years ago, and it took a while to get things going — a long dry spell in a lousy economy — but things have started to turn around, and I&#8217;m happy about that.</p>
<p>Let me talk first about a failure. It&#8217;s kind of the right of passage in the startup world, right? We built this web site for helping High School seniors search and apply for scholarships. We built the web site on Google App Engine. I coded most of it, and we had a young woman helping out.</p>
<p>She had dropped out of the workplace about 6 years ago when her second child was born, and now that he was entering kindergarten, she was ready to go back to work. Of course, technology moves on and she needed to polish up her skills. And she still needed to be able to help out in his classroom and be there to meet the school bus, that sort of thing. So we made a deal: I would help her develop her skills and she would do what she could coding-wise.</p>
<p>Sounds wonderful, right? We&#8217;d made some mistakes in how we structured ourselves — three partners plus a marketing guy working on equity and we just couldn&#8217;t make a go of it. And there wasn&#8217;t enough equity there to try and sell it so we&#8217;re in the process of dissolving it.</p>
<p>But within that failure was a big success too. We had a functioning web site that had been built for less than a thousand dollars, partly on Google App Engine, partly on Amazon. We took payments using PayPal, we had a marketing web site built on Drupal Gardens, all of it free.</p>
<p>So lessons learned, and the lessons are these:</p>
<ol>
<li>Get good legal help and structure the company in a way that makes it possible to adapt.</li>
<li>Make sure we&#8217;re not just a a bunch of chiefs, we have a few Indians too.</li>
<li>Eric Ries talks about validated learning. Validate that you have a market, that if you build it, they actually will come.</li>
<li>Finally, if I&#8217;m going to participate in a cash-free deal, I have to look at it like a VC would. And if you assume that VC&#8217;s generally know what they&#8217;re doing, you have to conclude that for us to take part in something, we have to have a pretty firm notion that the likelihood of being backed by someone with money is high.</li>
</ol>
<p>To the topic of today&#8217;s conversation, there are many reasons to get an advisor. If you want them because they look good on your company profile — Trophy Advisors, if you will — that&#8217;s a different story.</p>
<p>If you want them because they will help make the company, let me turn the question around: if you could get professional assistance from someone without paying a lot of money, would you take it? My bet is, you wouldn&#8217;t. You&#8217;d wonder why it was so inexpensive. You&#8217;d wonder if you were being sold a bill of goods. You&#8217;d wonder if the person was going to just steal your idea. The bottom line is, you want someone you can trust. You want someone who is as good in their field as you are in yours. In other words, this relationship has to be based on trust and mutual respect. That&#8217;s the key ingredient and if <em>that&#8217;s</em> missing, you don&#8217;t want that person.</p>
<p>There are a lot of areas where you can save money. In today&#8217;s technology environment, with Cloud Computing and open source software and NoSQL databases, the cost to actually develop something is not the barrier. The barrier is how you identify who the buyers will be, how you will sell to them, what exactly do they want, what are your barriers to entry, what is your value proposition, etc. Those are the critical elements that will determine your success.</p>
<p>So is there no other way to get key talent? Sure there is, but it&#8217;s hard. It&#8217;s harder than raising money from an investor. You have two tools: First, the vision. Not just that you can make money. Some people are motivated solely by money, others want to see a purpose in what they&#8217;re doing. If you can share that purpose, that mission, then that&#8217;s your reason to collaborate in making it happen. If you don&#8217;t share that vision, the other tool is the promise of future money. To assemble the right founding team, you need five things:</p>
<ol>
<li>Trust &#8211; you must absolutely trust this person</li>
<li>Ability &#8211; they must have the key skills you need at formation time</li>
<li>Available &#8211; the best people are usually not available&#8230;they are busy doing other things</li>
<li>Desire &#8211; they must share your vision and desire to see it happen</li>
<li>Commitment &#8211; they must be willing to work for free until you can get funding</li>
</ol>
<p>Having 2 or 3 out of 5 is not good enough. You need all five. A client that I&#8217;ve gotten to know pretty well convinced me this way: he has done startups before and done successful exits. He has an impressive Rolodex and can get f&amp;f money. The concept is not that far away from being tested. Either way, he can help us get other business because of his connections. That line of argument was convincing enough.</p>
<br />Filed under: <a href='http://main.earlystageit.com/category/customer-relationship/'>Customer Relationship</a>, <a href='http://main.earlystageit.com/category/raison-detre/'>Raison d'etre</a>, <a href='http://main.earlystageit.com/category/technology-strategy/'>Technology Strategy</a>  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/earlystageit.wordpress.com/615/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/earlystageit.wordpress.com/615/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/earlystageit.wordpress.com/615/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/earlystageit.wordpress.com/615/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/earlystageit.wordpress.com/615/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/earlystageit.wordpress.com/615/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/earlystageit.wordpress.com/615/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/earlystageit.wordpress.com/615/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/earlystageit.wordpress.com/615/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/earlystageit.wordpress.com/615/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/earlystageit.wordpress.com/615/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/earlystageit.wordpress.com/615/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/earlystageit.wordpress.com/615/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/earlystageit.wordpress.com/615/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=615&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://main.earlystageit.com/2011/04/17/how-to-get-professional-assistance-when-you-cant-afford-it/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">J.Singh</media:title>
		</media:content>
	</item>
		<item>
		<title>Metrics for SaaS Pricing Models</title>
		<link>http://main.earlystageit.com/2010/12/05/how-to-price-a-saas-service/</link>
		<comments>http://main.earlystageit.com/2010/12/05/how-to-price-a-saas-service/#comments</comments>
		<pubDate>Mon, 06 Dec 2010 04:41:56 +0000</pubDate>
		<dc:creator>J</dc:creator>
				<category><![CDATA[SaaS Pricing]]></category>
		<category><![CDATA[SaaS]]></category>
		<category><![CDATA[Software as a Service]]></category>
		<category><![CDATA[Usage Metrics]]></category>

		<guid isPermaLink="false">http://blog.earlystageit.com/?p=536</guid>
		<description><![CDATA[I ran across a post from ERP Software Advice on 9 Key Points to Negotiate in a SaaS Agreement by Derek Singleton, who writes an excellent blog on software selection. From a customer&#8217;s point of view, he seems to have done a &#8230; <a href="http://main.earlystageit.com/2010/12/05/how-to-price-a-saas-service/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=536&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>I ran across a post from <a href="http://www.softwareadvice.com/">ERP Software Advice</a> on <a href="http://www.softwareadvice.com/articles/enterprise/9-key-points-to-negotiate-in-a-saas-agreement-1112310/">9 Key Points to Negotiate in a SaaS Agreement</a> by Derek Singleton, who writes an excellent blog on software selection. From a customer&#8217;s point of view, he seems to have done a pretty complete job.</p>
<p>But what if you are just coming up with a SaaS service? What metrics should you base your pricing on? I&#8217;d like to look at it from a provider&#8217;s point of view.</p>
<p>In this post I&#8217;ll just focus on pricing metrics. What about some of the other factors he mentions, like SLA? Later.</p>
<h1>Activity-Pricing Congruence</h1>
<p>Let&#8217;s do a thought experiment. You have been asked to set up and operate a call center for a credit card company. Each of your representatives will need a computer connected to the company database with a fast network. Each rep will also need a phone. A set of 800- numbers and a call routing system will bring the customer phone calls to them. Before a rep gets a call, a voice response system will interact with each caller and get their credit card info and verify their PIN, etc. You find two suppliers that have different pricing models:</p>
<ol>
<li>Supplier A&#8217;s pricing is component-based. $x per computer, $y per CPU-hour of the company mainframe, $z per megabyte/sec of bandwidth, $v for the voice response unit, etc.</li>
<li>Supplier B&#8217;s pricing is activity-based. $s per service call. The costs for computers, network, phone, everything, is rolled into the one number.</li>
</ol>
<p>Which would you pick? Based on actual numbers, supplier A may be cheaper but supplier B&#8217;s charges will be a lot more predictable. Many SaaS companies set their pricing in units that make sense to them, even though it makes them less transparent to the customers. Why should the customer bear the uncertainty? <em>The key is to have a pricing scheme that makes sense to the customer.</em></p>
<h1>The Price of Simplistic Pricing</h1>
<p>The pricing scheme should be simple and congruent with usage, that was the message above.</p>
<p>But there is such a thing as too simple. For example, Salesforce.com charges by the number of licensed users and the per-user price is considered steep for anyone except the dedicated user. This has an interesting effect: most companies limit the number of employees who have licenses to the few who need to interact with it all day long. For everyone else, they hire a contractor to write a data extract program. And a reporting package to sit around this extracted data. And batch processes to keep stuff synchronized.</p>
<p>The net result of this simplistic pricing model is that Salesforce.com can&#8217;t realize revenue for this add-on ecosystem and the customers end up with a kludge.</p>
<p>If Salesforce.com priced themselves by usage rather than by number of users, they might get a lot more users (although with less per-user revenue). <em>And the customers wouldn&#8217;t end up operating a custom-done kludge designed to avoid the expense of Salesforce.com at the core.</em></p>
<br />Filed under: <a href='http://main.earlystageit.com/category/saas-pricing/'>SaaS Pricing</a> Tagged: <a href='http://main.earlystageit.com/tag/saas/'>SaaS</a>, <a href='http://main.earlystageit.com/tag/software-as-a-service/'>Software as a Service</a>, <a href='http://main.earlystageit.com/tag/usage-metrics/'>Usage Metrics</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/earlystageit.wordpress.com/536/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/earlystageit.wordpress.com/536/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/earlystageit.wordpress.com/536/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/earlystageit.wordpress.com/536/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/earlystageit.wordpress.com/536/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/earlystageit.wordpress.com/536/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/earlystageit.wordpress.com/536/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/earlystageit.wordpress.com/536/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/earlystageit.wordpress.com/536/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/earlystageit.wordpress.com/536/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/earlystageit.wordpress.com/536/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/earlystageit.wordpress.com/536/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/earlystageit.wordpress.com/536/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/earlystageit.wordpress.com/536/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=536&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://main.earlystageit.com/2010/12/05/how-to-price-a-saas-service/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">J.Singh</media:title>
		</media:content>
	</item>
		<item>
		<title>App Engine Checklist for using SSL</title>
		<link>http://main.earlystageit.com/2010/11/17/gae-reverse-proxy/</link>
		<comments>http://main.earlystageit.com/2010/11/17/gae-reverse-proxy/#comments</comments>
		<pubDate>Wed, 17 Nov 2010 20:07:01 +0000</pubDate>
		<dc:creator>J</dc:creator>
				<category><![CDATA[Service Offering]]></category>
		<category><![CDATA[Amazon AWS]]></category>
		<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SSL support]]></category>

		<guid isPermaLink="false">http://blog.earlystageit.com/?p=515</guid>
		<description><![CDATA[Using a Reverse Proxy for Google App Engine? Some code changes to consider: Code with this assumption in mind: all incoming (into GAE) requests will use https://abc.appspot.com; all outgoing links embedded in return HTML should use https://www.abc.com. Set secure: always in &#8230; <a href="http://main.earlystageit.com/2010/11/17/gae-reverse-proxy/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=515&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Using a <a href="http://blog.earlystageit.com/2010/07/14/gae-proxy/" target="_blank">Reverse Proxy for Google App Engine</a>? Some code changes to consider:</p>
<ol>
<li>Code with this assumption in mind: all incoming (into GAE) requests will use <span style="color:#0000ff;"><span style="text-decoration:underline;">https://abc.appspot.com</span></span>; all outgoing links embedded in return HTML should use <span style="color:#0000ff;"><span style="text-decoration:underline;">https://www.abc.com</span></span>.</li>
<li>Set <code>secure: always</code> in the application configuration.</li>
<li>For static files, use <span style="color:#0000ff;"><span style="text-decoration:underline;">https://abc.appspot.com</span></span> — there is no reason to serve them through the proxy.</li>
<li>The reverse proxy will not transform authentication tokens in any way; these may require special attention.</li>
</ol>
<br />Filed under: <a href='http://main.earlystageit.com/category/service-offering/'>Service Offering</a> Tagged: <a href='http://main.earlystageit.com/tag/amazon-aws/'>Amazon AWS</a>, <a href='http://main.earlystageit.com/tag/google-app-engine/'>Google App Engine</a>, <a href='http://main.earlystageit.com/tag/security/'>Security</a>, <a href='http://main.earlystageit.com/tag/ssl-support/'>SSL support</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/earlystageit.wordpress.com/515/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/earlystageit.wordpress.com/515/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/earlystageit.wordpress.com/515/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/earlystageit.wordpress.com/515/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/earlystageit.wordpress.com/515/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/earlystageit.wordpress.com/515/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/earlystageit.wordpress.com/515/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/earlystageit.wordpress.com/515/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/earlystageit.wordpress.com/515/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/earlystageit.wordpress.com/515/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/earlystageit.wordpress.com/515/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/earlystageit.wordpress.com/515/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/earlystageit.wordpress.com/515/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/earlystageit.wordpress.com/515/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=515&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://main.earlystageit.com/2010/11/17/gae-reverse-proxy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">J.Singh</media:title>
		</media:content>
	</item>
		<item>
		<title>Cloud-based deployments</title>
		<link>http://main.earlystageit.com/2010/11/14/cloud-deployments/</link>
		<comments>http://main.earlystageit.com/2010/11/14/cloud-deployments/#comments</comments>
		<pubDate>Mon, 15 Nov 2010 00:20:51 +0000</pubDate>
		<dc:creator>J</dc:creator>
				<category><![CDATA[Requirements]]></category>
		<category><![CDATA[Techniques]]></category>
		<category><![CDATA[Technology foundation]]></category>
		<category><![CDATA[Amazon AWS]]></category>
		<category><![CDATA[Building Web Applications]]></category>
		<category><![CDATA[Cloud Computing]]></category>

		<guid isPermaLink="false">http://blog.earlystageit.com/?p=461</guid>
		<description><![CDATA[Readers of this blog know that 80% of our work is on Google App Engine. Development on that platform is fast because Google takes care of everything under the App Engine framework. Sadly, as if to prove the Pareto Principle &#8230; <a href="http://main.earlystageit.com/2010/11/14/cloud-deployments/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=461&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Readers of this blog know that 80% of our work is on Google App Engine. Development on that platform is fast because Google takes care of everything under the App Engine framework. Sadly, as if to prove the <a href="http://en.wikipedia.org/wiki/Pareto_principle" target="_blank">Pareto Principle</a> yet again, 80% of my time has been going into the remaining 20% of the application.</p>
<p>The flexibility associated with <em>Some Assembly Required</em> is hardly Amazon&#8217;s fault but I&#8217;ve got tired of building and rebuilding these pieces. I am embarking on a project to reduce the install and deployment times of these components. The first component I am going to tackle is the <a href="http://blog.earlystageit.com/2010/07/14/gae-proxy/" target="_blank">reverse proxy</a>. I would like to reduce the deployment time from a couple of hours to under 10 minutes.</p>
<p>Edit 11/17/10: A Chef-based deployment implementation is now in place. We met our goal.</p>
<p><span id="more-461"></span>With this goal in mind, I have been looking at Administration platforms (complete list <a href="http://en.wikipedia.org/wiki/Comparison_of_open_source_configuration_management_software" target="_blank">here</a>). There are four serious contenders¹: <a href="http://www.cfengine.org/" target="_blank">CF Engine</a> or its variants such as <a href="http://rubyforge.org/projects/cfruby/" target="_blank">cfruby</a>, <a href="http://www.puppetlabs.com/" target="_blank">Puppet</a>, <a href="http://wiki.opscode.com/display/chef/Home" target="_blank">Chef</a> and <a href="http://www.rightscale.com/" target="_blank">RightScale</a>.</p>
<p>I can&#8217;t claim decades of experience with systems management; I had to rely on other people&#8217;s experience. Research on the web showed a lot of people citing reasons why they switched from CF Engine to Puppet but none going in the other direction. So even though I have had success with cfruby, I felt it was better to look at the other platforms.</p>
<p>Puppet is the established player in this space. It is the safe technology choice and is used for managing infrastructures in many large companies. A couple of articles cement this conclusion: <a href="http://bitfieldconsulting.com/puppet-vs-chef" target="_blank">one</a> and <a href="http://bhuga.net/2009/09/puppet-vs-chef" target="_blank">two</a>. The first article is a little weak but I found the ensuing discussion to be very informative. This comment in the second article seems to be a fair summary of the Puppet vs Chef comparison:</p>
<blockquote><p>Puppet has perfected automating the traditional sysadmin tasks, while Chef tackles elastic operations and application integration better.</p></blockquote>
<p>At the end of this analysis I was already leaning toward using Chef as the platform. <a href="http://blog.rightscale.com/2009/09/16/rackspace-rightlink-chef-machine-tags-vpc/" target="_blank">This announcement</a> from RightScale sealed the case.</p>
<p>Why not directly choose Rightscale as a solution? If they were open source, they would be worth serious consideration. After a bit of experimenting with Chef, if it doesn&#8217;t meet my goals, I&#8217;m more inclined to try Rightscale than Puppet. The problems Rightscale is trying to solve have more in common with the problems we at Early Stage IT need to solve; that&#8217;s less so with the problems Puppet is aimed at solving. But if a platform pivot is required at all, it&#8217;s a couple of weeks down the road.</p>
<p>My starting point: <a href="http://www.themomorohoax.com/2010/07/31/ruby-chef-tutorial" target="_blank">The Momoro Hoax</a>.</p>
<p>Meanwhile, did you notice that all four of these solutions are Ruby-based? Where did that come from? With Amazon&#8217;s and Rightscale&#8217;s adoption on Ruby and with Puppet as the premiere platform, Ruby has become the de facto standard in this space.</p>
<hr />¹ If I&#8217;ve missed any, hopefully one of my readers will point it out.</p>
<p>&nbsp;</p>
<br />Filed under: <a href='http://main.earlystageit.com/category/requirements/'>Requirements</a>, <a href='http://main.earlystageit.com/category/techniques/'>Techniques</a>, <a href='http://main.earlystageit.com/category/technology-foundation/'>Technology foundation</a> Tagged: <a href='http://main.earlystageit.com/tag/amazon-aws/'>Amazon AWS</a>, <a href='http://main.earlystageit.com/tag/building-web-applications/'>Building Web Applications</a>, <a href='http://main.earlystageit.com/tag/cloud-computing/'>Cloud Computing</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/earlystageit.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/earlystageit.wordpress.com/461/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/earlystageit.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/earlystageit.wordpress.com/461/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/earlystageit.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/earlystageit.wordpress.com/461/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/earlystageit.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/earlystageit.wordpress.com/461/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/earlystageit.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/earlystageit.wordpress.com/461/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/earlystageit.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/earlystageit.wordpress.com/461/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/earlystageit.wordpress.com/461/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/earlystageit.wordpress.com/461/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=461&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://main.earlystageit.com/2010/11/14/cloud-deployments/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">J.Singh</media:title>
		</media:content>
	</item>
		<item>
		<title>Google App Engine in Action</title>
		<link>http://main.earlystageit.com/2010/09/21/app-engine-in-action/</link>
		<comments>http://main.earlystageit.com/2010/09/21/app-engine-in-action/#comments</comments>
		<pubDate>Tue, 21 Sep 2010 11:21:47 +0000</pubDate>
		<dc:creator>J</dc:creator>
				<category><![CDATA[Techniques]]></category>
		<category><![CDATA[Google App Engine]]></category>

		<guid isPermaLink="false">http://blog.earlystageit.com/?p=400</guid>
		<description><![CDATA[In this post, we take the App Engine Getting Started Guide to the next level. We will be building a Personal Resume Tracker — an application for managing your resume-send-out process. It&#8217;s hardly a finished application but will serve the &#8230; <a href="http://main.earlystageit.com/2010/09/21/app-engine-in-action/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=400&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>In this post, we take the App Engine <a href="http://code.google.com/appengine/docs/python/gettingstarted/" target="_blank">Getting Started Guide</a> to the next level.</p>
<p>We will be building a Personal Resume Tracker — an application for managing your resume-send-out process. It&#8217;s hardly a finished application but will serve the purpose for which it was intended: as a pedagogical tool.</p>
<ul>
<li>Storing various versions of your resume,</li>
<li>Composing and including a cover letter,</li>
<li>Mailing a resume &amp; cover letter out and</li>
<li>Keeping track of who was mailed what version when.</li>
</ul>
<p>We will build this application in 5 steps, starting from <a href="http://code.google.com/appengine/docs/python/gettingstarted/staticfiles.html" target="_blank">where the Hello World example leaves off</a>. To give you a road map, here are the 5 steps:</p>
<ol>
<li>A data model for the application and replacing the <code>Greeting</code> class with the <code>Person</code> class.</li>
<li>A <code>ResumeDoc</code> class and uploading and downloading files.</li>
<li>A <code>Contact</code> class that represents an email contact made with a person. Implementation of the data model begun in step 1 is completed here. This step discusses how to navigate the data model.</li>
<li>Sending out a plain-text email with attachment. We utilize the GAE mail API, one of a number of services available in the GAE environment.</li>
<li>We utilize the Markdown API for converting plain-text to HTML and sending HTML-format email. We download an open source module and integrate it into our application.</li>
</ol>
<h3><span id="more-400"></span>Debugging</h3>
<p>Before delving into programming, a few words about debugging resources.</p>
<ul>
<li>The <a href="http://code.google.com/appengine/articles/logging.html">logging API</a> comes in handy for logging program events to the console. The logs are then available through the GAE Launcher, Eclipse or through the GAE Application Dashboard.</li>
<li>The state of the database may be viewed from GAE Launcher and <a href="http://localhost:8080/_ah/admin/" target="_blank">through a browser window</a> when running in the local sandbox. The state of the production database may be examined through the Application Dashboard.</li>
<li>If running under Eclipse, pydev provides a complete debugging support.</li>
</ul>
<p><strong>Step 1: Data Model and the Person class</strong></p>
<p><a href="http://earlystageit.files.wordpress.com/2010/09/resumedatamodel.gif"><img class="alignright size-full wp-image-416" title="Resume Data Model" src="http://earlystageit.files.wordpress.com/2010/09/resumedatamodel.gif?w=300" alt="Resume Tracker Data Model" width="411" height="119" /></a><br />
The data model is self-explanatory. Our application stores many versions of resumes and a list of people with whom the applicant wants to communicate. She can select a person and a resume, write a cover letter for the occasion and send. A system like this is a great way to accumulate statistics on which messages and which versions of resumes are effective and which less so, but we get ahead of ourselves here.</p>
<p>We replace the file index.html from the Getting Started example with these contents as <code>person.html</code>:</p>
<p><code><pre class="brush: xml;">
&lt;html&gt;
  &lt;head&gt;
    &lt;link type=&quot;text/css&quot; rel=&quot;stylesheet&quot; href=&quot;/stylesheets/main.css&quot; /&gt;
  &lt;/head&gt;
  &lt;script type=&quot;text/javascript&quot;&gt;
    function editPerson(k, n, e, p, btn){
        document.getElementById('prsn_id').value = k;
        document.getElementById('name').value = n;
        document.getElementById('email').value = e;
        document.getElementById('phone').value = p;
        document.getElementById('submitBtn').value = btn;
        document.getElementById('my_form').style.display = 'block';
    }
    function addPerson(){
        editPerson(&quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;Add&quot;);
    }
  &lt;/script&gt;
  &lt;body&gt;
  {% if user %}
    &lt;table&gt;
    {% for person in persons %}
        &lt;tr&gt;
            &lt;td&gt;&lt;b&gt;{{ person.Name }}&lt;/b&gt;&lt;/td&gt;
            &lt;td&gt;{{ person.Email }} &lt;/td&gt;
            &lt;td&gt;{{ person.Phone }} &lt;/td&gt;
            &lt;td&gt;&lt;button onclick='editPerson(&quot;{{ person.key }}&quot;, &quot;{{ person.Name }}&quot;, &quot;{{ person.Email }}&quot;, &quot;{{ person.Phone }}&quot;, &quot;Update&quot; ); return false;'&gt;Edit&lt;/button&gt; &lt;/td&gt;
        &lt;/tr&gt;
    {% endfor %}
        &lt;tr&gt;
            &lt;td&gt;&lt;/td&gt;
            &lt;td&gt;&lt;/td&gt;
            &lt;td&gt;&lt;/td&gt;
            &lt;td&gt;&lt;button onclick='addPerson(); return false;'&gt;New&lt;/button&gt; &lt;/td&gt;
        &lt;/tr&gt;
    &lt;/table&gt;
    &lt;div id=&quot;my_form&quot;  style=&quot;display: none;&quot;&gt;
    &lt;form action=&quot;/person&quot; method=&quot;post&quot;&gt;
      &lt;input name=&quot;prsn_id&quot; id=&quot;prsn_id&quot; type=&quot;hidden&quot; /&gt;
      &lt;label for=&quot;name&quot; &gt;Name&lt;/label&gt;  &lt;input id=&quot;name&quot;  name=&quot;name&quot;  type=&quot;text&quot; size=&quot;60&quot;&gt;&lt;/input&gt;&lt;br /&gt;
      &lt;label for=&quot;email&quot;&gt;Email&lt;/label&gt;&lt;input id=&quot;email&quot; name=&quot;email&quot; type=&quot;text&quot; size=&quot;60&quot;&gt;&lt;/input&gt;&lt;br /&gt;
      &lt;label for=&quot;phone&quot;&gt;Phone&lt;/label&gt;&lt;input id=&quot;phone&quot; name=&quot;phone&quot; type=&quot;text&quot; size=&quot;60&quot;&gt;&lt;/input&gt;&lt;br /&gt;
      &lt;div&gt;&lt;input id=&quot;submitBtn&quot; type=&quot;submit&quot;&gt;&lt;/div&gt;
    &lt;/form&gt;
    &lt;/div&gt;
  {% endif %}
    &lt;a href=&quot;{{ url }}&quot;&gt;{{ url_linktext }}&lt;/a&gt;

  &lt;/body&gt;
&lt;/html&gt;
</pre></code><br />
The <code>person.html</code> file shown above displays a grid of persons and an Edit button for each. A <em>New</em> button triggers creation of new entities. A common FORM element serves as the area for entering information. Notice that we utilize the datastore key to distinguish person objects.</p>
<p>The file <code>resume-tracker.py</code> is architecturally similar to helloworld.py:</p>
<p><code><pre class="brush: python;">
import os
from google.appengine.ext.webapp import template

from google.appengine.api import users
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.ext import db

class Person(db.Model):
    Name  = db.StringProperty(required = True)
    Email = db.StringProperty(required = True)
    Phone = db.StringProperty()
    updated = db.DateTimeProperty(auto_now = True)

class PersonPage(webapp.RequestHandler):
    def get(self):
        if users.get_current_user():
            url = users.create_logout_url(self.request.uri)
            url_linktext = 'Logout'
            persons_query = Person.all().order('-updated')
            persons = persons_query.fetch(100)
        else:
            persons = []
            url = users.create_login_url(self.request.uri)
            url_linktext = 'Login'

        template_values = {
            'persons': persons,
            'url': url,
            'url_linktext': url_linktext,
            'user': users.get_current_user(),
            }

        path = os.path.join(os.path.dirname(__file__), 'person.html')
        self.response.out.write(template.render(path, template_values))

    def post(self):
        if users.get_current_user():
            prsn_id = self.request.get('prsn_id')
            name = self.request.get('name')
            email = self.request.get('email')
            phone = self.request.get('phone')
            if prsn_id:
                entity = Person.get(str(prsn_id))
                entity.Name = name
                entity.Email = email
                entity.Phone = phone
            else:
                entity = Person(Name = name, Email = email, Phone = phone)
            entity.put()

        self.redirect('/person')

application = webapp.WSGIApplication(
                                     [('/',             PersonPage),
                                      ('/person',       PersonPage),
                                      ],
                                     debug=True)

def main():
    run_wsgi_app(application)

if __name__ == &quot;__main__&quot;:
    main()
</pre></p>
<p></code><br />
Try running this application on your local machine. A change to <code>app.yaml</code> will be needed.</p>
<h3>Step 2: The ResumeDoc class</h3>
<p>About a year ago, Nick Johnson of Google wrote a <a href="http://blog.notdot.net/2009/9/Handling-file-uploads-in-App-Engine">piece</a> on uploading and downloading documents. This section borrows heavily from Nick. You will need to add the following two classes to <code>resume-tracker.py</code>.</p>
<p><code><pre class="brush: python;">
class ResumeDoc(db.Model):
    Name = db.StringProperty(required = True)
    Body = db.BlobProperty()
    Mime = db.StringProperty()
    Fnam = db.StringProperty()
    updated = db.DateTimeProperty(auto_now = True)

class ResumePage(webapp.RequestHandler):
    def get(self):

        if users.get_current_user():
            url = users.create_logout_url(self.request.uri)
            url_linktext = 'Logout'
            resumes_query = ResumeDoc.all().order('-updated')
            resumes = resumes_query.fetch(10)
        else:
            resumes = []
            url = users.create_login_url(self.request.uri)
            url_linktext = 'Login'

        template_values = {
            'resumes': resumes,
            'url': url,
            'url_linktext': url_linktext,
            'user': users.get_current_user(),
            }

        path = os.path.join(os.path.dirname(__file__), 'resume.html')
        self.response.out.write(template.render(path, template_values))

class ResumeHandler(webapp.RequestHandler):
    def post(self):
        if users.get_current_user():
            file = self.request.POST['file']
            name = self.request.get('name')
            entity = ResumeDoc(Name = name, Body = file.value, Mime = file.type, Fnam = file.filename)
            entity.put()

        self.redirect('/')
    def get(self, key):
        if users.get_current_user():
            doc = ResumeDoc.get(key)
            body = doc.Body
            mime = doc.Mime
            if body:
                self.response.headers['Content-Type'] = mime
                self.response.headers['Content-Disposition'] = 'attachment; filename=%s' % doc.Fnam
                self.response.out.write(body)

</pre></p>
<p></code></p>
<p>The corresponding resume.html file follows the same architecture as person.html. Two differences:</p>
<ol>
<li>Use of  tag to specify file input</li>
<li>Use of <code>enctype="multipart/form-data"</code> in the form tag to specify text input in the same form.</li>
</ol>
<p>Create the file resume.html with the following content:</p>
<p><code><pre class="brush: xml;">
&lt;html&gt;
  &lt;head&gt;
    &lt;link type=&quot;text/css&quot; rel=&quot;stylesheet&quot; href=&quot;/stylesheets/main.css&quot; /&gt;
  &lt;script type=&quot;text/javascript&quot;&gt;
    function editResume(k, n, btn){
        document.getElementById('key').value = k;
        document.getElementById('name').value = n;
        document.getElementById('submitBtn').value = btn;
        document.getElementById('my_form').style.display = 'block';
    }
    function addResume(){
        editResume(&quot;&quot;, &quot;&quot;, &quot;Add&quot;);
    }
  &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
  {% if user %}
    {% for resume in resumes %}
        &lt;b&gt;&lt;a href=&quot;/resume/{{ resume.key }}&quot;&gt;{{ resume.Name }}&lt;/a&gt;&lt;/b&gt;: {{ resume.updated }}
        &lt;button onclick='editResume(&quot;{{ resume.key }}&quot;, &quot;{{ resume.Name }}&quot;, &quot;Update&quot; ); return false;'&gt;Change&lt;/button&gt;
        &lt;br /&gt;
    {% endfor %}
        &lt;button onclick='addResume(); return false;'&gt;New&lt;/button&gt; &lt;br /&gt;

    &lt;div id=&quot;my_form&quot;  style=&quot;display: none;&quot;&gt;
    &lt;form action=&quot;/resume&quot; method=&quot;post&quot; enctype=&quot;multipart/form-data&quot;&gt;
      &lt;input name=&quot;key&quot; id=&quot;key&quot; type=&quot;hidden&quot; /&gt;
      &lt;div&gt;&lt;label for=&quot;file&quot; &gt;File&lt;/label&gt;  &lt;input id=&quot;file&quot; name=&quot;file&quot; type=&quot;file&quot; size=&quot;60&quot;&gt;&lt;/input&gt;&lt;/div&gt;
      &lt;div&gt;&lt;label for=&quot;name&quot; &gt;Descriptive Name&lt;/label&gt;  &lt;input id=&quot;name&quot; name=&quot;name&quot; type=&quot;text&quot; size=&quot;60&quot;&gt;&lt;/input&gt;&lt;/div&gt;
      &lt;div&gt;&lt;input id=&quot;submitBtn&quot; type=&quot;submit&quot; value=&quot;Upload&quot;&gt;&lt;/div&gt;
    &lt;/form&gt;
    &lt;/div&gt;
  {% endif %}
    &lt;a href=&quot;{{ url }}&quot;&gt;{{ url_linktext }}&lt;/a&gt;

  &lt;/body&gt;
&lt;/html&gt;
</pre></p>
<p></code><br />
Connect the two classes <code>ResumePage</code> and <code>ResumeHandler</code> to the WSGI framework by replacing the object <code>application</code> with:</p>
<p><code><pre class="brush: python;">
application = webapp.WSGIApplication(
                                     [('/',             PersonPage),
                                      ('/person',       PersonPage),
                                      ('/resumes',      ResumePage),
                                      ('/resume/(.*)$', ResumeHandler),
                                      ('/resume',       ResumeHandler),
                                      ],
                                     debug=True)
</pre></p>
<p></code><br />
We have just added three new URLs. The URL <code>/resumes</code> just lists stored documents. The URL <code>/resume/documentKey</code> downloads a specific document to the user&#8217;s browser. Notice the use of a regular expression to specify a key. This formulation avoids the use of query strings to specify http arguments. The same URL is used with a post method to upload new documents.</p>
<h3>Step 3: The Contact class</h3>
<p>The data model for the <code>Contact</code> class is as follows.</p>
<p><code><pre class="brush: python;">
class Contact(db.Model):
    person  = db.ReferenceProperty(collection_name='documents')
    resume = db.ReferenceProperty(collection_name='recipients')
    cover = db.TextProperty()
    mailed = db.DateTimeProperty()
    updated = db.DateTimeProperty(auto_now = True)
    def abstract(self):
        inString = str(self.cover)
        return inString if (len(inString) &lt; 30) else (inString[:29] + '&amp;hellip;')
</pre></p>
<p></code></p>
<p>The <code>person</code> attribute of class <code>Contact</code> is a reference to a <code>Person</code> object. Given a <code>Contact</code> object <code>ctct</code>, the corresponding <code>Person</code> object can be obtained by using <code>ctct.person</code>. The dot notation is used to navigate the data model. The data store also implicitly maintains reverse pointers: given an object <code>prsn</code> of class <code>Person</code>, <code>prsn.documents</code> will yield a collection of <code>Contact</code> objects. For example, if we wanted a list of <code>ResumeDoc</code> objects that were sent to <code>george</code> and when, we would write</p>
<p><code><pre class="brush: python;">
ctcts = george.documents
for ctct in ctcts:
    print ctct.resume.Name, ctct.mailed
</pre></p>
<p></code><br />
Second, <code>cover</code> was made a TextProperty because StringProperty is limited to 500 characters and a cover letter could surely be larger. The fact that a StringProperty can be part of an index and TextProperty can not, is not much of an issue.</p>
<p>Finally, the method <code>abstract(self)</code> is a quick way of showing the first few words of the cover letter. This method will be used in <code>contact.html</code>, which is shown next.</p>
<p><code><pre class="brush: xml;">
&lt;html&gt;
  &lt;head&gt;
    &lt;link type=&quot;text/css&quot; rel=&quot;stylesheet&quot; href=&quot;/stylesheets/main.css&quot; /&gt;
  &lt;script type=&quot;text/javascript&quot;&gt;
    function editContact(k, pk, rk, cvr, btn){
        document.getElementById('key').value = k;
        document.getElementById('person').value = pk;
        document.getElementById('resume').value = rk;
        document.getElementById('cover').value = cvr;
        document.getElementById('submitBtn').value = btn;
        document.getElementById('my_form').style.display = 'block';
    }
    function addContact() {
        editContact(&quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;&quot;, &quot;Add&quot;);
    }
    function sendMail(k) {
        alert('Send Mail(' + k + ') will be implemented later');
    }
  &lt;/script&gt;
  &lt;/head&gt;
  &lt;body&gt;
  {% if user %}
    &lt;table&gt;
    {% for contact in contacts %}
        &lt;tr&gt;
            &lt;td&gt;{{ contact.person.Name }}&lt;/td&gt;
            &lt;td&gt;&lt;b&gt;{{ contact.resume.Name }} &lt;/b&gt;&lt;/td&gt;
            &lt;td&gt;{{ contact.abstract }} &lt;/td&gt;
            &lt;td valign=&quot;middle&quot;&gt;
                {% if contact.mailed %}
                {{ contact.mailed }}
                {% else %}
                &lt;button onclick='sendMail( &quot;{{ contact.key }}&quot; ); return false;' &gt;Send Mail&lt;/button&gt;
                {% endif %}
            &lt;/td&gt;
            &lt;td valign=&quot;middle&quot;&gt;&lt;button onclick='editContact(&quot;{{ contact.key }}&quot;, &quot;{{ contact.person.key }}&quot;, &quot;{{ contact.resume.key }}&quot;,
                &lt;r&gt;&lt;![CDATA[{{ contact.cover }}]]&gt;&lt;/r&gt;, &quot;Update&quot; ); return false;'&gt;Edit&lt;/button&gt; &lt;/td&gt;
        &lt;/tr&gt;
    {% endfor %}
        &lt;tr&gt;
            &lt;td&gt;&lt;/td&gt;
            &lt;td&gt;&lt;/td&gt;
            &lt;td&gt;&lt;/td&gt;
            &lt;td&gt;&lt;/td&gt;
            &lt;td&gt;&lt;button onclick='addContact(); return false;'&gt;New&lt;/button&gt; &lt;/td&gt;
        &lt;/tr&gt;
    &lt;/table&gt;

    &lt;div id=&quot;my_form&quot;  style=&quot;display: none;&quot;&gt;
    &lt;form action=&quot;/contact&quot; method=&quot;post&quot;&gt;
      &lt;input name=&quot;key&quot; id=&quot;key&quot; type=&quot;hidden&quot; /&gt;
      &lt;label for=&quot;person&quot; &gt;Person&lt;/label&gt;
        &lt;select id=&quot;person&quot;  name=&quot;person&quot;&gt;
            {% for person in persons %}
            &lt;option value=&quot;{{ person.key }}&quot;&gt;{{  person.Name }}&lt;/option&gt;
            {% endfor %}
        &lt;/select&gt;
      &lt;br /&gt;
      &lt;label for=&quot;resume&quot; &gt;Resume&lt;/label&gt;
        &lt;select id=&quot;resume&quot;  name=&quot;resume&quot;&gt;
            {% for resume in resumes %}
            &lt;option value=&quot;{{ resume.key }}&quot;&gt;{{ resume.Name }}&lt;/option&gt;
            {% endfor %}
        &lt;/select&gt;
      &lt;label for=&quot;cover&quot;&gt;Cover&lt;/label&gt;&lt;textarea id=&quot;cover&quot; name=&quot;cover&quot;  rows=&quot;15&quot; cols=&quot;80&quot;&gt;&lt;/textarea&gt;&lt;br /&gt;
      &lt;div&gt;&lt;input id=&quot;submitBtn&quot; type=&quot;submit&quot;&gt;&lt;/div&gt;
    &lt;/form&gt;
    &lt;/div&gt;
  {% endif %}
    &lt;a href=&quot;{{ url }}&quot;&gt;{{ url_linktext }}&lt;/a&gt;

  &lt;/body&gt;
&lt;/html&gt;
</pre></p>
<p></code><br />
The page shows a drop-down for persons and another one for resumes. The pattern for displaying and editing Contact objects is the same as for other data classes in this example. We have created a <em>Send Mail</em> button but it is not functional at the moment.</p>
<p>Server side handling of the <code>Contact</code> object also follows a similar pattern as for <code>Person</code> and <code>ResumeDoc</code>. The class definition is shown here:</p>
<p><code><pre class="brush: xml;">
class ContactPage(webapp.RequestHandler):
    def get(self):
        if users.get_current_user():
            url = users.create_logout_url(self.request.uri)
            url_linktext = 'Logout'
            contacts_query = Contact.all().order('-updated')
            contacts = contacts_query.fetch(100)
            resumes_query = ResumeDoc.all().order('-updated')
            resumes = resumes_query.fetch(10)
            persons_query = Person.all().order('-updated')
            persons = persons_query.fetch(100)
        else:
            contacts = []
            resumes = []
            persons = []
            url = users.create_login_url(self.request.uri)
            url_linktext = 'Login'

        template_values = {
            'contacts': contacts,
            'resumes': resumes,
            'persons': persons,
            'url': url,
            'url_linktext': url_linktext,
            'user': users.get_current_user(),
            }

        path = os.path.join(os.path.dirname(__file__), 'contact.html')
        self.response.out.write(template.render(path, template_values))

    def post(self):
        if users.get_current_user():
            key = self.request.get('key')
            pk = self.request.get('person')
            rk = self.request.get('resume')
            cover = self.request.get('cover')
            if key:
                entity = Contact.get(key)
                entity.person = Person.get(pk)
                entity.resume = ResumeDoc.get(rk)
                entity.cover = cover
            else:
                entity = Contact(person = Person.get(pk), resume = ResumeDoc.get(rk), cover = cover)
            entity.put()
        self.redirect('/contact')
</pre></p>
<p></code></p>
<h3>Step 4: The Mail API</h3>
<p>The <a href="http://code.google.com/appengine/docs/python/mail/">Mail API</a> is one of <a href="http://code.google.com/appengine/docs/python/apis.html">eight Service APIs</a>. These APIs serve to extend the base functionality.</p>
<p>The machinery for sending out emails is almost in place already. All that is needed is one more class whose post method sends the mail and updates <code>mailed</code> attribute of the Contact object and a small changes <code>contact.html</code>. The new class:</p>
<p><code><pre class="brush: python;">
class ContactMail(webapp.RequestHandler):
    def post(self):
        if users.get_current_user():
            key = self.request.get('key')
            entity = Contact.get(key)

            mail.send_mail(sender = users.get_current_user().email(),
                           to = '&quot;%s&quot; &lt;%s&gt;' % (name, email),
                           subject = 'Job Application',
                           body = entity.cover,
                           attachments=[(entity.resume.Fnam, entity.resume.Body)])
            now = datetime.datetime.utcnow()
            entity.mailed = now
            entity.put()
        self.redirect('/contact')
</pre></p>
<p></code><br />
In <code>contact.html</code>, change <code>sendMail</code> method:</p>
<p><code><pre class="brush: jscript;">
    function sendMail(k) {
        document.getElementById('mailKey').value = k;
        document.getElementById('sendMail').submit();
    }
</pre></p>
<p></code><br />
Also in <code>contact.html</code>, add this form to the body:</p>
<p><code><pre class="brush: xml;">
    &lt;form id=&quot;sendMail&quot; action=&quot;/mail&quot; method=&quot;post&quot; /&gt;
    &lt;input id=&quot;mailKey&quot; name=&quot;key&quot; type=&quot;hidden&quot; /&gt;
    &lt;/form&gt;
</pre></p>
<p></code><br />
You will also need to add it to the WSGI framework.</p>
<h3>Step 5: Markdown</h3>
<p>The emails we send using the code from step 4 are plain text. To be able to send HTML email, the mail API requires two versions: plain text version <em>and</em> HTML version. Recipients will see one or the other version depending on their mail reader and the preferences they have indicated. To keep from duplicating the work of creating two versions of every document, we choose Markdown: a library that takes plain text written according to some text conventions and converts them to HTML. Markdown was created for wiki authors who could not be persuaded to learn HTML but it will serve our purpose here.</p>
<p>A Python library for Markdown is available for download at <a href="http://code.google.com/p/python-markdown2/">http://code.google.com/p/python-markdown2/</a>. You need to copy the file markdown2.py into your code directory. Add the line <code>import markdown2</code> before referencing it.</p>
<p>To markdown plain text, add one more argument to the <code>send_mail</code> function.</p>
<p><code><pre class="brush: python;">
    html = markdown2.Markdown().convert(entity.cover),
</pre></p>
<p></code><br />
That&#8217;s it. Now all you need to do is master <a href="http://support.mashery.com/docs/customizing_your_portal/Markdown_Cheat_Sheet">markdown syntax</a>!</p>
<h3>Further Enhancements</h3>
<p>It has been a long post; hopefully it provides a flavor for how development gets done in the Google App Engine environment. The application is now ready to be uploaded to App Engine.</p>
<p>Enjoy.</p>
<br />Filed under: <a href='http://main.earlystageit.com/category/techniques/'>Techniques</a> Tagged: <a href='http://main.earlystageit.com/tag/google-app-engine/'>Google App Engine</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/earlystageit.wordpress.com/400/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/earlystageit.wordpress.com/400/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/earlystageit.wordpress.com/400/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/earlystageit.wordpress.com/400/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/earlystageit.wordpress.com/400/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/earlystageit.wordpress.com/400/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/earlystageit.wordpress.com/400/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/earlystageit.wordpress.com/400/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/earlystageit.wordpress.com/400/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/earlystageit.wordpress.com/400/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/earlystageit.wordpress.com/400/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/earlystageit.wordpress.com/400/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/earlystageit.wordpress.com/400/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/earlystageit.wordpress.com/400/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=400&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://main.earlystageit.com/2010/09/21/app-engine-in-action/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">J.Singh</media:title>
		</media:content>

		<media:content url="http://earlystageit.files.wordpress.com/2010/09/resumedatamodel.gif?w=300" medium="image">
			<media:title type="html">Resume Data Model</media:title>
		</media:content>
	</item>
		<item>
		<title>A Reverse Proxy for Google App Engine</title>
		<link>http://main.earlystageit.com/2010/07/14/gae-proxy/</link>
		<comments>http://main.earlystageit.com/2010/07/14/gae-proxy/#comments</comments>
		<pubDate>Wed, 14 Jul 2010 12:18:49 +0000</pubDate>
		<dc:creator>J</dc:creator>
				<category><![CDATA[Techniques]]></category>
		<category><![CDATA[Technology foundation]]></category>
		<category><![CDATA[Technology Strategy]]></category>
		<category><![CDATA[Amazon AWS]]></category>
		<category><![CDATA[Cloud Computing]]></category>
		<category><![CDATA[Google App Engine]]></category>
		<category><![CDATA[Security]]></category>

		<guid isPermaLink="false">http://blog.earlystageit.com/?p=354</guid>
		<description><![CDATA[Background Google App Engine supports HTTPS if you access your application through https://abc.appspot.com but not through https://www.abc.com. Google is working on a solution but there is currently no ETA. Our Reverse Proxy solution acts as a termination point for the client&#8217;s SSL &#8230; <a href="http://main.earlystageit.com/2010/07/14/gae-proxy/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=354&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<h3>Background</h3>
<p><a href="http://googleappengine.blogspot.com/2008/10/announcing-https-support-for-appspotcom.html" target="_blank">Google App Engine supports HTTPS</a> if you access your application through <span style="color:#0000ff;"><span style="text-decoration:underline;">https://abc.appspot.com</span></span> but not through <span style="color:#0000ff;"><span style="text-decoration:underline;">https://www.abc.com</span></span>. Google is working on a solution but there is currently no ETA.</p>
<p>Our Reverse Proxy solution acts as a termination point for the client&#8217;s SSL connection. It uses an SSL certificate created by a signing authority. For browsers to not issue a warning message to users, such a signed certificate is required. The Reverse Proxy acts as a front-end to Google, sending requests over SSL to your-app-id.appspot.com. It runs in its own virtual machine with one SSL connection to the client and one connection to Google App Engine. <a title="Wikipedia Definition" href="http://en.wikipedia.org/wiki/Reverse_proxy" target="_blank">Click here</a> to learn more about Reverse Proxies.</p>
<p>The solution described in this post is <a href="http://www.earlystageit.com/products" target="_blank">available as a service from Early Stage IT</a>. You can sign up now, use it as long as you need, cancel any time. The solution includes a self-signed certificate but not one signed by a certificate authority.</p>
<h3>Performance Impact</h3>
<p>We measured performance by repeatedly requesting a straightforward HTML page from the GAE infrastructure using a <a href="http://code.google.com/appengine/articles/load_test.html" target="_blank">simple load tester</a> with instrumentation added to measure response time. The reverse proxy solution can impose a 100ms-400 ms delay on a round trip.</p>
<p>The remainder of this post is a cookbook for setting up the proxy yourself if you don&#8217;t want to use our service. It is offered here in the spirit of open source.</p>
<p><span id="more-354"></span>Setting up a reverse proxy is conceptually simple. We terminate the SSL connection to <span style="color:#0000ff;"><span style="text-decoration:underline;">https://www.abc.com</span></span> at the EC2 server and have the server forward the request to <span style="color:#0000ff;"><span style="text-decoration:underline;">https://abc.appspot.com</span></span>. We built ours following these steps:</p>
<ol>
<li><strong>The foundation</strong>. A couple of years ago, Brad Larson described the process of <a href="http://www.sunsetlakesoftware.com/2008/09/13/running-drupal-website-amazon-ec2" target="_blank">setting up an EC2 server</a>. His purpose was different but the first steps of our process are the same as what he described. Following his lead, bring up an instance of a CentOS AMI. A couple of AMIs we have used are ami-0193f760 in US-East and ami-e32273a6 in US-West (both from RightScale). Snap a 1GB EBS volume to it and attach an IP address. See Brad&#8217;s article for details.</li>
<li><strong>The components</strong>. Squid requires just two components: <code>mod_ssl</code> and <code>squid</code>. Install them both using <code>yum</code> (or <code>apt-get</code> if you are using a different version of Linux). Of course, the vital component is the SSL certificate. We bought ours from GoDaddy. To generate it, generate a 2048-bit key. Use the key to generate a certificate signing request (<code>abc.csr</code> file). Finally, strip the password from the key — providing a password every time you need to start squid can be a pain.
<pre><code><pre class="brush: bash;">
openssl genrsa -des3 -out abc.key 2048
openssl req -new -key abc.key -out abc.csr
openssl rsa -in abc.key -out abc.pem&lt;/code&gt;
</pre>

 Once the certificate has been generated, copy its contents into a certificate file on the server. The path to the certificate file and the key file (</code><code>abc.pem</code>) will be needed in the next step.</pre>
</li>
<li><strong>Integration</strong>. On the EC2 server,
<ul>
<li>Update /etc/hosts and add the line <code>01.23.45.67 www.abc.com www_abc</code>.</li>
<li>Optional: for speed, add <a href="http://code.google.com/speed/public-dns/" target="_blank">Google nameservers</a> (or others good ones for your locale) to <code>/etc/resolv.conf</code>. To accomplish this, add the lines <code>nameserver 8.8.8.8</code> and <code>nameserver 8.8.4.4</code> to that file.</li>
<li>Finally, Squid Wiki <a href="http://wiki.squid-cache.org/ConfigExamples/Reverse/SslWithWildcardCertifiate" target="_blank">Configuring SSL Reverse Proxy</a> does a great job of walking through the steps of configuring the reverse proxy. We won&#8217;t reproduce the steps for creating a certificate. Here is the config file we use. Not much to it, declaring the SSL connection to the browser and to App Engine and tightening the security parameters.</li>
<pre><code><pre class="brush: bash;">
acl all src 0.0.0.0/0.0.0.0
acl Safe_ports port 443
acl gae dstdomain abc.appspot.com

visible_hostname www.abc.com
https_port 443 cert=/path/to/cert key=/path/to/key defaultsite=abc.appspot.com
cache_peer abc.appspot.com parent 443 0 no-query originserver ssl sslflags=DONT_VERIFY_PEER name=appspot
cache_peer_access appspot allow gae
always_direct allow gae

http_access allow gae Safe_ports
http_access deny all

debug_options ALL,1
</pre>

 </code></pre>
</ul>
</li>
<li><strong>Debugging</strong>. For the novice, Squid can be a little cryptic. It was built for speed, not programming ease. Here are a few hints on what to do when things don&#8217;t work according to plan:
<ul>
<li>If squid won&#8217;t even start, <code>squid –N –d l -D</code> can be useful in figuring out why.</li>
<li>If it starts but does not behave as you would expect, that last line (<code><a href="http://www.squid-cache.org/Doc/config/debug_options/" target="_blank">debug_options</a></code><a href="http://www.squid-cache.org/Doc/config/debug_options/" target="_blank"> command</a>) can be used to control the level of detail that is logged. Reading those logs can be a bit of a black art. Use your favorite search engine and the <a href="http://oreilly.com/catalog/9780596001629/index.html" target="_blank">Squid book</a> for help.</li>
</ul>
</li>
<li><strong>Operation</strong>. Once you have a basic configuration running, tighten up the security and optimize it for performance. We will be adding to this section in the future but here is a good starting point: <a href="http://onlamp.com/pub/a/onlamp/2004/02/12/squid.html" target="_blank">Six Things First Time Administrators Should Know</a>.</li>
</ol>
<h3>If you use it…</h3>
<p>…please cite this blog post. Feedback — positive or negative — gratefully accepted.</p>
<br />Filed under: <a href='http://main.earlystageit.com/category/techniques/'>Techniques</a>, <a href='http://main.earlystageit.com/category/technology-foundation/'>Technology foundation</a>, <a href='http://main.earlystageit.com/category/technology-strategy/'>Technology Strategy</a> Tagged: <a href='http://main.earlystageit.com/tag/amazon-aws/'>Amazon AWS</a>, <a href='http://main.earlystageit.com/tag/cloud-computing/'>Cloud Computing</a>, <a href='http://main.earlystageit.com/tag/google-app-engine/'>Google App Engine</a>, <a href='http://main.earlystageit.com/tag/security/'>Security</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/earlystageit.wordpress.com/354/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/earlystageit.wordpress.com/354/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/earlystageit.wordpress.com/354/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/earlystageit.wordpress.com/354/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/earlystageit.wordpress.com/354/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/earlystageit.wordpress.com/354/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/earlystageit.wordpress.com/354/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/earlystageit.wordpress.com/354/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/earlystageit.wordpress.com/354/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/earlystageit.wordpress.com/354/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/earlystageit.wordpress.com/354/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/earlystageit.wordpress.com/354/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/earlystageit.wordpress.com/354/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/earlystageit.wordpress.com/354/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=354&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://main.earlystageit.com/2010/07/14/gae-proxy/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">J.Singh</media:title>
		</media:content>
	</item>
		<item>
		<title>IT Strategy for a Web Startup</title>
		<link>http://main.earlystageit.com/2010/06/26/it-strategy-for-a-web-startup/</link>
		<comments>http://main.earlystageit.com/2010/06/26/it-strategy-for-a-web-startup/#comments</comments>
		<pubDate>Sat, 26 Jun 2010 21:20:53 +0000</pubDate>
		<dc:creator>J</dc:creator>
				<category><![CDATA[Customer Relationship]]></category>
		<category><![CDATA[Requirements]]></category>
		<category><![CDATA[Technology Strategy]]></category>
		<category><![CDATA[Agile Development]]></category>
		<category><![CDATA[Building Web Applications]]></category>
		<category><![CDATA[Cloud Computing]]></category>
		<category><![CDATA[Entrepreneurship]]></category>
		<category><![CDATA[Google App Engine]]></category>

		<guid isPermaLink="false">http://blog.earlystageit.com/?p=347</guid>
		<description><![CDATA[As we near the completion of a major web application for a startup, it&#8217;s time to reflect on the factors responsible for the success. Cloud Computing. The hardware cost for development was less than $750 including the cost of a &#8230; <a href="http://main.earlystageit.com/2010/06/26/it-strategy-for-a-web-startup/">Continue reading <span class="meta-nav">&#8594;</span></a><img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=347&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>As we near the completion of a major web application for a startup, it&#8217;s time to reflect on the factors responsible for the success.</p>
<ol>
<li>Cloud Computing. The hardware cost for development was less than $750 <em>including</em> the cost of a laptop.
<ul>
<li>We used Google App Engine for the most part but not exclusively.</li>
<li>For confidential documents, we used Amazon S3, preferring to go directly between the browser and Amazon, bypassing Google. Any doubts about whether Google will mine those documents were neutralized.</li>
</ul>
</li>
<li>Staying conceptual with the implementation.
<ul>
<li>Google App Engine was great for helping us stay conceptual. They take care of scalability, backups, OS versions, patches. All the stuff that drags you down.</li>
<li>Amazon EC2 is too low level. One has to worry about scalability, backups, OS versions, patches. We should have gone up a level by using a service like Rightscale. We haven&#8217;t yet, but it&#8217;s still an idea worth pursuing.</li>
<li>For the same reason, we would consider Amazon RDS over doing our own database management.</li>
</ul>
</li>
<li>Open Source Software. Most notably, jQuery. Their slogan is &#8220;write less, do more&#8221;. The reality matches the slogan. A rich milieu of available software allowed us to assemble components rather than write code. A couple of times we were bitten by it too. A month after we had integrated a component, the developer decided not to support it any more. We switched away. So one has to stay agile but that&#8217;s the name of the game anyway.</li>
<li>Staying conceptual with the requirements. This was the second-biggest factor: working with a team that trusts you, things don&#8217;t need to be written down to an excruciating level of detail. You hear the requirement in vague terms, you implement it, and if you had misunderstood, well, change quickly. Keeping the <a href="http://agilemanifesto.org/" target="_blank">Agile Manifesto</a> in mind.</li>
<li>Tracking shifts in business strategy. This was <em>the</em> biggest factor. The business strategy changed at least a couple of times during the project. The development was continuous, however. We were surprised to discover that the changes forced us to re-factor the code as it was re-purposed to fit the changing strategy — but very little of the code was thrown away. The re-factoring may have made it more modular, actually. By the time the business strategy had settled, we had software components that were well-tested already. Only the final integration was left to do.</li>
</ol>
<p>Your mileage will vary, of course, but these are the factors that made us successful for <em>this </em>project.</p>
<br />Filed under: <a href='http://main.earlystageit.com/category/customer-relationship/'>Customer Relationship</a>, <a href='http://main.earlystageit.com/category/requirements/'>Requirements</a>, <a href='http://main.earlystageit.com/category/technology-strategy/'>Technology Strategy</a> Tagged: <a href='http://main.earlystageit.com/tag/agile-development/'>Agile Development</a>, <a href='http://main.earlystageit.com/tag/building-web-applications/'>Building Web Applications</a>, <a href='http://main.earlystageit.com/tag/cloud-computing/'>Cloud Computing</a>, <a href='http://main.earlystageit.com/tag/entrepreneurship/'>Entrepreneurship</a>, <a href='http://main.earlystageit.com/tag/google-app-engine/'>Google App Engine</a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/earlystageit.wordpress.com/347/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/earlystageit.wordpress.com/347/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/earlystageit.wordpress.com/347/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/earlystageit.wordpress.com/347/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/earlystageit.wordpress.com/347/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/earlystageit.wordpress.com/347/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/earlystageit.wordpress.com/347/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/earlystageit.wordpress.com/347/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/earlystageit.wordpress.com/347/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/earlystageit.wordpress.com/347/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/earlystageit.wordpress.com/347/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/earlystageit.wordpress.com/347/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/earlystageit.wordpress.com/347/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/earlystageit.wordpress.com/347/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=main.earlystageit.com&amp;blog=5823501&amp;post=347&amp;subd=earlystageit&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://main.earlystageit.com/2010/06/26/it-strategy-for-a-web-startup/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
	
		<media:content url="" medium="image">
			<media:title type="html">J.Singh</media:title>
		</media:content>
	</item>
	</channel>
</rss>
