Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1287 lines (906 sloc) 100 KB
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Benedikt Deicke - Software Engineer</title>
<id>http://benediktdeicke.com/</id>
<link href="http://benediktdeicke.com/"/>
<link href="http://feeds.feedburner.com/benediktdeicke" rel="self"/>
<updated>2017-01-04T00:00:00+00:00</updated>
<author>
<name>Benedikt Deicke</name>
</author>
<entry>
<title>2016 in Review</title>
<link rel="alternate" href="/2017/01/year-in-review/"/>
<id>/2017/01/year-in-review/</id>
<published>2017-01-04T00:00:00+00:00</published>
<updated>2017-01-04T00:00:00+00:00</updated>
<author>
<name>Benedikt Deicke</name>
</author>
<summary type="html">&lt;p&gt;A year ago, I published my first &lt;a href="http://benediktdeicke.com/2016/01/year-in-review/"&gt;Year in Review&lt;/a&gt; post. I did not publish anything since that review and therefore was a little hesitant about writing one for 2016. It was a tweet by Jamie Lawrence that convinced me to write this review:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Year in review” posts are great way of rewriting history, and a terrible way to compare yourself with others – &lt;a href="https://twitter.com/ideasasylum/status/814447889351143424"&gt;Jamie Lawrence&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, my intention isn't to...&lt;/p&gt;</summary>
<content type="html">&lt;p&gt;A year ago, I published my first &lt;a href="http://benediktdeicke.com/2016/01/year-in-review/"&gt;Year in Review&lt;/a&gt; post. I did not publish anything since that review and therefore was a little hesitant about writing one for 2016. It was a tweet by Jamie Lawrence that convinced me to write this review:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Year in review” posts are great way of rewriting history, and a terrible way to compare yourself with others &amp;ndash; &lt;a href="https://twitter.com/ideasasylum/status/814447889351143424"&gt;Jamie Lawrence&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, my intention isn&amp;#39;t to rewrite history. Instead, I want to preserve the things (both good and bad) that happened this year.&lt;/p&gt;
&lt;p&gt;To give you some perspective, please allow me to introduce myself. I&amp;#39;m Benedikt Deicke, a software engineer and consultant who launched &lt;a href="http://stagecms.com"&gt;Stage&lt;/a&gt;, a content management system for bands, as a side project in 2015.&lt;/p&gt;
&lt;h2 id="toc_0"&gt;The year in general&lt;/h2&gt;
&lt;p&gt;Looking back at 2016, I&amp;#39;m a little disappointed, even though I shouldn&amp;#39;t be. My goals for the year included &lt;em&gt;increasing my revenue&lt;/em&gt;, &lt;em&gt;growing Stage to 1,000€ MRR&lt;/em&gt;, and &lt;em&gt;launching a productized consulting offering&lt;/em&gt;. I only count the revenue goal as a success, but more on that below.&lt;/p&gt;
&lt;h2 id="toc_1"&gt;The consulting business&lt;/h2&gt;
&lt;p&gt;In contrast to 2015, the consulting business was a smooth ride. I was able to hit my revenue goal for the year by consulting alone, and even a few months early. I had the pleasure of working on interesting projects, together with people I like and admire.&lt;/p&gt;
&lt;p&gt;The lessons learned in previous years definitely helped to make this possible.&lt;/p&gt;
&lt;p&gt;I also sold some coaching and mentoring retainer packages. While this was part of my goals for the year, I only call it half a success, because I never announced them publicly.&lt;/p&gt;
&lt;h2 id="toc_2"&gt;The SaaS business&lt;/h2&gt;
&lt;p&gt;One of my biggest goals this year, was to grow Stage to 1.000€ in monthly recurring revenue. I started the year with a baseline of 357€ MRR. Unfortunately, I quickly realized that adding 643€ was going to be tough. My target audience (professional musicians) are hard to reach online and don&amp;#39;t care about their websites that much. In the end, I managed to close two deals, adding 58€ in revenue. A third deal I closed in 2016 will start adding another 79€ in MRR this February. So, depending on how you count it, I&amp;#39;m at least 506€ short of my goal.&lt;/p&gt;
&lt;p&gt;Here are the things I tried to grow Stage this year:&lt;/p&gt;
&lt;h3 id="toc_3"&gt;Lead generation&lt;/h3&gt;
&lt;p&gt;To help achieve my MRR goal, I hired a lead generation service. The goal was to get prequalified leads for one-on-one product demos. This was one of the worst experiences this year. While happily taking my money, they failed to deliver &lt;em&gt;at all&lt;/em&gt;. Instead I got put off with empty promises for the first few weeks, until they finally decided to stop responding to messages at all. It took several weeks to get both the subscription cancelled and my money back. The service has been shut down completely since.&lt;/p&gt;
&lt;h3 id="toc_4"&gt;Doing cold outreach&lt;/h3&gt;
&lt;p&gt;In another effort, I hired a virtual assistant to help me with researching potential leads. It was a great experience and my virtual assistant was a great help. She went through several record label websites, compiled lists of bands, and researched their managers.&lt;/p&gt;
&lt;p&gt;I later used that list and emailed potential customers. While I got a pretty good response rate, none of them were interested in the product.&lt;/p&gt;
&lt;p&gt;In addition, I tried reaching out to record labels. Thanks to a friend, I even got an in-person meeting with a major record label. Unfortunately they also were not interested in my offerings.&lt;/p&gt;
&lt;p&gt;The three deals I closed this year, all came via referrals from friends and people I met in previous years. So, to sum it up, it was a great learning experience with no effect on growth at all.&lt;/p&gt;
&lt;h3 id="toc_5"&gt;Bye, bye profit…&lt;/h3&gt;
&lt;p&gt;To make things worse, the hosting provider I was using for Stage, shut down in early 2016. They were a perfect fit for the requirements Stage had. Also they were quite cheap compared to the alternatives. In hindsight that should have been a warning sign.&lt;/p&gt;
&lt;p&gt;They were kind enough to announce the end of their service several months in advance. Still, I had to move to multiple new providers (they were the only ones that combined all of my needs in one service) and the costs skyrocketed. What once was a profitable (read: making a bit more than 0€) product was now losing several hundred euros per month. Most of it was fixed costs, though. Having my MRR goal in mind I had some hopes to make it up in added revenue, but as you know that never happened.&lt;/p&gt;
&lt;p&gt;For 2017, I&amp;#39;m hoping to be able restructure parts of the product to be able to cut costs down to a level where it stops draining my bank account.&lt;/p&gt;
&lt;h2 id="toc_6"&gt;New endeavors&lt;/h2&gt;
&lt;p&gt;In previous years, I tried to focus on Stage as my one and only side project. In the second half of the year, I allowed myself to think about new ideas.&lt;/p&gt;
&lt;p&gt;One result of this was &lt;a href="http://evgadgets.com"&gt;EVGadgets&lt;/a&gt;, an affiliate website for electric vehicle accessories. The plan was to outsource the writing on Upwork, publish a lot of reviews, get picked up by Google and earn a lot of money using affiliate links.&lt;/p&gt;
&lt;p&gt;I totally underestimated how hard it is to find good people on Upwork, though. None of the reviews I got was great and all had to be edited quite a bit. A few of the reviews were so bad, it didn&amp;#39;t even make sense to edit them.&lt;/p&gt;
&lt;p&gt;What really kicked all my motivation out of me, was dealing with the accounting and legal side of hiring through Upwork. The amount of work I had to put in to be on the safe side made it unbearable.&lt;/p&gt;
&lt;p&gt;Right now, the site is still up and I&amp;#39;m not ruling out giving it another try. But it&amp;#39;s definitely not going to make any money anytime soon.&lt;/p&gt;
&lt;h2 id="toc_7"&gt;The Podcast&lt;/h2&gt;
&lt;p&gt;In 2016, Christoph Engelhardt and I continued recording episodes for our (German) podcast &lt;a href="http://nebenberufstartup.de"&gt;Nebenberuf Startup&lt;/a&gt;. In total, we published 17 episodes. It&amp;#39;s fewer episodes than we set out to publish. We failed to stick to our bi-weekly schedule because of our other commitments. Still, it was fun to record episodes with people like Steli Efti, Åke Brattberg, and Daniel Alm.&lt;/p&gt;
&lt;h3 id="toc_8"&gt;FemtoConf&lt;/h3&gt;
&lt;p&gt;The most exciting thing coming out of the podcast this year is &lt;a href="http://femtoconf.com"&gt;FemtoConf 2017&lt;/a&gt;. What was meant as a meetup with some of our listeners, turned into an international mini conference. People form Germany, Finland, Czech Republic, Spain, France, Slovenia, Russia, and even the US bought a ticket to be part of it. Check back in a year to learn how it went.&lt;/p&gt;
&lt;h2 id="toc_9"&gt;Rails Girls Summer of Code and Refugees on Rails&lt;/h2&gt;
&lt;p&gt;There was no &lt;a href="http://railsgirls.com/frankfurt"&gt;Rails Girls Frankfurt&lt;/a&gt; event in 2016. Instead I joined &lt;a href="https://www.facebook.com/roraschaffenburg/"&gt;Refugees on Rails Aschaffenburg&lt;/a&gt; as a coach and &lt;a href="http://railsgirlssummerofcode.org"&gt;Rails Girls Summer of Code&lt;/a&gt; as a supervisor. As always, I loved meeting new people while teaching how to become a software developer.&lt;/p&gt;
&lt;h2 id="toc_10"&gt;Goals for 2017&lt;/h2&gt;
&lt;h3 id="toc_11"&gt;Maintain overall revenue&lt;/h3&gt;
&lt;p&gt;While I&amp;#39;m a little wary about the usefulness of this goal, I&amp;#39;d like to maintain my revenue generated in 2016. While it helps to have a clear number in mind, it also makes it easy to choose the quicker ways to make money over the ones that take time to grow.&lt;/p&gt;
&lt;h3 id="toc_12"&gt;Relaunch my website with productized consulting offerings&lt;/h3&gt;
&lt;p&gt;I definitely want to relaunch my website this year. For one, I&amp;#39;m getting a bit tired by the design. More importantly though, I finally want to focus on offering productized consulting packages.&lt;/p&gt;
&lt;h3 id="toc_13"&gt;Launch a new product or relaunch Stage with a twist&lt;/h3&gt;
&lt;p&gt;During 2016, it became clear that Stage is a tough product to sell. Maybe it&amp;#39;s the target audience, the product, or me just being bad at selling. For 2017, I want to come up with something new and launch it. That might be a new product, or a new major feature of Stage. Whatever it&amp;#39;ll be, I want to make sure, it&amp;#39;ll fix a pain people are having, making it easier to sell it.&lt;/p&gt;
&lt;h3 id="toc_14"&gt;Move into a new apartment&lt;/h3&gt;
&lt;p&gt;On the personal side of things, I want to move into a new apartment. We&amp;#39;ve been kinda looking for a new one for a while now, but it was never a serious undertaking. Promoting this to a goal for the year will help to make it happen.&lt;/p&gt;
&lt;h2 id="toc_15"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;As mentioned earlier, I&amp;#39;m a bit disappointed about 2016. I was wishing for a better result when it comes to Stage. Nonetheless, I learned a lot of things while trying to make it work. Most importantly: There are no silver bullets for anything.&lt;/p&gt;
&lt;p&gt;I hope you got some value out of reading this. I tried to include all business related ups and downs I had this year. Thanks for reading and a &lt;strong&gt;Happy New Year 2017&lt;/strong&gt;!&lt;/p&gt;
</content>
</entry>
<entry>
<title>2015 in Review</title>
<link rel="alternate" href="/2016/01/year-in-review/"/>
<id>/2016/01/year-in-review/</id>
<published>2016-01-04T00:00:00+00:00</published>
<updated>2016-01-04T00:00:00+00:00</updated>
<author>
<name>Benedikt Deicke</name>
</author>
<summary type="html">&lt;p&gt;This is the first time I’m writing a “Year in Review” post. In the past, I wasn’t sure about the value of me writing a post like this. However, I always liked to read similar posts by people I know. I hope there’s something in this post that is of value to you, or at least inspires you in some way or another.&lt;/p&gt;
&lt;p&gt;Let me introduce myself: I’m Benedikt Deicke. I’m currently running two businesses side...&lt;/p&gt;</summary>
<content type="html">&lt;p&gt;This is the first time I’m writing a “Year in Review” post. In the past, I wasn’t sure about the value of me writing a post like this. However, I always liked to read similar posts by people I know. I hope there’s something in this post that is of value to you, or at least inspires you in some way or another.&lt;/p&gt;
&lt;p&gt;Let me introduce myself: I’m Benedikt Deicke. I’m currently running two businesses side-by-side. Most of the time I’m working as a consultant, helping small and medium businesses with their software development projects. Additionally I’m working on a product called Stage, a &lt;a href="http://stagecms.com"&gt;content management system for band websites&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="toc_0"&gt;The year in general&lt;/h2&gt;
&lt;p&gt;All in all, I’m quite happy about how this year turned out to be. The fact that it was off to a rough start probably had some influence on that. One of my clients of 2014 was close to bankruptcy and still owed me a bunch of cash. As a result, I barely made enough money to pay my rent and living in 2014. That was a real downer, but it also had a positive effect on me. For the first time ever, I made the choice to set goals for 2015. I set an &lt;em&gt;overall revenue goal&lt;/em&gt;, committed on &lt;em&gt;launching Stage&lt;/em&gt;, agreed to &lt;em&gt;write 20 blog posts&lt;/em&gt;, and made the decision to &lt;em&gt;refurnish our bedroom&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="toc_1"&gt;The SaaS business&lt;/h2&gt;
&lt;p&gt;I started the year focusing on Stage. It wasn’t launched to the public yet, and I was working on some new features to be able to onboard a new client. Everything got done in time and their website launched in late March. After this was done, I considered the product as “good enough” and started working on the marketing website. It took a while, but I was able to achieve my goal and launch it in June.&lt;/p&gt;
&lt;p&gt;In preparation for the launch, I also started sending emails to my launch list. It was a nice learning experience and a first for me. I also set up an email course about building band websites.&lt;/p&gt;
&lt;p&gt;A few weeks after the launch, &lt;a href="https://www.producthunt.com/tech/stage"&gt;Stage got posted on Product Hunt&lt;/a&gt;. It did quite well, and I got a bunch of traffic to its website. Unfortunately, none of this had any effect. I haven&amp;#39;t gotten a single signup since the launch of Stage.&lt;/p&gt;
&lt;p&gt;I did some experiments with advertising on Facebook. That got me a few signups on the email course, but none of them converted to signups for the actual product.&lt;/p&gt;
&lt;p&gt;If I didn&amp;#39;t have paying customers, I’d consider the product a total failure. For now, I’m assuming that I wasn’t able to reach the target audience with the approaches I tried. As a result, I’ll try some direct sales approaches in 2016. We’ll see how that goes…&lt;/p&gt;
&lt;h2 id="toc_2"&gt;The consulting business&lt;/h2&gt;
&lt;p&gt;As I mentioned before, the consulting business was off to a rough start this year. Luckily, my client avoided bankruptcy and was able to pay me in February. Still, it wasn’t a great experience. Amazingly enough, something similar happened a few months later. I didn’t get paid for months, because of a delayed investment. Eventually, the client got their investment and has been able to pay on time since.&lt;/p&gt;
&lt;p&gt;During the time I was waiting to get paid, the taxes for both 2013 and 2014, as well as the tax deposits for 2015 were due. That cut a huge hole into my savings account. Luckily, I had known this day would come eventually. I had set aside enough to pay the taxes and still be able to pay my rent while waiting for my invoices to get paid.&lt;/p&gt;
&lt;p&gt;I definitely learned two things this year:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I won’t let anyone talk their way out of an upfront deposit anymore.&lt;/li&gt;
&lt;li&gt;I’ll always keep enough cash in the bank to pay my living for at least 6 months.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Against all odds, I hit my revenue goal for this year. I was able to make enough money to end this year with a profit.&lt;/p&gt;
&lt;h2 id="toc_3"&gt;The Podcast&lt;/h2&gt;
&lt;p&gt;I’m co-hosting a (German) podcast together with my friend &lt;a href="http://christophengelhardt.com"&gt;Christoph Engelhardt&lt;/a&gt;. It’s called &lt;a href="http://www.nebenberufstartup.de"&gt;Nebenberuf Startup&lt;/a&gt;, and we’re mostly talking about building a software related product business on the side. It started in early 2014 as a result of our bi-weekly mastermind-y calls. We still joke about it being our very own group therapy session. Nonetheless, it became quite successful in 2015. We recorded our &lt;a href="http://nebenberufstartup.de/folge50"&gt;50th episode&lt;/a&gt; just a few weeks back, grew an online community of more than 50 people around it, and interviewed some amazing people.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;(If you&amp;#39;re into Year in Review posts, &lt;a href="http://www.christophengelhardt.com/year-review-2015/"&gt;check out Christoph&amp;#39;s over here&lt;/a&gt;…)&lt;/em&gt;&lt;/p&gt;
&lt;h2 id="toc_4"&gt;Rails Girls and Rails Girls Summer of Code&lt;/h2&gt;
&lt;p&gt;Once again, I was involved in the &lt;a href="http://railsgirls.com/frankfurt"&gt;Rails Girls Frankfurt&lt;/a&gt; event that took place in autumn. This time not just as a coach, but also as a part of the organizing team. The event was a success, and it was great to meet so many inspiring people.&lt;/p&gt;
&lt;p&gt;I also joined the &lt;a href="http://railsgirlssummerofcode.org"&gt;Rails Girls Summer of Code&lt;/a&gt; as a supervisor and coach again. While I’ve been a supervisor in the previous two years, this year was an even better experience. It was awesome to work with so many people on making the world a little bit better.&lt;/p&gt;
&lt;h2 id="toc_5"&gt;Conferences&lt;/h2&gt;
&lt;p&gt;I only visited one conference in 2015: &lt;a href="http://microconfeurope.com"&gt;MicroConf Europe&lt;/a&gt;. Once again, it was an amazing experience. I was stoked to meet some of my online friends, talk to inspiring people and learn a thing or two during the talks.&lt;/p&gt;
&lt;h2 id="toc_6"&gt;The personal side of things&lt;/h2&gt;
&lt;p&gt;Okay, I’ll admit it right away: I failed to accomplish my goal to refurnish the bedroom. While my girlfriend and I did some planning, we never found the time to actually pull it off.&lt;/p&gt;
&lt;p&gt;At least we found the time to spend a beautiful weekend in Bremen, as well as some amazing 2,5 weeks in sunny Spain. It was lovely to just relax in the sun for a while.&lt;/p&gt;
&lt;h2 id="toc_7"&gt;My goals for 2016&lt;/h2&gt;
&lt;p&gt;Having goals for 2015 helped a lot to stay focused and not get distracted by too many things. It sometimes made it easier to come to a decision, because I was able to judge things about their effect on my goals.&lt;/p&gt;
&lt;h3 id="toc_8"&gt;Increase overall revenue&lt;/h3&gt;
&lt;p&gt;In 2016, want to increase my overall revenue by about 30%. In theory, I could accomplish that by just doing consulting the entire year. However, that would probably have a negative effect on my other goals.&lt;/p&gt;
&lt;h3 id="toc_9"&gt;Grow Stage to at least 1000€ MRR&lt;/h3&gt;
&lt;p&gt;As I mentioned above, Stage did not grow at all this year. Yes, I onboarded a new client and increased the MRR this way, I already had their commitment in 2014. When it comes to Stage, my primary focus for 2016 will be on growing both revenue and the number of customers.&lt;/p&gt;
&lt;h3 id="toc_10"&gt;Launch a productized consulting offering&lt;/h3&gt;
&lt;p&gt;Throughout the year, I had several discussions with fellow consultants about productized offerings. I have some ideas in mind, that I’d want to try in 2016. My goal is to have at least one productized consulting offer launched to the public and making some money.&lt;/p&gt;
&lt;h2 id="toc_11"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;As always, the year had its ups and downs. In the end, I’d consider it one of my most successful years so far. Let’s hope 2016 exceeds it!&lt;/p&gt;
&lt;p&gt;If you made it this far, I hope you got some value out of this post. All that remains is wishing you a very &lt;strong&gt;Happy New Year 2016&lt;/strong&gt;!&lt;/p&gt;
</content>
</entry>
<entry>
<title>Launching a product, in just 3652 days</title>
<link rel="alternate" href="/2015/07/launching-a-product/"/>
<id>/2015/07/launching-a-product/</id>
<published>2015-07-15T00:00:00+00:00</published>
<updated>2015-07-15T00:00:00+00:00</updated>
<author>
<name>Benedikt Deicke</name>
</author>
<summary type="html">&lt;p&gt;Today, I launched &lt;a href="http://stagecms.com"&gt;Stage&lt;/a&gt;, a content management system, designed for band websites. As of today, it’s out there for everyone to see and use. It’s already making 307€ in monthly recurring revenue and hosting the websites of five bands, including those of internationally known bands like &lt;a href="http://nightwish.com"&gt;Nightwish&lt;/a&gt;, &lt;a href="http://epica.nl"&gt;Epica&lt;/a&gt;, and &lt;a href="http://sonataarctica.info"&gt;Sonata Arctica&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is a pretty huge deal for me personally. I love building software products...&lt;/p&gt;</summary>
<content type="html">&lt;p&gt;Today, I launched &lt;a href="http://stagecms.com"&gt;Stage&lt;/a&gt;, a content management system, designed for band websites. As of today, it’s out there for everyone to see and use. It’s already making 307€ in monthly recurring revenue and hosting the websites of five bands, including those of internationally known bands like &lt;a href="http://nightwish.com"&gt;Nightwish&lt;/a&gt;, &lt;a href="http://epica.nl"&gt;Epica&lt;/a&gt;, and &lt;a href="http://sonataarctica.info"&gt;Sonata Arctica&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is a pretty huge deal for me personally. I love building software products, but usually fail to launch them. My list of unfinished side projects is long, and I’ve got my own little collection of domain names that I registered, but never really used.&lt;/p&gt;
&lt;p&gt;&lt;img src="/assets/2015/07/stage.png" alt="The website of Stage"&gt;&lt;/p&gt;
&lt;p&gt;Looking back, the history of Stage started a little over eleven years ago. Back then, I was just out of high school and only had few of years experience with programming and building websites. It was also the time when I started listening to metal music and growing my hair. Nightwish was one of my favorite bands at the time, so one day I wrote an e-mail to their webmasters and offered my help. To my surprise, I actually got a reply and a short while later started building a CMS for their band website. The website launched on April 1st, 2006.&lt;/p&gt;
&lt;p&gt;I used the CMS for one or two other websites, but it was a pain on so many levels. I had no idea what I was doing. I haven’t had heard about “version control systems”, or “tests”, or “object oriented design” so it quickly became an unmaintainable mess.&lt;/p&gt;
&lt;p&gt;Meanwhile, I started studying computer science and started working as a freelance developer on the side. Eventually, I learned Ruby and Ruby on Rails, which is now my language and framework of choice. Every now and then, I’d start building a new side project. Sometimes alone, sometimes with friends: A location based database of metal shows of your favorite bands in your area; A website to find new people nearby for leisure time activities; A website with online menus (and reviews) for your universities canteen; A service that analyzed tweets to get the weather for a given location (Yeah, that was more of a fun project); An app to manage your recipes and grocery lists on a tablet mounted in your kitchen (This was even before the iPad existed… We thought about hacking interactive photo frames for this); A web application to discover the location of your band/movie/product’s fans; as well as several others, including the ones I forgot about…&lt;/p&gt;
&lt;p&gt;Every single time I was excited. “This is awesome! This will be the next big thing!”, I told myself. As you can probably guess by now… none of them made it to anything substantial. &lt;strong&gt;I’d work on them for a few weeks, sometimes even months, but eventually I’d lose motivation and never get back to it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Over the years, alongside my studies and work, I also made several attempts to rebuild the CMS. I had learned a lot over the years, and I knew I could do better.&lt;/p&gt;
&lt;p&gt;The first time I started rewriting it from scratch I started by designing a beautiful user interface. I’d implement a sophisticated authentication and authorization system that would allow different roles and permissions. The new system also had a very useful activity feed, but the thing was… you couldn’t manage your website’s content. &lt;strong&gt;I got carried away by superfluous features&lt;/strong&gt; and lost motivation before even getting to the core feature.&lt;/p&gt;
&lt;p&gt;The next time I started from scratch, focusing on the core features first. I started to implement all the amazing features I had in mind for the content managing part of the system. The plan was to have all the features of the old version in there, as well as several new ones. I had about two months so we could relaunch the Nightwish website using the new system. There was a lot to do, so I started working on several features in parallel. Pushing forward on all of them, but &lt;strong&gt;never finishing just one&lt;/strong&gt;. As a result, I couldn’t hold the deadline. We had to use the old CMS one more time, I lost motivation and abandoned the rewrite project.&lt;/p&gt;
&lt;p&gt;Meanwhile, I finished college and started working in my first job. I knew that if I ever wanted to successfully build my own product, I had to learn about VC, investors and funding. So getting a job in a startup was the only logical choice. I was working as a developer and never was involved with pitching the product to investors. Nonetheless, I tried to watch, listen and learn about the process. I experienced the struggles startups encounter when they constantly need outside investment to survive.&lt;/p&gt;
&lt;p&gt;I was still working on side-projects from time to time, but like before, they never really got anywhere. I even gave the CMS another try, but having a full-time job at an early stage startup didn&amp;#39;t leave much time to actually work on it. I’d get to it every few weeks and spend a weekend updating dependencies so everything was up-to-date. More often than not, things would break and I’d spend the rest of the weekend with fixing stuff. &lt;strong&gt;It became pretty clear that my approach to building products had a flaw.&lt;/strong&gt; At the same time, I started to realize that outside funding wasn’t the only way to build a business. Watching companies like 37signals, GitHub and Travis CI bootstrap from small teams into successful businesses was motivating.&lt;/p&gt;
&lt;p&gt;In 2012 a &lt;a href="http://www.it-engelhardt.de"&gt;friend&lt;/a&gt; recommended &lt;a href="http://www.startupbook.net"&gt;Rob Walling’s “Start Small, Stay Small: A Developer’s Guide to Launching a Startup”&lt;/a&gt; to me. To say it changed my life is probably an exaggeration, but it was an eye-opener nonetheless. It definitely put things into a new perspective. I learned about “Micropreneurship” and that starting and staying small might actually be a good thing. &lt;strong&gt;A successful startup doesn’t have to be “the next big thing”&lt;/strong&gt; and sell to some large company for an incredibly huge amount. It taught me about the importance of finding a niche, why it’s better to sell to businesses instead of consumsers, why marketing is the most important thing, and much, much more.&lt;/p&gt;
&lt;p&gt;A few months later, I quit my job and started working as a freelance software engineer. I had several reasons for it, but one was definitely to be able to carve out more time to work on Stage. This time, I approached things differently.&lt;/p&gt;
&lt;p&gt;I once again got rid of all the code I had written before, but didn’t start programming again. Instead, I started writing copy for a landing page and put it online. I signed up for Google AdWords and spent about 100€ to send traffic to this page. It took a while, but about 50 people actually signed up for the launch notification mailing list I had set up. &lt;strong&gt;While I didn’t really validate my idea this way, it was a huge boost in motivation.&lt;/strong&gt; Before actually getting back into building the product, I reached out to a smaller band, pitching them my product and actually naming a price. They agreed to start paying as soon as the product is ready and they’re making some money by selling records and merchandize. To me, this was enough to have a reason to start writing code. I started implementing the product, so it would fit the needs of this &lt;a href="http://ultimate-music-covers.com"&gt;one, small, single-page band website&lt;/a&gt; (the “Newcomer” plan).&lt;/p&gt;
&lt;p&gt;&lt;img src="/assets/2015/07/umc.png" alt="Ultimate Music Covers was the first website on Stage"&gt;&lt;/p&gt;
&lt;p&gt;I managed to set aside one day per week to focus on building the CMS. It took about 4 months, but in early 2014 it was up and running, hosting this one small band website. &lt;strong&gt;Holy shit, I actually managed to ship something!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It still didn’t earn any money, and it still wasn’t launched to the public - but it was online and had users other than myself. Around the same time, I got lucky and started talking to &lt;a href="http://sonataarctica.info"&gt;two&lt;/a&gt; &lt;a href="http://epica.nl"&gt;other&lt;/a&gt; bands about redoing their band websites.&lt;/p&gt;
&lt;p&gt;Both were significantly larger and definitely would need a few more features (the “Professional” plan). I managed to get them both to commit to paying for the new system.&lt;/p&gt;
&lt;p&gt;So I started implementing the missing features, always having those to websites in mind. I’d build everything they’d need, but nothing else. Again, a few months later, both websites went live, using Stage.&lt;/p&gt;
&lt;p&gt;&lt;img src="/assets/2015/07/epica.png" alt="The website of Epica"&gt;&lt;/p&gt;
&lt;p&gt;I had three bands using the CMS for their band websites. All of them committed to pay for it on a recurring basis. So in August, I integrated payments into the system and asked them to start paying. This was one of the scariest things in the whole journey.&lt;/p&gt;
&lt;p&gt;Asking those customers to enter their credit card info and start paying. Boy was I relieved when they did so, and everything worked! &lt;strong&gt;I actually started earning money! With my own product!&lt;/strong&gt; You could see me smiling for the rest of the day…&lt;/p&gt;
&lt;p&gt;I probably could have launched by then, but there was one thing I still wanted to do get done: Get the &lt;a href="http://nightwish.com"&gt;Nightwish&lt;/a&gt; website on &lt;a href="http://stagecms.com"&gt;Stage&lt;/a&gt;. They’d still need a few more features (i.e. Internationalization) and it would at least double the traffic the system would have to handle. Once again, I started by talking to the band, trying to get them to commit to paying for the new CMS and only after they did, started working on the new features (the “Rockstar” plan). The website launched in March and they started paying just a few weeks after.&lt;/p&gt;
&lt;p&gt;&lt;img src="/assets/2015/07/nightwish.png" alt="The website of Nightwish"&gt;&lt;/p&gt;
&lt;p&gt;Fast forward to today: I launched Stage to the public. I’m still embarrassed, because there a lot of things missing and it has some rough edges here and there. I could have waited with the launch until I polished everything, but as you probably know, there’s always something else coming up. So instead, I took Reid Hoffman&amp;#39;s famous quote (&amp;ldquo;If you are not embarrassed by the first version of your product, you’ve launched too late.&amp;rdquo;) by heart and here it is!&lt;/p&gt;
&lt;p&gt;As you can see, I learned a lot of things about building products the hard way. &lt;strong&gt;In hindsight it’s ridiculous how I could work on this for almost ten years.&lt;/strong&gt; Especially, since there is no proof that this will eventually lead to something. It’s still far from being a successful product, and &lt;em&gt;the path ahead is definitely a tough one&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;To sum this up, here are a few things I learned over the years:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Start small&lt;/strong&gt;, you can always build up from there. Just take one small step after the other.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Focus on the core of your product.&lt;/strong&gt; Don’t get distracted by fluffy details around the core.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Launch a landing page&lt;/strong&gt; that describes your product and has a way for people to sign up for a mailing list. It helps a lot with motivation to see a sign-up notification from time to time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Talk to people.&lt;/strong&gt; Talk to your potential customers and people around you.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Marketing is important.&lt;/strong&gt; It doesn’t help to build a cool product if people don’t know about it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don’t be afraid of sales.&lt;/strong&gt; Your product is solving a problem, it’s only fair that your customers pay money for it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Be persistent.&lt;/strong&gt; I had several chances to just give up, but I didn’t. There are rough times, but you can push through them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stay focused.&lt;/strong&gt; I stopped working on new side-projects, just focusing on this one product. This helped me make actual progress.&lt;/li&gt;
&lt;/ol&gt;
</content>
</entry>
<entry>
<title>The tools that help me build my product</title>
<link rel="alternate" href="/2015/01/the-tools-that-help-me-build-my-product/"/>
<id>/2015/01/the-tools-that-help-me-build-my-product/</id>
<published>2015-01-30T00:00:00+00:00</published>
<updated>2015-01-30T00:00:00+00:00</updated>
<author>
<name>Benedikt Deicke</name>
</author>
<summary type="html">&lt;p&gt;As a single founder, my time is valuable. I have to split my time between working for consulting clients, doing paperwork and acquisition, and working on my product &lt;a href="http://stagecms.com"&gt;Stage&lt;/a&gt;. I wouldn’t get anything done, without several tools I heavily rely on. &lt;/p&gt;
&lt;p&gt;In the following, I want to share the most important products, that help me bootstrap my own product. Hopefully, you’ll get some insights into my toolset...&lt;/p&gt;</summary>
<content type="html">&lt;p&gt;As a single founder, my time is valuable. I have to split my time between working for consulting clients, doing paperwork and acquisition, and working on my product &lt;a href="http://stagecms.com"&gt;Stage&lt;/a&gt;. I wouldn’t get anything done, without several tools I heavily rely on. &lt;/p&gt;
&lt;p&gt;In the following, I want to share the most important products, that help me bootstrap my own product. Hopefully, you’ll get some insights into my toolset and discover one or two products you didn’t know about, yet. &lt;/p&gt;
&lt;h2 id="toc_0"&gt;Sales and Marketing&lt;/h2&gt;
&lt;p&gt;Let’s get started with the tools I use for marketing. At the moment, Stage is not launched publicly. Nonetheless, a few bands use it already, and I’m slowly adding more customers to it. As I’m slowly getting closer to the public launch, I expect this list to grow quite a bit over time.&lt;/p&gt;
&lt;h3 id="toc_1"&gt;&lt;a href="http://trello.com"&gt;Trello&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To manage the sales funnel, I’m using a Trello board. Each Lead gets a card that is tagged with a plan I’m trying to sign them up to. The card then moves from &lt;em&gt;New&lt;/em&gt;, to &lt;em&gt;Doing&lt;/em&gt; , to &lt;em&gt;Won&lt;/em&gt; or &lt;em&gt;Lost&lt;/em&gt;. It’s very simple but helps to keep things organized. &lt;/p&gt;
&lt;h3 id="toc_2"&gt;&lt;a href="http://bufferapp.com"&gt;Buffer&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;From time to time, I come across interesting content about music business and band websites in particular. In order to share them with people following Stage on Twitter and Facebook, I’m using Buffer to queue up posts. Buffer will then publish them, based on a predefined schedule.&lt;/p&gt;
&lt;h3 id="toc_3"&gt;&lt;a href="http://getdrip.com"&gt;Drip&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For now, the only thing you can do when visiting Stage’s website, is to sign up to a mailing list. This list is currently hosted on Drip. I used to use &lt;a href="http://mailchimp.com"&gt;Mailchimp&lt;/a&gt; for this. However, in order to get e-mail marketing automation in every customer lifetime phase (lead, trial, paying), switched to drip just a few days ago.&lt;/p&gt;
&lt;h2 id="toc_4"&gt;Development and Operations&lt;/h2&gt;
&lt;p&gt;Of course, to run a SaaS product, there’s also infrastructure involved. At the moment, I rely on these providers: &lt;/p&gt;
&lt;h3 id="toc_5"&gt;&lt;a href="http://shellycloud.com"&gt;Shelly Cloud&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Stage is hosted on Shelly Cloud, a Platform as a Service provider focused on Ruby applications. The main reason I chose them is because they allow storing files on disk and provide decent front-end caching out of the box. On top of that, they’re are a bit cheaper compared to similar other services and provide incredible customer support. &lt;/p&gt;
&lt;h3 id="toc_6"&gt;&lt;a href="http://dnsimple.com"&gt;DNSimple&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To manage DNS records I rely heavily on DNSimple. Their service is targeted to developers and makes it very easy to get things done without getting lost in strange user interfaces. Additionally they also sell SSL certificates at a reasonable price. &lt;/p&gt;
&lt;h3 id="toc_7"&gt;&lt;a href="http://stripe.com"&gt;Stripe&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For credit card processing and subscription management, I rely on Stripe. Their service makes it easy to get set up quickly and start earning money. During the last couple of years, the eco-system around it has also evolved quite a bit. There are a ton of tools for analytics, invoicing, or churn prevention, all of them set up with a few clicks. &lt;/p&gt;
&lt;h3 id="toc_8"&gt;&lt;a href="http://github.com"&gt;GitHub&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’m hosting all of Stage’s code on GitHub. In my opinion it’s the best tool out there to work and collaborate on software. Their pull-requests feature makes it easy to keep things organized and discuss changes to the codebase. I’m also using GitHub’s issue tracker to manage the bug fixing process, as well as improvements to existing features.&lt;/p&gt;
&lt;h3 id="toc_9"&gt;&lt;a href="http://codeship.com"&gt;Codeship&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’m a huge fan of continuos integration and continuos deployment. I set up Codeship to automatically run Stage’s test suite every time I push to its GitHub repository. As soon as the tests pass, the master branch is also deployed to Shelly Cloud. This workflow has several benefits for me: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Because everything is automated, I don’t accidentally forget a step during deployment.&lt;/li&gt;
&lt;li&gt;It forces me to write proper tests. Everything is deployed automatically, so I really want to make sure that I’ve got everything tested and things don’t break in production.&lt;/li&gt;
&lt;li&gt;I’ll make sure I don’t hack stuff together in the repositories master branch. Every feature get’s developed in a separate branch and only merged when it is finished. This way, the master branch is always clean and ready to deploy.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="toc_10"&gt;Monitoring and Analytics&lt;/h2&gt;
&lt;p&gt;To keep track of what’s happening, I rely on several monitoring and analytics tools. &lt;/p&gt;
&lt;h3 id="toc_11"&gt;&lt;a href="http://appsignal.com"&gt;AppSignal&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;To monitor errors and performance of the application, I rely on AppSignal. Their service is easy to set up and gives great insights into performance issues. The exception notification feature sends all the data required to fix a problem without spamming my inbox. In contrast to other solutions out there, they charge based on the requests the application handles instead of the number of hosts it runs on. &lt;/p&gt;
&lt;h3 id="toc_12"&gt;&lt;a href="http://pingdom.com"&gt;Pingdom&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Sometimes a problem is not directly related to the application itself, but to the infrastructure that runs it. In order to get notified about downtimes &lt;em&gt;before&lt;/em&gt; my customers notice them, I’m using Pingdom. Every minute, their service checks several websites running on Stage. As soon as one of them is not reachable, it sends an email and also tries to figure out the root cause of the problem automatically. &lt;/p&gt;
&lt;h3 id="toc_13"&gt;&lt;a href="http://hookfeed.com"&gt;Hookfeed&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Hookfeed automatically sends notifications for all Stripe related events. Everytime a credit card is charged, a customer subscribes to a plan, or a charge failed, I instantly get notified about it. As a plus, it also provides simple metrics such as Customer Life Time Value or Monthly Recurring Revenue. &lt;/p&gt;
&lt;h3 id="toc_14"&gt;&lt;a href="http://google.com/analytics"&gt;Google Analytics&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Google Analytics is probably the go-to solution for statistics about website visitors. I use it mostly to keep track of traffic sources and to monitor email signup conversions on the current landing page of Stage. &lt;/p&gt;
&lt;h2 id="toc_15"&gt;Misc&lt;/h2&gt;
&lt;h3 id="toc_16"&gt;&lt;a href="http://flowdock.com"&gt;Flowdock&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Almost all of the tools I use send notifications to a Flowdock chat. In a way, it’s the aggregating inbox for everything that happens with Stage. From time to time, I also use it to discuss Stage-related features and problems with friends.&lt;/p&gt;
&lt;h2 id="toc_17"&gt;Open Source&lt;/h2&gt;
&lt;p&gt;To build Stage itself, I heavily rely on open source software. There would be no way for me to create a product like Stage without the basis hundreds of developers created and shared with the world. The application uses so many open source tools, that it’s impossible to count them all. Therefore I singled out just two of them. &lt;/p&gt;
&lt;h3 id="toc_18"&gt;&lt;a href="http://rubyonrails.org"&gt;Ruby on Rails&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The backend of Stage is written in &lt;a href="http://ruby-lang.org"&gt;Ruby&lt;/a&gt; using the Ruby on Rails framework. This simply was the natural choice for me. It’s the toolset I’m most familiar with and it’s therefore the most efficient to work with.&lt;/p&gt;
&lt;h3 id="toc_19"&gt;&lt;a href="http://emberjs.com"&gt;Ember.js&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I chose Ember.js as a framework to create the user interface of the content management part of Stage. The framework is targeted to ambitious web applications, and that’s exactly what the user interface of Stage is. Also, the core ideas and values behind Ember.js resonate with me as a developer. &lt;/p&gt;
&lt;h2 id="toc_20"&gt;Costs&lt;/h2&gt;
&lt;p&gt;Great tools come at a price, however not all of the products listed above are paid. Some of them offer free plans that are a good fit when you’re just starting out. Just check their pricing pages carefully, it might have a small link to a free plan somewhere.&lt;/p&gt;
&lt;table&gt;&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th align="right"&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Buffer&lt;/td&gt;
&lt;td align="right"&gt;$102/year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drip&lt;/td&gt;
&lt;td align="right"&gt;$49/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNSimple&lt;/td&gt;
&lt;td align="right"&gt;$110/year&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub&lt;/td&gt;
&lt;td align="right"&gt;$22/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stripe&lt;/td&gt;
&lt;td align="right"&gt;2.9% + $0.30&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shelly Cloud&lt;/td&gt;
&lt;td align="right"&gt;80€/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AppSignal&lt;/td&gt;
&lt;td align="right"&gt;39€/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pingdom&lt;/td&gt;
&lt;td align="right"&gt;12.95€/month&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Analytics&lt;/td&gt;
&lt;td align="right"&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trello&lt;/td&gt;
&lt;td align="right"&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Codeship&lt;/td&gt;
&lt;td align="right"&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hookfeed&lt;/td&gt;
&lt;td align="right"&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flowdock&lt;/td&gt;
&lt;td align="right"&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Of course, I work with several more tools every day, especially several desktop applications. However, the list above covers the most important SaaS tools involved in running my product.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This article is part of a &lt;a href="http://blog.linksspy.com/tools-founders-use-for-world-domination"&gt;series&lt;/a&gt; where fellow bootstrappers share the tools they use to build their products.&lt;/em&gt;&lt;/p&gt;
</content>
</entry>
<entry>
<title>Reading List 2014</title>
<link rel="alternate" href="/2015/01/reading-list-2014/"/>
<id>/2015/01/reading-list-2014/</id>
<published>2015-01-02T00:00:00+00:00</published>
<updated>2015-01-02T00:00:00+00:00</updated>
<author>
<name>Benedikt Deicke</name>
</author>
<summary type="html">&lt;p&gt;I’ve always been a fan of &lt;a href="http://www.paperplanes.de/2014/12/30/reading-list-2014.html"&gt;Mathias Meyer’s reading lists&lt;/a&gt;. They’re one of my go-to resources to discover interesting books to read. While I didn’t read nearly as many books as he did, I wanted to share the books read in 2014 in the same vein. So here it is: my reading list of 2014!&lt;/p&gt;
&lt;h2 id="toc_0"&gt;&lt;a href="http://www.amazon.de/gp/product/0670919519/ref=as_li_tl?ie=UTF8&amp;amp;camp=1638&amp;amp;creative=6742&amp;amp;creativeASIN=0670919519&amp;amp;linkCode=as2&amp;amp;tag=beneddeick-21&amp;amp;linkId=YJ5MC7EYAWZJ7ZZB"&gt;The Personal MBA&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Josh Kaufman’s book gives an birds-eye view about all business related topics. He covers value creation...&lt;/p&gt;</summary>
<content type="html">&lt;p&gt;I’ve always been a fan of &lt;a href="http://www.paperplanes.de/2014/12/30/reading-list-2014.html"&gt;Mathias Meyer’s reading lists&lt;/a&gt;. They’re one of my go-to resources to discover interesting books to read. While I didn’t read nearly as many books as he did, I wanted to share the books read in 2014 in the same vein. So here it is: my reading list of 2014!&lt;/p&gt;
&lt;h2 id="toc_0"&gt;&lt;a href="http://www.amazon.de/gp/product/0670919519/ref=as_li_tl?ie=UTF8&amp;amp;camp=1638&amp;amp;creative=6742&amp;amp;creativeASIN=0670919519&amp;amp;linkCode=as2&amp;amp;tag=beneddeick-21&amp;amp;linkId=YJ5MC7EYAWZJ7ZZB"&gt;The Personal MBA&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Josh Kaufman’s book gives an birds-eye view about all business related topics. He covers value creation, marketing, sales, value delivery, and finance. On top of that he also talks about the human mind, how to work with yourself and others, and how to understand/analyze and improve systems.&lt;/p&gt;
&lt;p&gt;The book is split up into short sections about each aspect of the general topics. This makes it easy to quickly pick up the book and read a few sections, even when you only have a few minutes of time. &lt;/p&gt;
&lt;p&gt;The Personal MBA is a great book if you want to get up to speed with the general ideas on running a business. Additionally it includes a huge list of books with more in-depth information. &lt;/p&gt;
&lt;h2 id="toc_1"&gt;&lt;a href="http://www.amazon.de/gp/product/B00LA95B68/ref=as_li_tl?ie=UTF8&amp;amp;camp=1638&amp;amp;creative=6742&amp;amp;creativeASIN=B00LA95B68&amp;amp;linkCode=as2&amp;amp;tag=beneddeick-21&amp;amp;linkId=RSSHJZTCVFGZJIJV"&gt;Startup Growth Engines&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Judging by the title of the book, this sounded like an interesting read. Unfortunately it was not. The book is merely a collection of blog posts featuring successful startups. The observations about each startup are shallow and I didn’t get any actionable advise from it. &lt;/p&gt;
&lt;h2 id="toc_2"&gt;&lt;a href="http://doubleyourfreelancing.com/rate/"&gt;Double Your Freelancing Rate&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve already read the first edition of Double Your Freelancing Rate in 2013. This year, Brennan Dunn released a new edition of the book in which he rewrote several parts of it. The book is packed with actionable advise on how to improve ones success as a freelancer. It helped me outline a process that allows me to position my services as a valuable investment towards my client’s goals. &lt;/p&gt;
&lt;p&gt;I highly recommend this book to everyone who&amp;#39;s freelancing. It comes with a great collection of useful worksheets and templates so you can start working on your business right away. &lt;/p&gt;
&lt;h2 id="toc_3"&gt;&lt;a href="http://uibreakfast.com/mastering-app-presentation/"&gt;Mastering App Presentation&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this book, Jane Portman talks about how to properly present your application. Whether it is presenting the work you did for a client or designing the marketing website for one of your applications, the book has you covered. While the examples mostly talk about mobile applications, a lot of it is easily applicable to web and desktop applications as well. &lt;/p&gt;
&lt;h2 id="toc_4"&gt;&lt;a href="http://www.amazon.de/gp/product/1439199191/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1638&amp;amp;creative=6742&amp;amp;creativeASIN=1439199191&amp;amp;linkCode=as2&amp;amp;tag=beneddeick-21&amp;amp;linkId=O5V6QYZZZWHNU4R3"&gt;How to Win Friends and Influence People&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When I first came across this book, I was a bit put off by its title. However I kept seeing this classic (first published in 1936) recommended by a lot of people. Eventually my friend Christoph sent it to me as a gift. &lt;/p&gt;
&lt;p&gt;After reading it, I realized the book is not about manipulating other people, but yourself. Dale Carnegie teaches a couple of valuable lessons that really changed how I perceive and interact with other people. In that regard, it’s the most valuable book I read this year.&lt;/p&gt;
&lt;h2 id="toc_5"&gt;&lt;a href="http://www.amazon.de/gp/product/B00GRBIFH6/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1638&amp;amp;creative=6742&amp;amp;creativeASIN=B00GRBIFH6&amp;amp;linkCode=as2&amp;amp;tag=beneddeick-21&amp;amp;linkId=ZK36J37XX5ELAKIF"&gt;Everything I Know&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In “Everything I Know”, Paul Jarvis shares his experiences and lessons learned as a professional in the creative industry. It was a quick and interesting read, but I didn’t get much out of it. &lt;/p&gt;
&lt;h2 id="toc_6"&gt;&lt;a href="http://www.amazon.de/gp/product/B0031RDVVY/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1638&amp;amp;creative=6742&amp;amp;creativeASIN=B0031RDVVY&amp;amp;linkCode=as2&amp;amp;tag=beneddeick-21&amp;amp;linkId=UODJUS2AVZEBAPQY"&gt;The Knack&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In “The Knack”, Norm Brodsky and Bo Burlingham write about lessons learned while running a business. Each lesson is backed by a real-life story. This makes it easy to understand the idea behind it, as well as to remember them later on. &lt;/p&gt;
&lt;p&gt;The book is filled with experience you don’t yet have when you’re just starting out. Instead of learning everything the hard way, I highly recommend reading this book. &lt;/p&gt;
&lt;h2 id="toc_7"&gt;&lt;a href="http://www.amazon.de/gp/product/B00AZRBLHO/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1638&amp;amp;creative=6742&amp;amp;creativeASIN=B00AZRBLHO&amp;amp;linkCode=as2&amp;amp;tag=beneddeick-21&amp;amp;linkId=5FG2GCVLKK5FJM64"&gt;The Phoenix Project&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Phoenix Project is a primer on DevOps and Lean Manufacturing masked as a fiction book. It tells the story of Bill, an IT director who suddenly finds himself becoming the vice president of IT operations. The company he’s working at, has huge problems releasing new software for their internal processes. If you’re working in IT, you’ll most likely recognize some of the problems they are facing. In the course of the story, Bill learns how to fix these problems by applying principles of lean manufacturing. &lt;/p&gt;
&lt;p&gt;It is a fun read, even if you&amp;#39;re already familiar with the ideas behind DevOps and Lean. &lt;/p&gt;
&lt;h2 id="toc_8"&gt;&lt;a href="https://www.masteringmodernpayments.com"&gt;Mastering Modern Payments&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To my own surprise this is the only programming related book I’ve read this year. In August, I implemented payments into my product &lt;a href="http://stagecms.com"&gt;Stage&lt;/a&gt; and Pete Keen’s book was a helpful guide. His book goes beyond the resources Stripe provides on their website and covers everything from one-time payments to subscriptions and marketplaces. &lt;/p&gt;
&lt;h2 id="toc_9"&gt;&lt;a href="http://www.amazon.de/gp/product/0340920750/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1638&amp;amp;creative=6742&amp;amp;creativeASIN=0340920750&amp;amp;linkCode=as2&amp;amp;tag=beneddeick-21&amp;amp;linkId=5UL3ZSNT7ZIKHDW2"&gt;The Swarm&lt;/a&gt; &amp;amp; &lt;a href="http://www.amazon.de/gp/product/3442380294/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;amp;camp=1638&amp;amp;creative=6742&amp;amp;creativeASIN=3442380294&amp;amp;linkCode=as2&amp;amp;tag=beneddeick-21&amp;amp;linkId=H3RQT4WQAXISHAD5"&gt;Black Out&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In addition to the non-fiction books, I read two more fiction books this year: The Swarm and Black Out. I enjoyed both a lot.&lt;/p&gt;
</content>
</entry>
<entry>
<title>ActionWidgets: Rails helpers on steroids</title>
<link rel="alternate" href="/2013/05/action-widgets-rails-helpers-on-steroids/"/>
<id>/2013/05/action-widgets-rails-helpers-on-steroids/</id>
<published>2013-05-21T00:00:00+00:00</published>
<updated>2013-05-21T00:00:00+00:00</updated>
<author>
<name>Benedikt Deicke</name>
</author>
<summary type="html">&lt;p&gt;More than once writing view code for a Rails application ends up with a messy template file. The separation of structure and style doesn&amp;#39;t always work out as intended. Frameworks like Bootstrap force you to use nested structures and lots of class attributes. The view code ends up with a lot of duplication and is hard to read as a result. You&amp;#39;ll get away with that for a while, constantly fearing the next redesign&amp;hellip;&lt;/p&gt;
&lt;p&gt;</summary>
<content type="html">&lt;p&gt;More than once writing view code for a Rails application ends up with a messy template file. The separation of structure and style doesn&amp;#39;t always work out as intended. Frameworks like Bootstrap force you to use nested structures and lots of class attributes. The view code ends up with a lot of duplication and is hard to read as a result. You&amp;#39;ll get away with that for a while, constantly fearing the next redesign&amp;hellip;&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;To tackle this, you probably start writing helper methods. They help to remove logic out of the template and hide overly complex structures behind a nice interface. Unfortunately some view components are more complex to implement and require more than just a single method. This is even more true when you need to configure the view components in some way or another.&lt;/p&gt;
&lt;h2 id="toc_0"&gt;Introducing ActionWidgets&lt;/h2&gt;
&lt;p&gt;In the following, I&amp;#39;ll present you a different approach to this issue: &lt;a href="https://github.com/t6d/action_widget"&gt;ActionWidget&lt;/a&gt;. The gem by &lt;a href="https://twitter.com/t6d"&gt;Konstantin Tennhard&lt;/a&gt; takes helper methods to the next level. The basic idea is simple, instead of writing a complex helper method, you&amp;#39;ll write a class inheriting from &lt;code&gt;ActionWidget::Base&lt;/code&gt; and put it into the &lt;code&gt;app/widgets&lt;/code&gt; directory. The only method you have to implement is the &lt;code&gt;render&lt;/code&gt; method. The ActionWidget gem, will provide you with a simple helper method to use your widget automatically. Let me show you a simple example.&lt;/p&gt;
&lt;h3 id="toc_1"&gt;A simple button widget&lt;/h3&gt;
&lt;p&gt;To illustrate the basic usage of ActionWidget, let&amp;#39;s write a widget to represent a button. The button will have a title, a target, a type (default, primary, or danger) and a size (small, default, or large). For convenience, the gem provides a generator to get you started:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::shell-unix-generic
rails generate action_widget:widget button
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The generator creates a file called &lt;code&gt;app/widgets/button_widget.rb&lt;/code&gt; for you. This is where the implementation of the button widget lives. An example implementation might look something like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
# app/widgets/button_widget.rb
class ButtonWidget &amp;lt; ActionWidget::Base
property :title,
:converts =&amp;gt; :to_s,
:required =&amp;gt; true
property :target,
:converts =&amp;gt; :to_s,
:required =&amp;gt; true
property :type,
:converts =&amp;gt; :to_sym,
:accepts =&amp;gt; [:default, :primary, :danger],
:default =&amp;gt; :default
property :size,
:converts =&amp;gt; :to_sym,
:accepts =&amp;gt; [:small, :default, :large],
:default =&amp;gt; :default
def render
content_tag(:a, title, :href =&amp;gt; target, :class =&amp;gt; css_classes)
end
protected
def css_classes
css_classes = [&amp;#39;btn&amp;#39;]
css_classes &amp;lt;&amp;lt; &amp;quot;btn-#{size}&amp;quot; unless size == :default
css_classes &amp;lt;&amp;lt; &amp;quot;btn-#{type}&amp;quot; unless type == :default
css_classes
end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The ActionWidget gem depends on &lt;a href="https://github.com/t6d/smart_properties"&gt;Smart Properties&lt;/a&gt; to provide the &lt;code&gt;property&lt;/code&gt; macro method. This allows you to add simple configuration values to your widget. It&amp;#39;ll also do conversion and input validation for you. With the implementation above, you&amp;#39;ll be forced to always provide a title and a target attribute for every button. Otherwise the widget will complain with an exception. &lt;/p&gt;
&lt;p&gt;Using the button widget in your view is as simple as a call to the (automatically generated) &lt;code&gt;button_widget&lt;/code&gt; method:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
button_widget :title =&amp;gt; &amp;#39;Sign Up&amp;#39;,
:target =&amp;gt; signup_path,
:type =&amp;gt; :primary,
:size =&amp;gt; :large
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that you have a basic understanding of the idea behind ActionWidgets, let&amp;#39;s dive into a more complex example. &lt;/p&gt;
&lt;h3 id="toc_2"&gt;A more advanced example&lt;/h3&gt;
&lt;p&gt;Tabs are a pretty common user interface element and their implementation is pretty straight forward. Let&amp;#39;s look at the implementation of tabs, on a site using Twitter Bootstrap: &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::html
&amp;lt;ul class=&amp;quot;nav nav-tabs&amp;quot; id=&amp;quot;myTab&amp;quot;&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#first&amp;quot;&amp;gt;First&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li class=&amp;quot;active&amp;quot;&amp;gt;&amp;lt;a href=&amp;quot;#second&amp;quot;&amp;gt;Second&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;li&amp;gt;&amp;lt;a href=&amp;quot;#third&amp;quot;&amp;gt;Third&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&amp;lt;/ul&amp;gt;
&amp;lt;div class=&amp;quot;tab-content&amp;quot;&amp;gt;
&amp;lt;div class=&amp;quot;tab-pane&amp;quot; id=&amp;quot;first&amp;quot;&amp;gt;...&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;tab-pane active&amp;quot; id=&amp;quot;second&amp;quot;&amp;gt;...&amp;lt;/div&amp;gt;
&amp;lt;div class=&amp;quot;tab-pane&amp;quot; id=&amp;quot;third&amp;quot;&amp;gt;...&amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To implement the tabs, you need a list with links for each tab and a collection of tab panes. For every &lt;code&gt;href&lt;/code&gt; attribute of each link, there must be a corresponding tab pane with its &lt;code&gt;id&lt;/code&gt; attribute set accordingly. Additionally, the &lt;code&gt;active&lt;/code&gt; class has to match, or your users will be a bit confused when clicking through the tabs. Of course when adding the tabs to one of your pages, you&amp;#39;ll have to remember all the details of the markup that&amp;#39;s required. There has to be both the &lt;code&gt;nav&lt;/code&gt; and &lt;code&gt;nav-tabs&lt;/code&gt; class in the &lt;code&gt;ul&lt;/code&gt; tag, and don&amp;#39;t forget the wrapping &lt;code&gt;div&lt;/code&gt; tag with class &lt;code&gt;tab-content&lt;/code&gt; around all the &lt;code&gt;tab-pane&lt;/code&gt; elements. That&amp;#39;s a lot to remember, and a lot to mess up. If you don&amp;#39;t screw it up now, then probably later when you try to change something quickly. To make your life a little easier, I&amp;#39;ll show you how to use ActionWidgets to hide the markup behind this expressive interface.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
= tabs_widget do |t|
= t.tab &amp;#39;First&amp;#39; do
Content of the first tab
= t.tab &amp;#39;Second&amp;#39;, :active =&amp;gt; true do
Content of the second tab
= t.tab &amp;#39;Third&amp;#39; do
Content of the third tab
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I&amp;#39;m using Haml in this example, but that&amp;#39;s not a requirement. ActionWidgets work with all of your templates, as long as they support helper methods.&lt;/p&gt;
&lt;h4 id="toc_3"&gt;Building the TabsWidget&lt;/h4&gt;
&lt;p&gt;Let&amp;#39;s start by generating the &lt;code&gt;TabsWidget&lt;/code&gt; class. Again, it&amp;#39;s as simple as creating a file called &lt;code&gt;app/widgets/tabs_widget.rb&lt;/code&gt; or running the generator.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
class TabsWidget &amp;lt; ActionWidget::Base
def render(&amp;amp;block)
navigation = content_tag(:ul, :class =&amp;gt; [&amp;#39;nav&amp;#39;, &amp;#39;nav-tabs&amp;#39;]) do
capture(self, &amp;amp;block)
end
contents = content_tag(:div, :class =&amp;gt; [&amp;#39;tab-content&amp;#39;]) do
tabs.each do |tab|
concat(tab.render)
end
end
navigation + contents
end
def tab(*args)
# We&amp;#39;ll implement this soon...
end
private
def tabs
@tabs ||= []
end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every ActionWidget has to implement a &lt;code&gt;render&lt;/code&gt; method. If the generated helper method gets passed a block, it will be handed to this method as well. Every ActionWidget has delegators to all the methods in your view. We use this to our advantage and use &lt;code&gt;capture&lt;/code&gt; to get the contents of the block. We also pass &lt;code&gt;self&lt;/code&gt;, which makes it possible to call the widget&amp;#39;s &lt;code&gt;tab&lt;/code&gt; method from within the block. The captured contents get wrapped in the necessary &lt;code&gt;ul&lt;/code&gt; tag.&lt;/p&gt;
&lt;p&gt;Next we create a &lt;code&gt;div&lt;/code&gt; tag to wrap the tab contents. We iterate over the collection of tabs and render each one. By using &lt;code&gt;concat&lt;/code&gt; we make sure, that the rendered tab-pane actually gets appended to the view.&lt;/p&gt;
&lt;p&gt;With this code in place, we don&amp;#39;t get an error when trying to view the page we&amp;#39;re embedding the tabs widget in, but we don&amp;#39;t see the tabs either.&lt;/p&gt;
&lt;h4 id="toc_4"&gt;Adding a nested TabWidget&lt;/h4&gt;
&lt;p&gt;The general idea behind ActionWidget is to use objects instead of a set of methods to generate markup code. In this spirit, let&amp;#39;s add another ActionWidget to represent a single tab. It&amp;#39;s pretty useless without the wrapping tabs widget, so let&amp;#39;s add it as a nested class:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
class TabsWidget &amp;lt; ActionWidget::Base
class Tab &amp;lt; ActionWidget::Base
property :name,
:required =&amp;gt; true,
:converts =&amp;gt; :to_s
property :active,
:accepts =&amp;gt; [true, false],
:default =&amp;gt; false
property :target,
:converts =&amp;gt; :to_s
property :content,
:required =&amp;gt; true
def render
content_tag(:div, :id =&amp;gt; target, :class =&amp;gt; [&amp;#39;tab-pane&amp;#39;] + css_classes, &amp;amp;content)
end
def render_navigation
content_tag(:li, :class =&amp;gt; [&amp;#39;tab&amp;#39;] + css_classes) do
link_to(name, &amp;#39;#&amp;#39; + target)
end
end
def target
super || name.parameterize
end
private
def css_classes
css_classes = []
css_classes &amp;lt;&amp;lt; &amp;#39;active&amp;#39; if active
css_classes
end
end
# ...
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, the class is quite simple. Apart from defining a set of properties, it has two render methods. One to render the tab pane, and another one to render the navigation link. They both make use of the &lt;code&gt;target&lt;/code&gt; method so the link&amp;#39;s &lt;code&gt;href&lt;/code&gt; attribute matches the tab pane&amp;#39;s &lt;code&gt;id&lt;/code&gt; attribute. The private &lt;code&gt;css_classes&lt;/code&gt; method makes sure, both elements get the &lt;code&gt;active&lt;/code&gt; class when the tab is active.&lt;/p&gt;
&lt;p&gt;Now everything we need to do is to tie the &lt;code&gt;TabsWidget&lt;/code&gt; and the &lt;code&gt;TabsWidget::Tab&lt;/code&gt; together, usin the &lt;code&gt;TabsWidget#tab&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
def tab(name, options = {}, &amp;amp;block)
options.merge!({
:name =&amp;gt; name,
:content =&amp;gt; block
})
tab = Tab.new(view, options)
tabs &amp;lt;&amp;lt; tab
tab.render_navigation
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method creates a new instance of the &lt;code&gt;TabsWidget::Tab&lt;/code&gt; widget, while passing both the view context and a options hash to it. Afterwards it adds the tab to the collection and renders the navigation link.&lt;/p&gt;
&lt;p&gt;Now we have everything in place to render tab widgets with a few lines of nice and expressive code. Should you ever need to change the implementation of all tab widgets on your website, you now have a single place to do this.&lt;/p&gt;
&lt;h2 id="toc_5"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;ActionWidget allows you to separate the concept of a view component from the actual implementation. In contrast to the presenter pattern, the widgets don&amp;#39;t necessarily need to wrap a model object. As they are implemented in classes, it&amp;#39;s easy to extend them. Additionally the possibility to use private methods internally makes their code easier to maintain than standard helper methods.&lt;/p&gt;
&lt;p&gt;Hopefully this short introduction made you curious about the ActionWidget approach and gives you some pointers to implement some (or all?) of your user interface components using this gem. Unfortunately there&amp;#39;s no documentation for it yet, but as the &lt;a href="https://github.com/t6d/action_widget"&gt;gem&amp;#39;s code&lt;/a&gt; is pretty simple and I covered the basic idea in this article, you should be able to get started easily.&lt;/p&gt;
</content>
</entry>
<entry>
<title>I'm going freelance</title>
<link rel="alternate" href="/2013/02/i-am-going-freelance/"/>
<id>/2013/02/i-am-going-freelance/</id>
<published>2013-02-25T00:00:00+00:00</published>
<updated>2013-02-25T00:00:00+00:00</updated>
<author>
<name>Benedikt Deicke</name>
</author>
<summary type="html">&lt;p&gt;More than two years ago, I joined &lt;a href="https://flinc.org"&gt;flinc&lt;/a&gt; as a Rails developer. During this time, I helped building, launching and maintaining the realtime ride sharing network. From april on, I'm going to work as a freelance software engineer. It's not totally new for me, but it's the first time it'll be my main source of income.&lt;/p&gt;
&lt;p&gt;Besides doing freelance software development work, I'm planning to focus more on my...&lt;/p&gt;</summary>
<content type="html">&lt;p&gt;More than two years ago, I joined &lt;a href="https://flinc.org"&gt;flinc&lt;/a&gt; as a Rails developer. During this time, I helped building, launching and maintaining the realtime ride sharing network. From april on, I&amp;#39;m going to work as a freelance software engineer. It&amp;#39;s not totally new for me, but it&amp;#39;s the first time it&amp;#39;ll be my main source of income.&lt;/p&gt;
&lt;p&gt;Besides doing freelance software development work, I&amp;#39;m planning to focus more on my own projects. One of them is &amp;ndash; of course &amp;ndash; this blog, which has been neglected for far too long. Additionally I&amp;#39;ve got some other projects in mind, that I&amp;#39;m going to announce here eventually. &lt;/p&gt;
&lt;p&gt;During the upcoming weeks, I&amp;#39;ll update my website to provide some more information about my services and past work. In the meantime, please don&amp;#39;t hesitate to &lt;a href="mailto:benedikt@benediktdeicke.com"&gt;contact me&lt;/a&gt; in case you want me to help you develop your product.&lt;/p&gt;
</content>
</entry>
<entry>
<title>What you did not know about Capistrano, yet</title>
<link rel="alternate" href="/2013/02/what-you-did-not-know-about-capistrano-yet/"/>
<id>/2013/02/what-you-did-not-know-about-capistrano-yet/</id>
<published>2013-02-14T00:00:00+00:00</published>
<updated>2013-02-14T00:00:00+00:00</updated>
<author>
<name>Benedikt Deicke</name>
</author>
<summary type="html">&lt;p&gt;Like any other build process, deploying Rails applications should be a quick and easy task. You should be able to deploy everything to your server with just one command. There are several tools available for this task. One of them is &lt;a href="https://github.com/capistrano/capistrano"&gt;Capistrano&lt;/a&gt;, which is probably the oldest and by far the most popular deployment tool in the Rails community. Unfortunately the &lt;a href="https://github.com/capistrano/capistrano/wiki"&gt;documentation&lt;/a&gt; is still not great and...&lt;/p&gt;</summary>
<content type="html">&lt;p&gt;Like any other build process, deploying Rails applications should be a quick and easy task. You should be able to deploy everything to your server with just one command. There are several tools available for this task. One of them is &lt;a href="https://github.com/capistrano/capistrano"&gt;Capistrano&lt;/a&gt;, which is probably the oldest and by far the most popular deployment tool in the Rails community. Unfortunately the &lt;a href="https://github.com/capistrano/capistrano/wiki"&gt;documentation&lt;/a&gt; is still not great and a lot of very handy features are unknown to many. Only digging through the API documentation and the &lt;a href="http://rubydoc.info/github/capistrano/capistrano/master/frames"&gt;source code&lt;/a&gt; reveals them.&lt;/p&gt;
&lt;h2 id="toc_0"&gt;Variables&lt;/h2&gt;
&lt;p&gt;A lot of tasks for deploying Rails applications are the same for almost every application. They only differ by minor aspects such as the path to deploy to, or the branch to checkout the code from. Capistrano allows setting these aspects using variables. Setting a variable is as simple as calling &lt;code&gt;set :name, &amp;#39;value&amp;#39;&lt;/code&gt;. Accessing a previously defined variable is even easier by just using it as a ruby method.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
set :name, &amp;#39;value&amp;#39;
puts name # Prints &amp;#39;value&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However there are some aspects of Capistrano variables that make them really useful. &lt;/p&gt;
&lt;h3 id="toc_1"&gt;Accessing variables that might not exist&lt;/h3&gt;
&lt;p&gt;In some cases you don&amp;#39;t want to set every single variable to configure certain aspects of a task. But this comes with a problem. Accessing a variable that has not been defined before, will make Capistrano yell at you and crash with a &lt;code&gt;NameError&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;There are two ways to handle this. One of them is to define all variables with sane defaults and overwrite them as needed afterwards. The other one, and by far my preferred one, is to use Capistrano&amp;#39;s &lt;code&gt;fetch&lt;/code&gt; method. It allows you to access a variable and additionally provide a default that will be returned when the variable is not defined, yet. This allows you to write flexible tasks without having to define every single variable upfront. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
fetch(:some_setting, false)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="toc_2"&gt;Setting variables via the command line&lt;/h3&gt;
&lt;p&gt;Another very useful feature is the possibility to set variables via the command line. Appending a &lt;code&gt;-S variable_name=value&lt;/code&gt; option to the command line will set the given variable to the given value. This comes with a minor caveat, though. Any call of &lt;code&gt;set&lt;/code&gt; with the same variable name will overwrite the value set by the command line option. Luckily Capistrano&amp;#39;s &lt;code&gt;exists?&lt;/code&gt; method allows you to check for the existence of a variable. This allows you to only set a variable if it doesn&amp;#39;t exist yet. You might change your &lt;code&gt;set :branch&lt;/code&gt; call to something like this: &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
set(:branch, &amp;#39;staging&amp;#39;) unless exists?(:branch)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows you to deploy any branch to your staging servers without fiddling with the configuration files, just by appending &lt;code&gt;-S branch=other-branch&lt;/code&gt; to your &lt;code&gt;cap deploy&lt;/code&gt; call:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::shell-unix-generic
cap deploy -S branch=awesome-new-feature
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Combining the ability to set variables via the command line with the previously explained &lt;code&gt;fetch&lt;/code&gt; method is also useful to toggle certain aspects of your deployment.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
namespace :translations do
task :update do
if fetch(:with_translations, true)
# Download current translations from your translation service provider
end
end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="toc_3"&gt;Reading passwords from STDIN&lt;/h3&gt;
&lt;p&gt;Variables are great to store your configuration options. There&amp;#39;s just one exception: Passwords. You probably don&amp;#39;t want to have your production database password checked into your repository.
This is where Capistrano&amp;#39;s command line interface tools come in handy. It allows you, among several other options, to read passwords from STDIN. It even makes sure your terminal won&amp;#39;t print it while you&amp;#39;re typing.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
Capistrano::CLI.password_prompt(&amp;#39;Enter database password: &amp;#39;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="toc_4"&gt;Task descriptions&lt;/h2&gt;
&lt;p&gt;As you probably know, it&amp;#39;s possible to list all available tasks using &lt;code&gt;cap -T&lt;/code&gt;. This will print a list of all available commands. Every task with a description will show up in this list. Adding a description is easy. Just prepend the task definition with a &lt;code&gt;desc&lt;/code&gt; call, passing it a string.&lt;/p&gt;
&lt;h3 id="toc_5"&gt;Adding extended description&lt;/h3&gt;
&lt;p&gt;Each defined task may also have an extended description. To show it, simply execute &lt;code&gt;cap -e task name&lt;/code&gt;. Adding an extended description works just like adding the short description explained above. Just like &lt;code&gt;git commit&lt;/code&gt;, Capistrano will use the first paragraph as short description and every following paragraph as extended description. Ruby&amp;#39;s heredoc literal is handy when defining extended descriptions:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
desc &amp;lt;&amp;lt;-EOS
This is a short description that is visible in the `cap -T` list
This the extended description that is visible when executing `cap -e task name`
EOS
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="toc_6"&gt;Hiding internal tasks&lt;/h3&gt;
&lt;p&gt;Some tasks you&amp;#39;ll define for your deployment process are not intended to be executed manually and therefore shouldn&amp;#39;t show up when using &lt;code&gt;cap -T&lt;/code&gt;. The simplest way to prevent this, is to not give them any description. However, in some cases, a description would be useful for later reference and documentation. By prepending the description with &lt;code&gt;[internal]&lt;/code&gt; you can hide the task from the &lt;code&gt;cap -T&lt;/code&gt; list. It will then only be visible when executing &lt;code&gt;cap -vT&lt;/code&gt;, which will list absolutely all defined tasks.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
desc &amp;#39;[internal] This task does something useful but you should not invoke it manually&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="toc_7"&gt;Executing commands&lt;/h2&gt;
&lt;p&gt;Running commands on the remote servers is easy. Capistrano&amp;#39;s &lt;code&gt;run&lt;/code&gt; method will execute the given command on every server the current task applies to. There are two more methods related to command execution that are very useful, though. &lt;/p&gt;
&lt;h3 id="toc_8"&gt;Running local commands&lt;/h3&gt;
&lt;p&gt;One of them is &lt;code&gt;run_locally&lt;/code&gt;. It behaves just like &lt;code&gt;run&lt;/code&gt;, except it will run the command on your machine. While it is of course possible to use Ruby&amp;#39;s various methods for command execution, &lt;code&gt;run_locally&lt;/code&gt; will ensure both the command and it&amp;#39;s output will show up cleanly aligned with all other output of the Capistrano run. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
run_locally &amp;#39;bundle exec rake deploy:prepare&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="toc_9"&gt;Streaming output from remote servers&lt;/h3&gt;
&lt;p&gt;The other very useful method is &lt;code&gt;stream&lt;/code&gt;. It will execute the given command on every server the task applies to, but unlike the &lt;code&gt;run&lt;/code&gt; method it will stream the command&amp;#39;s output to your console. This allows you, for example, to peek into your log files in realtime. Combined with &lt;code&gt;grep&lt;/code&gt; and Rails&amp;#39; &lt;code&gt;ActiveSupport::TaggedLogging&lt;/code&gt; this helps a lot when debugging an issue that only shows up on your servers.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
stream &amp;quot;tail -f #{current_path}/log/#{rails_env}.log&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="toc_10"&gt;Handling failing commands&lt;/h3&gt;
&lt;p&gt;When executing a remote command that fails, Capistrano will abort the deployment process. This makes sense in most cases, but wouldn&amp;#39;t it be nice to have a little more control over the behavior? Let&amp;#39;s say you want to check for the existence of a file on the remote server. One easy way to do this is the &lt;code&gt;test&lt;/code&gt; command. It&amp;#39;s exit code will be &lt;code&gt;0&lt;/code&gt; for success or &lt;code&gt;1&lt;/code&gt; for failure. Unfortunately this means one of the cases will cause the entire deployment process to fail. To prevent this, all you have to do is to rescue &lt;code&gt;Capistrano::CommandError&lt;/code&gt;. A practical example for this technique is this task, that checks if the server is in maintenance mode before running migrations. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
namespace :deploy do
namespace :web do
desc &amp;#39;[internal] Ensures that web is currently disabled&amp;#39;
task :ensure do
begin
run &amp;quot;test -f #{shared_path}/system/maintenance.html&amp;quot;
rescue Capistrano::CommandError
unless fetch(:force, false)
logger.important &amp;#39;You should disable the website using deploy:web:disable before doing this&amp;#39;
exit
end
end
end
end
end
before &amp;#39;deploy:migrations&amp;#39;, &amp;#39;deploy:web:ensure&amp;#39;
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="toc_11"&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This is &amp;ndash; of course &amp;ndash; only a small subset of the useful features Capistrano provides. I hope I showed you one or two you haven&amp;#39;t known before. I encourage you to read through Capistrano&amp;#39;s &lt;a href="https://github.com/capistrano/capistrano"&gt;source code&lt;/a&gt; and &lt;a href="http://rubydoc.info/github/capistrano/capistrano/master/frames"&gt;API documentation&lt;/a&gt; to find more of these hidden features. &lt;/p&gt;
</content>
</entry>
<entry>
<title>Cleaner specs with custom RSpec example groups</title>
<link rel="alternate" href="/2013/01/custom-rspec-example-groups/"/>
<id>/2013/01/custom-rspec-example-groups/</id>
<published>2013-01-09T00:00:00Z</published>
<updated>2013-01-09T00:00:00Z</updated>
<author>
<name>Benedikt Deicke</name>
</author>
<summary type="html">&lt;p&gt;Sometimes, when testing your code with RSpec, you'll notice similarities and duplication between your spec files. Most of these will involve setup that doesn't say much about the object under test. There's something that helps you to reduce this duplication: Custom example groups! RSpec itself (rspec-rails) &lt;a href="https://github.com/rspec/rspec-rails/tree/master/lib/rspec/rails/example"&gt;uses example groups&lt;/a&gt; for the different types of tests for models, controllers, helpers and...&lt;/p&gt;</summary>
<content type="html">&lt;p&gt;Sometimes, when testing your code with RSpec, you&amp;#39;ll notice similarities and duplication between your spec files. Most of these will involve setup that doesn&amp;#39;t say much about the object under test. There&amp;#39;s something that helps you to reduce this duplication: Custom example groups! RSpec itself (rspec-rails) &lt;a href="https://github.com/rspec/rspec-rails/tree/master/lib/rspec/rails/example"&gt;uses example groups&lt;/a&gt; for the different types of tests for models, controllers, helpers and views. In the following I&amp;#39;ll show you how to use them to reduce duplication and improve your tests.&lt;/p&gt;
&lt;h2 id="toc_0"&gt;What are example groups?&lt;/h2&gt;
&lt;p&gt;An example group is defined by using RSpec&amp;#39;s &lt;code&gt;describe&lt;/code&gt; method (or its alias &lt;code&gt;context&lt;/code&gt;). It&amp;#39;s a subclass of &lt;a href="http://rubydoc.info/gems/rspec-core/RSpec/Core/ExampleGroup"&gt;&lt;code&gt;RSpec::Core::ExampleGroup&lt;/code&gt;&lt;/a&gt; and provides all the macro methods you&amp;#39;re used to in your specs. Additionally it provides a description, a set of metadata, and optionally a described class. It also knows about the examples defined using &lt;code&gt;it&lt;/code&gt; and how to run each of them with all hooks and stuff.&lt;/p&gt;
&lt;h2 id="toc_1"&gt;Customizing example groups&lt;/h2&gt;
&lt;p&gt;Now that you know about example groups, let&amp;#39;s start customizing them. As we&amp;#39;ve learned above, example groups are simple classes and therefore can be extended with modules. This happens to be the way rspec-rails is working as well. To illustrate the process, let&amp;#39;s look at an example.&lt;/p&gt;
&lt;h3 id="toc_2"&gt;A serializer example group&lt;/h3&gt;
&lt;p&gt;In a recent project, I was working with &lt;a href="https://github.com/rails-api/active_model_serializers"&gt;&lt;code&gt;ActiveModel::Serializers&lt;/code&gt;&lt;/a&gt;. In case you don&amp;#39;t know it, you should definitely have a look at it. In my opinion, it&amp;#39;s a great way to serialize your models into JSON. For our example we want to create a custom example group for serializers so we can test them like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
require &amp;#39;spec_helper&amp;#39;
describe UserSerializer do
let(:attributes) { FactoryGirl.attributes_for(resource_name) }
it { should have_key(:name) }
it { should have_key(:email) }
it { should have_key(:created_at) }
it { should have_key(:updated_at) }
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to get there we create a new file called &lt;code&gt;spec/support/example_groups/serializer_example_group.rb&lt;/code&gt;. Then we define a new module called &lt;code&gt;SerializerExampleGroup&lt;/code&gt;. For convenience (and following the pattern of rspec-rails) we extend this module with &lt;code&gt;ActiveSupport::Concern&lt;/code&gt;. In addition we must tell RSpec to include this module into the example groups of all specs in the &lt;code&gt;spec/serializers&lt;/code&gt; folder and all specs explicitly setting &lt;code&gt;:type =&amp;gt; :serializer&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
module SerializerExampleGroup
extend ActiveSupport::Concern
RSpec.configure do |config|
config.include self,
:type =&amp;gt; :serializer,
:example_group =&amp;gt; { :file_path =&amp;gt; %r(spec/serializers) }
end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="toc_3"&gt;Adding custom behavior&lt;/h3&gt;
&lt;p&gt;That&amp;#39;s all we have to do to set up our custom example group. Next we add our desired custom behavior. We have to create a new instance of our serializer and pass a resource to serialize. The resource needs attributes and must implement a &lt;code&gt;read_attributes_for_serialization&lt;/code&gt; method. We can add all this by defining an &lt;code&gt;included&lt;/code&gt; block and use RSpec&amp;#39;s &lt;code&gt;let&lt;/code&gt; macro. In order to get the name of the resource that is serialized by the serializer under test we use RSpec&amp;#39;s &lt;code&gt;described_class&lt;/code&gt; method. It returns the class defined in the &lt;code&gt;describe UserSerializer&lt;/code&gt; statement. By converting its name to underscores, removing the &lt;code&gt;_serializer&lt;/code&gt; part and converting it into a symbol we get &lt;code&gt;:user&lt;/code&gt; as &lt;code&gt;resource_name&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
included do
let(:attributes) do
{}
end
let(:resource_name) do
described_class.name.underscore[0..-12].to_sym
end
let(:resource) do
double(resource_name, attributes).tap do |double|
double.stub(:read_attribute_for_serialization) { |name| attributes[name] }
end
end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we set the serialized hash as the subject for the example group. For convenience we also convert it into a &lt;code&gt;HashWithIndifferentAccess&lt;/code&gt; so we don&amp;#39;t have to think about using strings or symbols as keys.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
let(:serializer) { described_class.new(resource) }
subject { serializer.serializable_hash.with_indifferent_access }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="toc_4"&gt;Adding some metadata&lt;/h3&gt;
&lt;p&gt;Now we have the desired custom behavior. To wrap things up, we add some metadata to the example group. In the &lt;code&gt;included&lt;/code&gt; block we have access to the &lt;code&gt;metadata&lt;/code&gt; method and are able to set the &lt;code&gt;:type&lt;/code&gt; to &lt;code&gt;:serializer&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
metadata[:type] = :serializer
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="toc_5"&gt;The finished example group&lt;/h3&gt;
&lt;p&gt;All this results in a &lt;code&gt;spec/support/example_groups/serializer_example_group.rb&lt;/code&gt; file looking like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
module SerializerExampleGroup
extend ActiveSupport::Concern
included do
metadata[:type] = :serializer
let(:attributes) do
{}
end
let(:resource_name) do
described_class.name.underscore[0..-12].to_sym
end
let(:resource) do
double(resource_name, attributes).tap do |double|
double.stub(:read_attribute_for_serialization) { |name| attributes[name] }
end
end
let(:serializer) { described_class.new(resource) }
subject { serializer.serializable_hash.with_indifferent_access }
end
RSpec.configure do |config|
config.include self,
:type =&amp;gt; :serializer,
:example_group =&amp;gt; { :file_path =&amp;gt; %r(spec/serializers) }
end
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this custom example group in place, testing the serializers gets much easier. Of course, this is only one example of what you can do. Instead of adding a lot of setup you might only use this to add metadata (such as tags) to specs in a specific folder.&lt;/p&gt;
</content>
</entry>
<entry>
<title>I'm joining flinc as a Rails developer</title>
<link rel="alternate" href="/2010/11/i-m-joining-flinc-as-a-rails-developer/"/>
<id>/2010/11/i-m-joining-flinc-as-a-rails-developer/</id>
<published>2010-11-01T00:00:00Z</published>
<updated>2010-11-01T00:00:00Z</updated>
<author>
<name>Benedikt Deicke</name>
</author>
<summary type="html">&lt;p&gt;&lt;a href="http://flinc.org"&gt;&lt;img src="/assets/2010/11/flinc.png" style="float: right" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Today’s my first day as a full-time employee at
&lt;a href="http://flinc.org"&gt;flinc&lt;/a&gt; where I’ll mainly be doing Ruby on Rails
development. I got in contact with &lt;a href="http://twitter.com/m_ic"&gt;Michael&lt;/a&gt; a
few months back when he joined the &lt;a href="http://webdevfulda.de"&gt;Web Development
Fulda&lt;/a&gt; group on Xing. We met at the meetup in
september and a few days later he invited me to their offices in
Dieburg. I was impressed by their product and the spirit of the
&lt;a href="http://flinc.org/theflincteam"&gt;team&lt;/a&gt;, so I agreed on
doing trial work for a week. Obviously they liked me and my work and
offered me a job.&lt;/p&gt;
&lt;p&gt;</summary>
<content type="html">&lt;p&gt;&lt;a href="http://flinc.org"&gt;&lt;img src="/assets/2010/11/flinc.png" style="float: right" /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Today’s my first day as a full-time employee at
&lt;a href="http://flinc.org"&gt;flinc&lt;/a&gt; where I’ll mainly be doing Ruby on Rails
development. I got in contact with &lt;a href="http://twitter.com/m_ic"&gt;Michael&lt;/a&gt; a
few months back when he joined the &lt;a href="http://webdevfulda.de"&gt;Web Development
Fulda&lt;/a&gt; group on Xing. We met at the meetup in
september and a few days later he invited me to their offices in
Dieburg. I was impressed by their product and the spirit of the
&lt;a href="http://flinc.org/theflincteam"&gt;team&lt;/a&gt;, so I agreed on
doing trial work for a week. Obviously they liked me and my work and
offered me a job.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="toc_0"&gt;About flinc&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;“flinc arranges spontaneous rideshares in realtime. Directly on your
navigation system in your car.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The idea of &lt;a href="http://flinc.org"&gt;flinc&lt;/a&gt; is to connect smartphones with
satellite navigation systems to arrange rideshares in realtime. For
riders this results in an alternative to an own car, for drivers it’s an
easy way to reduce their costs.&lt;/p&gt;
&lt;p&gt;&lt;a href="http://flinc.org/"&gt;&lt;img src="/assets/2010/11/how_flinc_works_en.png" alt="image"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;flinc is currently tested in the city of Friedrichshafen and is planned
to be rolled out in several innovation regions in 2011. Head over to
&lt;a href="http://flinc.org"&gt;flinc.org&lt;/a&gt; to register and get more information.&lt;/p&gt;
&lt;h2 id="toc_1"&gt;Pair with me!&lt;/h2&gt;
&lt;p&gt;As I’m the third developer at flinc, we’re looking for another Ruby on
Rails developer to join the team. If you’re interested in pair
programming with me (or of course one of the other developers) while
working on an exciting product, send a mail to
&lt;a href="mailto:michael.huebl@flincteam.org"&gt;Michael&lt;/a&gt;.&lt;/p&gt;
</content>
</entry>
<entry>
<title>Multitouch Inspector for iPad</title>
<link rel="alternate" href="/2010/06/multitouch-inspector-for-ipad/"/>
<id>/2010/06/multitouch-inspector-for-ipad/</id>
<published>2010-06-24T00:00:00Z</published>
<updated>2010-06-24T00:00:00Z</updated>
<author>
<name>Benedikt Deicke</name>
</author>
<summary type="html">&lt;p&gt;&lt;img src="/assets/2010/06/icon.png" style="float: right" /&gt;&lt;/p&gt;
&lt;p&gt;During the last couple weeks I&amp;#39;ve been playing around with the iPad and
Mobile Safari. I built a little tool to familiarize myself with the
Multitouch JavaScript API provided by Mobile Safari as well as web
applications for the iPad in general. I named the result &lt;a href="/assets/2010/06/index.html"&gt;Multitouch
Inspector&lt;/a&gt; because
that&amp;#39;s what it does: Inspect the TouchEvents fired by the JavaScript
API. ;-) Today I decided to rewrite the tool to drop the dependency on
&lt;a href="http://prototypejs.org"&gt;Prototype.js&lt;/a&gt; and I published it on
&lt;a href="http://github.com/benedikt/multitouch-inspector"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;</summary>
<content type="html">&lt;p&gt;&lt;img src="/assets/2010/06/icon.png" style="float: right" /&gt;&lt;/p&gt;
&lt;p&gt;During the last couple weeks I&amp;#39;ve been playing around with the iPad and
Mobile Safari. I built a little tool to familiarize myself with the
Multitouch JavaScript API provided by Mobile Safari as well as web
applications for the iPad in general. I named the result &lt;a href="/assets/2010/06/index.html"&gt;Multitouch
Inspector&lt;/a&gt; because
that&amp;#39;s what it does: Inspect the TouchEvents fired by the JavaScript
API. ;-) Today I decided to rewrite the tool to drop the dependency on
&lt;a href="http://prototypejs.org"&gt;Prototype.js&lt;/a&gt; and I published it on
&lt;a href="http://github.com/benedikt/multitouch-inspector"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;/p&gt;
&lt;h2 id="toc_0"&gt;Using Multitouch Events from JavaScript&lt;/h2&gt;
&lt;p&gt;There are four events that are related to touch: &lt;code&gt;TouchStart&lt;/code&gt;, &lt;code&gt;TouchMove&lt;/code&gt;,
&lt;code&gt;TouchEnd&lt;/code&gt; and &lt;code&gt;TouchCancel&lt;/code&gt;. I&amp;#39;m not sure about what situation would
trigger &lt;code&gt;TouchCancel&lt;/code&gt; so I decided to skip it for now. Working with the
touch events is straight forward:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::javascript
document.addEventListener(&amp;#39;touchstart&amp;#39;, function(event) {
// Do whatever you&amp;#39;d like to do with the event
}, false);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Of course you can add the listener to any element just like you would
with &lt;code&gt;Click&lt;/code&gt; or &lt;code&gt;MouseOver&lt;/code&gt; events. The function gets passed an object
of type &lt;code&gt;TouchEvent&lt;/code&gt;. There are several properties on this object, but
the most interesting ones are &lt;code&gt;Touches&lt;/code&gt;, &lt;code&gt;changedTouches&lt;/code&gt; and
&lt;code&gt;targetTouches&lt;/code&gt;. They all are of type &lt;code&gt;TouchList&lt;/code&gt; and contain several
&lt;code&gt;Touch&lt;/code&gt; objects. The &lt;code&gt;touches&lt;/code&gt; property lists all touches currently on
the screen. The &lt;code&gt;changedTouches&lt;/code&gt; list contains the touches that changed
and caused the event to fire. The touches in the &lt;code&gt;targetTouches&lt;/code&gt; list
are those that are currently within the target element.&lt;/p&gt;
&lt;p&gt;Every &lt;code&gt;Touch&lt;/code&gt; has an &lt;code&gt;identifier&lt;/code&gt; property as well as &lt;code&gt;pageX&lt;/code&gt; and &lt;code&gt;pageY&lt;/code&gt;
properties. As you might have guessed already, the &lt;code&gt;pageX&lt;/code&gt; and &lt;code&gt;pageY&lt;/code&gt;
properties include the touch&amp;#39;s position on the screen. The &lt;code&gt;identifier&lt;/code&gt;
property provides an unique integer for this touch. It stays the the
same for this touch as long it is on the screen. This is particularly
useful as removing one finger will trigger a &lt;code&gt;TouchEnd&lt;/code&gt; event that implies
that all fingers were removed, immediately followed by a &lt;code&gt;TouchStart&lt;/code&gt;
event including the remaining fingers. Luckily the identifier property
stays the same for those fingers that weren&amp;#39;t removed from the screen.&lt;/p&gt;
&lt;h2 id="toc_1"&gt;Offline Application Caching&lt;/h2&gt;
&lt;p&gt;In order to use the application without having an active internet
connection or simply while the development server isn&amp;#39;t running I&amp;#39;m
using &lt;a href="http://www.w3.org/TR/offline-webapps/#offline"&gt;HTML5 Offline Application
Caching&lt;/a&gt;. It works by
defining a manifest file and referencing it in the html-tag:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::html
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html manifest=&amp;quot;application.manifest&amp;quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The manifest file itself looks like this and defines what files are
required to view the application while offline:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;CACHE MANIFEST
# c94640e9114e05f16e189605e5b65ba2357117712c949cae92cc29bc1bbd3c47
/images/background.png
/images/icon.png
/index.html
/javascripts/application.js
/stylesheets/application.css
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might wonder about the random string at the top. As the browser will
reload everything when it can&amp;#39;t find one file in it&amp;#39;s cache, I&amp;#39;m using
this string to force a reload during development. I built a small
Sinatra app (see the listing below this paragraph) that generates the
manifest and resets this string for every request. As a result, the
browser reloads everything while online but falls back to the cached
files when offline.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;:::ruby
require &amp;#39;sinatra&amp;#39;
require &amp;#39;digest/sha2&amp;#39;
require &amp;#39;pathname&amp;#39;
get &amp;#39;/application.manifest&amp;#39; do
content_type &amp;#39;text/cache-manifest&amp;#39;
manifest = &amp;quot;CACHE MANIFEST\n\n&amp;quot;
manifest &amp;lt;&amp;lt; &amp;quot;#&amp;quot; &amp;lt;&amp;lt; Digest::SHA2.hexdigest(Time.now.to_s + Time.now.usec.to_s) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot;
root = Pathname.new(settings.public)
Pathname.glob(File.join(root, &amp;quot;**&amp;quot;, &amp;quot;*&amp;quot;)).each do |p|
manifest &amp;lt;&amp;lt; &amp;quot;/&amp;quot; &amp;lt;&amp;lt; p.relative_path_from(root) &amp;lt;&amp;lt; &amp;quot;\n&amp;quot; if p.file?
end
manifest
end
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id="toc_2"&gt;Issues&lt;/h2&gt;
&lt;p&gt;There are some issues that I couldn&amp;#39;t resolve while building the
application. The startup image will only work while in portrait
orientation and its dimension has to be exactly 1004x768. Currently
there isn&amp;#39;t a way to define a startup image for horizontal orientation.&lt;/p&gt;
&lt;p&gt;While the tracking of the touches works on the iPhone (with iOS 4.0) as
well for some reason it isn&amp;#39;t possible to press the buttons in the
toolbar. At the moment I have no explanation for this rather strange
behavior, but I might take another look at it in the future.&lt;/p&gt;
&lt;h2 id="toc_3"&gt;Screenshot &amp;amp; Demo&lt;/h2&gt;
&lt;p&gt;Now that you have some insights on the internals of the application,
here&amp;#39;s a screenshot as well as a link on it to a demo:&lt;/p&gt;
&lt;p&gt;&lt;a href="/assets/2010/06/index.html"&gt;&lt;img src="/assets/2010/06/multitouch-inspector_medium.png" alt="Screenshot of the Multitouch Inspector"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="toc_4"&gt;Fork it on GitHub&lt;/h2&gt;
&lt;p&gt;I published the source code on
&lt;a href="http://github.com/benedikt/multitouch-inspector"&gt;GitHub&lt;/a&gt;. Feel free to
fork it!&lt;/p&gt;
&lt;h2 id="toc_5"&gt;Further reading&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt; &lt;a href="http://developer.apple.com/safari/library/documentation/appleapplications/reference/safariwebcontent/handlingevents/handlingevents.html#//apple_ref/doc/uid/TP40006511-SW22"&gt;Safari Web Content Guide: Handling
Events&lt;/a&gt;&lt;/li&gt;
&lt;li&gt; &lt;a href="http://mir.aculo.us/2010/06/04/making-an-ipad-html5-app-making-it-really-fast/"&gt;Making an iPad HTML5 App &amp;amp; making it really
fast&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
</content>
</entry>
</feed>