The Lab Notebookhttp://velocitylabs.io/2022-02-28T21:24:24+00:00Velocity LabsWhat Makes Velocity Labs an Outstanding Web Development Company?http://velocitylabs.io/blog/2018/07/02/phoenix-web-development-company2018-07-02T00:00:00+00:002018-07-02T00:00:00+00:00
Why should you choose Velocity Labs as your custom web application developer? Learn about our core values and what sets us apart.
<p class="lead">Here at Velocity Labs, we live for helping our clients create amazing software to start or grow their business.</p>
<p>Our developers have been creating software professionally since 2001 and we've learned a few things over the years. These lessons have become some of our core values, part of what sets us apart from the competition, and why we made the list of <a href="http://themanifest.com/web-development/companies/phoenix">top web development companies in Phoenix</a>.</p>
<h3>Communicate Early and Often</h3>
<p>Many software projects that fail, do so because of communication issues. We're not the type of company to disappear for months, only to show up with some software that doesn't meet your needs. Sadly, this happens more than you might think.</p>
<p>We highly value communication and want to speak with you every day if possible. There should never be a time when you don't know what we're working on, what progress has been made, or what roadblocks we've encountered.</p>
<h3>Always Be Learning</h3>
<div class="pull-right with-padding">
<img class="img-responsive" alt="Velocity Labs at RailsConf 2017," width="120" src="/assets/ruby-add0d5f75f0414fab4e9552978f92755ac213eba55c6ba8d5727ca3a12e61b96.png">
</div>
<p>Technology changes quickly, and we need to be able to keep up with the latest in our field. To accomplish that, we keep ourselves up-to-date on the latest developments in Ruby, Ruby on Rails, and JavaScript.</p>
<p>Additionally, we learn new techniques and software patterns, so we can develop highly maintainable and robust code.</p>
<p>Our clients can rest assured they're taken care of from a technical standpoint. Jason Adams, the co-founder of Tap Inspect, had this to say</p>
<blockquote>[They] were great at introducing us to new topics and approaches that left us as better engineers ourselves.</blockquote>
<h3>Stay Flexible and Adjust Based on the Needs of Your Customers</h3>
<blockquote class="pull-right">Our solution was developed and launched early and efficiently. The final result was very popular with our end users. - Valley Metro</blockquote>
<p>Part of our development process includes getting a minimum viable product in the hands of users as quickly as possible. The longer you wait to gain feedback from your customers, the more likely it is you're concentrating on the wrong functionality.</p>
<p>We encourage all of our clients to launch with the simplest product that provides value. After that, it's a continuous customer feedback loop that drives the development process.</p>
<h3>Need More Proof?</h3>
<div class="clutch-widget pull-left" data-url="https://clutch.co" data-widget-type="2" data-height="50" data-clutchcompany-id="478268"></div>
<p>Our reviews, along with <a href="/portfolio">our portfolio</a> give potential partners a great idea of our capabilities and what it is like to work with us.</p>
<p>Our first reviews have been outstanding, and we’ve already jumped to the top of the first page of <a href="https://clutch.co/web-developers/phoenix">Web Developers in Phoenix directory</a>. With a few more reviews, we’re hoping to be number one on this directory and some of their other Phoenix directories.</p>
<p>Before we get back to working for our clients, we encourage businesses that are thinking about working with us to view our Clutch profile and <a href="https://clutch.co/profile/velocity-labs">read what clients say about Velocity Labs</a>. We also encourage our current and past clients to leave a review for us. Now, back to developing!</p>
<p><img class="img-responsive" alt="Velocity Labs at RailsConf 2017" src="/assets/velocity-railsconf-2017-68ba2467a1f2cb24d9de3e6ec51d42d007bdcff66bddd2b17903724d476dc98a.png"></p>
Easily Extract Deeply Nested Hash Data in Rubyhttp://velocitylabs.io/blog/2018/06/20/easily-extract-deeply-nested-hash-data-in-ruby2018-06-20T00:00:00+00:002018-06-20T00:00:00+00:00
Ruby 2.3 introduced a new method, which is very usful when dealing with deeply nested data in a hash or array. Learn how to add it to your toolbox!
<p class="lead">
Dealing with deeply nested hashes can be a huge pain, but it doesn't need to be.
</p>
<p>We've all been in the situation where you obtain data in a hash and need to extract some value. For example, you have the following hash, and need to get the innermost value:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">hash</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">first: </span><span class="p">{</span> <span class="ss">second: </span><span class="p">{</span> <span class="ss">third: </span><span class="s1">'value'</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span></code></pre></figure>
<p>You could try:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">hash</span><span class="p">[</span><span class="ss">:first</span><span class="p">][</span><span class="ss">:second</span><span class="p">][</span><span class="ss">:third</span><span class="p">]</span> <span class="c1">#=> 'value'</span></code></pre></figure>
<p>However, what happens when a <code>nil</code> is encountered along the way?</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">hash</span> <span class="o">=</span> <span class="p">{}</span>
<span class="nb">hash</span><span class="p">[</span><span class="ss">:first</span><span class="p">][</span><span class="ss">:second</span><span class="p">][</span><span class="ss">:third</span><span class="p">]</span> <span class="c1">#=> NoMethodError: undefined method `[]' for nil:NilClass</span></code></pre></figure>
<p>Throwing an exeption may not be what you want. If you have access to <code>ActiveSupport</code> you could use the <code>try</code> method:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">hash</span><span class="p">.</span><span class="nf">try</span><span class="p">(</span><span class="ss">:[]</span><span class="p">,</span> <span class="ss">:first</span><span class="p">).</span><span class="nf">try</span><span class="p">(</span><span class="ss">:[]</span><span class="p">,</span> <span class="ss">:second</span><span class="p">).</span><span class="nf">try</span><span class="p">(</span><span class="ss">:[]</span><span class="p">,</span> <span class="ss">:third</span><span class="p">)</span> <span class="c1">#=> 'value'</span></code></pre></figure>
<p><b>
Now, I like the <code>try</code> method, but this is truly an abomination.
</b></p>
<h3>Enter Hash#dig</h3>
<p>As of Ruby 2.3.0, the <code>Hash#dig</code> method makes this so much easier. Take a look!</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="nb">hash</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">first: </span><span class="p">{</span> <span class="ss">second: </span><span class="p">{</span> <span class="ss">third: </span><span class="s1">'value'</span> <span class="p">}</span> <span class="p">}</span> <span class="p">}</span>
<span class="nb">hash</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="ss">:first</span><span class="p">,</span> <span class="ss">:second</span><span class="p">,</span> <span class="ss">:third</span><span class="p">)</span> <span class="c1">#=> 'value'</span>
<span class="nb">hash</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">first: </span><span class="p">{</span> <span class="ss">second: </span><span class="kp">nil</span> <span class="p">}</span> <span class="p">}</span>
<span class="nb">hash</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="ss">:first</span><span class="p">,</span> <span class="ss">:second</span><span class="p">,</span> <span class="ss">:third</span><span class="p">)</span> <span class="c1">#=> nil</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>If a <code>nil</code> is encountered at any level, then <code>nil</code> is returned.</p>
<p>Note that this also works for
<a href="http://ruby-doc.org/core-2.5.1/Array.html#method-i-dig">
<code>Array</code>
</a>and
<a href="http://ruby-doc.org/core-2.5.1/Struct.html#method-i-dig">
<code>Struct</code>
</a>, although I'm not sure they are as useful as the <code>Hash</code> method:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">array</span> <span class="o">=</span> <span class="p">[[[</span><span class="s1">'value'</span><span class="p">]]]</span>
<span class="n">array</span><span class="p">.</span><span class="nf">dig</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">)</span> <span class="c1">#=> 'value'</span></code></pre></figure>
Ruby::AZ Meetup - June 19, 2018http://velocitylabs.io/blog/2018/06/19/ruby-az-meetup-june-19th2018-06-19T00:00:00+00:002018-06-19T00:00:00+00:00
Ruby::AZ is hosting a meetup on 06/19/2018. Join fellow Ruby developers for a variety of lightning talks, and share your own!
<p><a href="https://www.meetup.com/Ruby-AZ/events/xfjpvpyxjbzb/">
Ruby::AZ is hosting a meetup on June 19th.
</a>
The Phoenix Ruby User group is a monthly meeting for Ruby enthusiasts in the Phoenix metropolitan area.</p>
<blockquote>
This month will feature several short talks on varying subjects, including how to install, execute, and interpret flame graphs, a recap of the Ruby for Good conference, and other topics.
</blockquote>
<p>Come prepared to share something, if you want, or just sit back and hear what other Phoenix Rubyists are up to. And as always, be sure to say hi to Chris or Curtis if you see us there!</p>
Save disk space by truncating dev log fileshttp://velocitylabs.io/blog/2018/06/18/how-to-save-disk-space-prune-those-dev-project-log-files2018-06-18T00:00:00+00:002018-06-18T00:00:00+00:00
If you deal with multiple projects over time, it's easy to forget about the leftover log files that may be eating up your disk space.
<h3>The problem</h3>
<p>Ever wonder where all that disk space is going?</p>
<p><img class="img-responsive" alt="Low Disk Space" src="/assets/low-disk-267bf93d74b484764b162670bcde8affee0cf989be3a7c8326c3409ec5ce2174.png"></p>
<p>Over the course of a year we touch alot of projects here at Velocity Labs. It's not uncommon for one of us to touch 5+ different code bases in that time.</p>
<p>When we're actively working on a project we're running it in development mode, running test specs, etc. All that contributes to dev and test log files growing larger and larger. Then we move on to the next project and forget about those files.</p>
<div class="wp-terminal">
$ ls -lah /Users/supairish/Projects/best/log/development.log
-rw-r--r--+ 1 supairish staff 2.6G May 25 16:58 /Users/supairish/Projects/best/log/development.log<br/>
</div>
<p>2 gigs!?</p>
<p><i>Is there a quick way to clean these up?</i></p>
<h3>The solution</h3>
<p>Well there happens to be a <b>truncate unix command</b> we could use to shorten up these log files. The only problem is that <b>truncate</b> isn't available on OSX.</p>
<p>But we can get the same functionality via Homebrew + coreutils.</p>
<div class="wp-terminal">
$ brew install coreutils <br>
==> Installing coreutils
==> Downloading https://homebrew.bintray.com/bottles/coreutils-8.29.high_sierra.bottle.tar.gz
######################################################################## 100.0%
==> Pouring coreutils-8.29.high_sierra.bottle.tar.gz
==> Caveats
All commands have been installed with the prefix 'g'.
If you really need to use these commands with their normal names, you
can add a "gnubin" directory to your PATH from your bashrc like:
PATH="/usr/local/opt/coreutils/libexec/gnubin:$PATH"
Additionally, you can access their man pages with normal names if you add
the "gnuman" directory to your MANPATH from your bashrc as well:
MANPATH="/usr/local/opt/coreutils/libexec/gnuman:$MANPATH"
==> Summary
🍺 /usr/local/Cellar/coreutils/8.29: 430 files, 8.9MB
</div>
<p>Now we can find log files and pass them to truncate setting them to 0 in size.</p>
<p><i>One thing of note is that all coreutils commands are prefixed with a <b>'g'</b>, so we'll be using <b>gtruncate</b></i></p>
<div class="wp-terminal">
$ find ~/Projects/ -name development.log | xargs gtruncate -s0 <br/>
</div>
<p>The -s flag of gtruncate allows us to set the file to any size after truncation.</p>
<p>So let's check one of our logs and see what size it's at now.</p>
<div class="wp-terminal">
$ ls -lah /Users/supairish/Projects/best/log//development.log
-rw-r--r--+ 1 supairish staff 0B Jun 8 16:47 /Users/supairish/Projects/best/log//development.log
</div>
<p>Oh yeah, nice and empty, go forth and reclaim that space from your dev machines!</p>
How Does Velocity Labs Work With New Projects?http://velocitylabs.io/blog/2018/06/11/what-to-expect-from-velocity-for-new-projects2018-06-11T00:00:00+00:002018-06-11T00:00:00+00:00
Over the years, we've refined our process to deliver a great product. Here's what you can expect from us on your new project.
<p>We are often approached by non-technical founders looking for a development team to create their idea. We also encounter businesses experiencing limitations in their current workflow, and looking for a solution.</p>
<p><b>Whatever your case may be, let our experience guide your project to success.</b></p>
<ol>
<li>
We'll schedule an initial phone meeting with you to discuss your project and expectations. Read more about
<a href="/blog/2018/04/23/what-to-expect-in-our-initial-meeting">what to expect in our initial meeting</a>.
</li>
<li>
In order to accurately estimate the amount of work required, we
<a href="/blog/2009/12/16/gathering-requirements-through-story-workshops">conduct a story workshop</a>. In this workshop, we'll gather the high-level requirements of your project. We will then use those requirements to provide a proposal and estimate.
</li>
<li>
Once we've agreed on the scope of the project and the budget, we'll get started. Find out
<a href="/blog/2018/05/07/what-is-a-typical-first-week-like">what a typical first week of development looks like</a>.
</li>
<li>
Each day, you'll see progress on your project. We believe that
<a href="/blog/2018/05/14/what-makes-a-successful-software-project">transparency and communication are the keys to a successful project</a>. We won't be working in a vacuum and you'll typically hear from us every day.
</li>
<li>
As the project begins to wind down, you may want to enter a maintenance phase. We also offer continued monitoring and small changes through our
<a href="/ruby-on-rails/maintenance">maintenance package</a>.
</li>
</ol>
<p>Whether you're in need of a new team, or just need to supplement your existing team, we have the experience to take on your project. <a href="/#contact">Contact us today!</a></p>
Easily generate test files of any sizehttp://velocitylabs.io/blog/2018/06/04/easily-generate-test-files-of-any-size2018-06-04T00:00:00+00:002018-06-04T00:00:00+00:00
Anyone who has implemented file uploads, with file size validations, knows how annoying it is to test. As Chris shows, creating files of a correct size can be fast and easy.
<h3>The problem</h3>
<p>Say you have a file upload validation in one of your Rails models. This validation limits the size of an uploaded file to 10 Mb.</p>
<script src="https://gist.github.com/velocitylabs-admin/f21951d7d08d01d32bb3e8916b191ba3.js"></script>
<p>You now want to test the upload validation size limit works correctly (manually or automated).</p>
<p>So you start searching your Documents folder, hoping to find a couple files that will work.</p>
<p>You want one file that's just below 10 Mb limit and another that is just above 10 Mb. This can be cumbersome and you just may not have local files that fit the bill.</p>
<p><i>What can we do instead?</i></p>
<h3>The solution</h3>
<p>How about dynamically generate a file of the exact size we need?</p>
<p>We can accomplish this using the <b>dd unix command</b>. So let's try creating a file of exactly 10 Mb in size.</p>
<div class="wp-terminal">
$ dd if=/dev/random of=test.txt bs=1024 count=10240<br/>
10240+0 records in<br/>
10240+0 records outt<br/>
10485760 bytes transferred in 0.859163 secs (12204622 bytes/sec)<br/>
</div>
<p>What were saying here is, read data from /dev/random, into a new file called 'test.txt', in 1024-byte size blocks (1 KB), and give me 10,240 of them (10 Mbs is 10,240 KB).</p>
<p>We can verify this file is the correct size with</p>
<div class="wp-terminal">
$ ls -lah test.txt<br/>
-rw-r--r--+ 1 supairish staff 10M Jun 4 17:42 test.txt<br/>
</div>
<p>Excellent!</p>
<p>So just rerun this command with a smaller or larger count number to get the exact file sizes you need and you're good to go.</p>
<p><i>Just remember: <b>1 Mb is 1,024 KB</b></i></p>
<p>You could also wrap this command in a spec helper to use in MiniSpec or Rspec and generate files of random size ranges during your runs.</p>
<p>Just remember to cleanup/delete in your teardowns so you don't accidentally eat up disk space.</p>
How Does Velocity Labs Work With Existing Projects?http://velocitylabs.io/blog/2018/05/21/what-to-expect-from-velocity-for-existing-projects2018-05-21T00:00:00+00:002018-05-21T00:00:00+00:00
Over the years, we've refined our process to deliver a great product. Here's what you can expect from us.
<p>We often encounter clients who have already begun, or finished, developing their inital application. Some have a completed project that needs additional functionality to better service their customers. Some are partially complete and in need of assistance in order to launch to production.</p>
<p><b>Whatever your case may be, we can help.</b></p>
<ol>
<li>
We'll want to schedule an initial phone meeting with you to discuss your project and expectations. Read more about
<a href="/blog/2018/04/23/what-to-expect-in-our-initial-meeting">what to expect in our initial meeting</a>.
</li>
<li>
You have an existing project that's been touched by one or more developers, over time. We'll want to
<a href="/blog/2018/04/30/why-do-i-need-a-code-review">review the existing code</a> to help us understand where you are currently.
</li>
<li>
In order to accurately estimate the amount of work required, we
<a href="/blog/2009/12/16/gathering-requirements-through-story-workshops">conduct a story workshop</a>. In this workshop, we'll gather the high-level requirements of your project. We will then use those requirements to provide a proposal and estimate.
</li>
<li>
Once we've agreed on the scope of the project and the budget, we'll get started. Find out
<a href="/blog/2018/05/07/what-is-a-typical-first-week-like">what a typical first week of development looks like</a>.
</li>
<li>
Each day, you'll see progress on your project. We believe that
<a href="/blog/2018/05/14/what-makes-a-successful-software-project">transparency and communication are the keys to a successful project</a>. We won't be working in a vacuum and you'll typically hear from us every day.
</li>
<li>
As the project begins to wind down, you may want to enter a maintenance phase. We also offer continued monitoring and small changes through our
<a href="/ruby-on-rails/maintenance">maintenance package</a>.
</li>
</ol>
<p>Whether you're in need of a new team, or just need to supplement your existing team, we have the experience to take on your existing project. <a href="/#contact">Contact us today!</a></p>
What Makes A Successful Software Project?http://velocitylabs.io/blog/2018/05/14/what-makes-a-successful-software-project2018-05-14T00:00:00+00:002018-05-14T00:00:00+00:00
There are many moving parts to a software project. After decades of experience, here's what we've learned makes a successful project.
<p class="lead">
Velocity Labs has several decades worth of experience building software projects for single person startups, 10 person small businesses, publicly-traded multi-billion dollar corporations, and everything in between. Here are some things we've found that help make a software project successful.
</p>
<h3>Communication</h3>
<p>We believe in a very high-level of communication. That's why we speak with our clients every day in our daily standup. In addition, the tools we use, like Trello, allow us to have text discussions around a particular piece of functionality. Of course, if needed, we can always jump on a conference call.</p>
<h3>Transparency</h3>
<p>We are more than just software developers. We are trusted consultants that advise our clients to the best of our ability on their business. We let them know our thoughts on ideas, as well as coming up with our own suggestions. Ultimately, the direction of their business is up to them, but we always let our clients know what we think.</p>
<h3>Simplicity</h3>
<p>One of the biggest killers of software projects is unnecessary complexity. Clients should remain focused on business goals and keeping things simple. By avoiding complexity, we're far more likely to create software that meets our clients' needs, as well as staying within budget and timeframe.</p>
<h3>Flexibility</h3>
<p>As a client discovers more about their customers and business, it may be necessary to adjust the priority of what we're working on, scrap some ideas, or add entirely new ones. This is a natural part of the software development process. Don't get locked into what comes out of the <a href="/blog/2009/12/16/gathering-requirements-through-story-workshops">story workshop</a>, as software is constantly evolving.</p>
<p>On the other hand, it's easy to allow flexibility to lead to unnecessary complexity. Stay focused on business goals, and remember to keep it simple.</p>
<h3>Early Feedback From Customers</h3>
<p>This is one of the most overlooked activities in software development. Many people believe they know exactly what their (future) customers will want. After all, they have all of the requirements, right?</p>
<p>Unfortunately, it doesn't usually work out that way. We know it can often feel embarassing to show a potential customer an incomplete, ugly, work-in-progress. Find trusted customers who are willing to provide unfiltered feedback, and who understand that the product is not finished. Allow them to see it as it solidifies, and as their feedback is incorporated, then reward them when the product launches.</p>
<h3>Client Participation</h3>
<p>Of course, software development wouldn't be complete without our client's participation. We expect a lot from our clients. After all, we're building this software for them.</p>
<p>At a minimum, there are 4-5 hours of planning, standups, and demos per week. Additionally, we expect our clients to be responsive when we have questions that need answers. The longer it takes to get a question answered, the longer it will take to develop that piece of functionality.</p>
<p>As we complete story cards, we also ask our clients to review them and provide feedback. The faster this feedback loop happens, the faster we can incorporate changes.</p>
<p>So, if you're ready to successfully develop your next project, <a href="/#contact">contact us now</a>!</p>
What Is a Typical First Week Like?http://velocitylabs.io/blog/2018/05/07/what-is-a-typical-first-week-like2018-05-07T00:00:00+00:002018-05-07T00:00:00+00:00
From project kickoff, to our first demo. Here's what you can expect from us in the first week of work.
<p class="lead">
The <a href="/blog/2009/12/16/gathering-requirements-through-story-workshops">story workshop</a> gathered the high-level requirements for the project and placed them in Trello as story cards. Now it's time to implement that functionality.
</p>
<h3>Project Kickoff</h3>
<p>At the beginning of the first week-long iteration, we'll schedule a kickoff meeting, the first planning meeting for the project. At this planning meeting, we'll discuss the stories for the week. Each iteration will start with a planning meeting, but the first one is slightly different than others.</p>
<p>If you have an existing project, then we'll be gathering credentials, gaining access to servers and 3rd party APIs, and setting up a staging server, if needed.</p>
<p>For green-field projects, we'll be creating the initial application, setting up the code repository, creating the staging server, making our first commits, and first deployments.</p>
<p>In either case, we'll also be grabbing the first few story cards and working on them.</p>
<h3>Staging & Review</h3>
<p>The staging server is where you'll go to verify the work done on a story card. During the first week, you can expect us to deploy to staging often. <b>Most likely, we'll deploy something the first day.</b> And when we do, we'll let you know that it's ready for your review.</p>
<h3>Daily Standups</h3>
<p>Each day of the week, we'll have a short meeting called a daily standup. This is our chance to ask you questions about a card we're working on, one we're about to pick up, or to bring up problems that we've encountered.</p>
<p>It's also your chance to ask us questions, get a status update, clarify information about a story card, or make a change.</p>
<h3>End-of-week Demo</h3>
<p>At the end of the week, we'll have deployed many times to staging, and you will have verified some of the functionality already. The end-of-week demo is our chance to walk through what we've accomplished during the week and get sign off on the story cards we feel are complete.</p>
<p>If there are any small changes to be made for a particular story card, we'll make those after the demo. Larger changes will either push the card back, and it will get rolled into the next iteration, or we may be able to create a separate card for those.</p>
<h3>Repeat</h3>
<p>In general, this is how we'll proceed each week, and after a few weeks, it will become second nature. If you're ready to get started <a href="/#contact">contact us</a>!</p>
Why Do I Need a Code Review?http://velocitylabs.io/blog/2018/04/30/why-do-i-need-a-code-review2018-04-30T00:00:00+00:002018-04-30T00:00:00+00:00
Existing applications may have been developed a while ago, by many different developers. A code review allows us to assess the current state of the project.
<p class="lead">
You've spent time and money developing your application, but do you know what was actually developed?
</p>
<p>By creating software, you've made a significant investment in your business. It might be your core business, something that compliments your business, or an internal tool to enable better business decisions. Whatever the case, do you have a clear understanding of what was built, <i>including it's limitations</i>?</p>
<p>As time goes on, your users may be the ones to discover those limtations, severely affecting your bottom line. Catching problems as early as possible reduces the overall cost of the software and instills confidence that what was built will stand the test of time.</p>
<h3>What Are We Looking For?</h3>
<p>As we review your code, we're looking for a few things:</p>
<ul>
<li>
<b>Up-to-date versions of your programming languages, frameworks, and libraries.</b> When you fall behind on versions, you miss security updates, bug fixes and performance improvements.
</li>
<li>
<b>Platform infrastructure.</b> Your application is only as secure as your infrastructure. We'll look at the current versions of your server's OS and applications.
</li>
<li>
<b>Robust test suite.</b> Having a thorough suite of tests means that new bugs are less likely to appear and ensures old bugs are not going to reappear.
</li>
<li>
<b>Best practices.</b> Did your previous developers use industry best practices when developing your software? Best practices means that your software is more maintainable and less likely to have bugs.
</li>
<li>
<b>Application security.</b> We'll audit your application's security to identify known security vulnerabilities.
</li>
</ul>
<p>If you're ready to get started with a code review, <a href="/#contact">contact us</a>.</p>
What Can I Expect From Our Initial Meeting?http://velocitylabs.io/blog/2018/04/23/what-to-expect-in-our-initial-meeting2018-04-23T00:00:00+00:002018-04-23T00:00:00+00:00
This article describes what what you can expect from our initial meeting, what information we're trying to gather and why we're gathering it.
<p>It's a question we get asked a lot. Before we start developing, there are a few things we like to ask to learn a bit more about your project.</p>
<h3>The Problem</h3>
<p><b>What problem are you trying to solve, and why? What business goals do you have in mind after you successfully execute this?</b></p>
<p>We do more than just develop software, we act as consultants in your endeavor. The more information we have about your business goals, the better equipped we'll be to advise you on the best path to reach them.</p>
<h3>Budget</h3>
<p><b>Do you have a budget set aside for this project? And is it going to be sufficient to execute your idea?</b></p>
<p>We strive to take on projects where we're an investment, and what we produce yields a return-on-investment for our clients. We'll briefly overview the monetary goals you have with this project so that we can determine whether the payoff will ultimately outweigh your costs.</p>
<h3>Timeframe</h3>
<p><b>When are you looking to get started on this project?</b></p>
<p>In order to schedule your project with the appropriate resources, we need to have a handle on your timeframe. Do you have a hard deadline or a soft one? If we estimate an 8 week project, but you have 4 weeks until a major deadline, then we'll need to allocate more resources to the project.</p>
<p>If you're ready to get started, <a href="/#contact">contact us</a>.</p>
Find and replace {} in sedhttp://velocitylabs.io/blog/2015/06/01/find-sed-empty-json-object2015-06-01T00:00:00+00:002015-06-01T00:00:00+00:00
There is an awkward gotcha when trying to find and replace the empty hash/object with sed from the command line.
<h3>The problem</h3>
<p>Replacing all instances of <code>vcr: {}</code> with <code>vcr: { record: :new_episodes }</code>, in all files below <code>spec/features</code>.</p>
<p>Normally this <code>find -exec</code> can come to the rescue, so a reasonable approach might be:</p>
<div class="highlight"><pre><code class="language-" data-lang="">$ find spec/features/ -type f -exec sed -i 's#vcr: {}#vcr: { record: :new_episodes }#' {} \;
</code></pre></div>
<p><i>Pro-tip: <a href="http://en.wikipedia.org/wiki/Regular_expression#Delimiters">change the delimiter</a> to <code>#</code></i></p>
<p>Unfortunately we run in to trouble here: It just so happens that <code>{}</code> is the sequence find's <code>-exec</code> switch uses to substitute in filenames.</p>
<p>Well, surely we can just escape the <code>{}</code> through find, right?</p>
<div class="highlight"><pre><code class="language-" data-lang="">$ find spec/features/ -type f -exec sed -i 's#vcr: \{\}#vcr: { record: :new_episodes }#' {} \;`
sed: -e expression #1, char 45: Invalid content of \{\}
</code></pre></div>
<p>Not quite. Now the <code>\{\}</code> are being passed straight through to <code>sed</code>, including the <code>\</code>s, and unfortunately when sed sees escaped <code>{}</code>s it assumes them to be a <a href="http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03_06">consecutive occurrence matcher</a>.</p>
<h3>The solution</h3>
<p>Sometimes it's best to just ditch <code>-exec</code> altogether, and use the ever versatile <code>xargs</code>:</p>
<div class="highlight"><pre><code class="language-" data-lang="">$ find spec/features/ -type f -print0 | xargs -0 sed -i 's#vcr: {}#vcr: { record: :new_episodes }#'
</code></pre></div>
Per tenant IDs, with CanCanCanhttp://velocitylabs.io/blog/2015/05/25/per-tenant-ids-with-cancancan2015-05-25T00:00:00+00:002015-05-25T00:00:00+00:00
This article shows you how to scope IDs to tenants in a multitenant system, using CanCanCan, in Ruby on Rails.
<h3>The problem</h3>
<p>You have a <a href="http://en.wikipedia.org/wiki/Multitenancy">multitenant</a> system, where each tenant is only aware of the instances of a model which belong to them. Let's say our tenants are organizations, and each organization has many customers.</p>
<p>Let's assume we've already set up a customer controller. Here we're using <a href="https://github.com/cancancommunity/cancancan">CanCanCan</a>'s <a href="https://github.com/ryanb/cancan/wiki/Nested-Resources">nested resources</a> to load an organization's customers:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="code"><pre><span class="k">class</span> <span class="nc">CustomersController</span> <span class="o"><</span> <span class="no">ApplicationController</span>
<span class="n">load_and_authorize_resource</span> <span class="ss">:organization</span>
<span class="n">load_and_authorize_resource</span> <span class="ss">:customer</span><span class="p">,</span> <span class="ss">:through</span> <span class="o">=></span> <span class="ss">:organization</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Now assume we have two organizations: Foo and Bar.</p>
<p>Foo comes along and creates two customers, which are assigned IDs <code>1</code> and <code>2</code>; next Bar creates a customer, which gets ID <code>3</code>; then Foo creates a third customer, which gets ID <code>4</code>.</p>
<p>There are a few undesirable properties with this system:</p>
<ol>
<li>From an organization's perspective, the IDs are non-contiguous, so Foo sees customers <code>1</code>, <code>2</code>, and <code>4</code>, with no explanation where 3 went.</li>
<li>Any organization will be able to infer the total number of customers across all organizations.</li>
</ol>
<p>The underlying issue with both of these properties is that it makes an organization aware of the existence of other organizations.</p>
<h3>Our solution</h3>
<p>We had a few goals for our solution:</p>
<ul>
<li>Keep our existing <a href="https://en.wikipedia.org/wiki/Surrogate_key">surrogate keys</a> i.e. the <code>id</code> column on <code>Customer</code>.</li>
<li>Be general enough to apply not only to <code>Customer</code>, but to any per-organization model added in the future.</li>
</ul>
<p>Based on this we used a solution in three parts:</p>
<ol>
<li><p>We will use an <code>organization_object_counters</code> table to keep track of how many instances of each model an organization has.
This table will offer functionality similar to <a href="http://guides.rubyonrails.org/association_basics.html#counter-cache">counter cache</a>, but it will not decrement when an instance of a model is deleted (since we do not wish to reuse IDs).</p></li>
<li><p>Any model which is scoped to an organization such as <code>Customer</code> will gain an <code>id_within_organization</code> column.
More generally we'll say that if a model <code>belongs_to :organization</code>, then it should have an <code>id_within_organization</code>.</p></li>
<li><p>When a new instance of such a model is persisted, we will set its <code>id_within_organization</code> to the current <code>organization_object_counters</code> for that organization and model.
Once that is done we will increment the count ready for the next object.</p></li>
</ol>
<h3>The code</h3>
<p>Firstly we add the table to track our <code>next_id</code> for each pairing of organization and model.
Note we also add an index, which serves the dual purpose of improving performance and ensuring we only have one <code>next_id</code> for each pairing on organization and model.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="code"><pre> <span class="k">def</span> <span class="nf">change</span>
<span class="n">create_table</span> <span class="ss">:organization_object_counters</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>
<span class="n">t</span><span class="p">.</span><span class="nf">references</span> <span class="ss">:organization</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">false</span>
<span class="n">t</span><span class="p">.</span><span class="nf">string</span> <span class="ss">:klass</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">false</span>
<span class="n">t</span><span class="p">.</span><span class="nf">integer</span> <span class="ss">:next_id</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">false</span><span class="p">,</span> <span class="ss">default: </span><span class="mi">1</span>
<span class="k">end</span>
<span class="n">add_index</span> <span class="ss">:organization_object_counters</span><span class="p">,</span> <span class="p">[</span><span class="ss">:organization_id</span><span class="p">,</span> <span class="ss">:klass</span><span class="p">],</span>
<span class="ss">unique: </span><span class="kp">true</span><span class="p">,</span> <span class="ss">name: </span><span class="s1">'index_organization_object_counters'</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Next we identify any model which <code>belongs_to :organization</code> and give it an <code>id_within_organization</code>.
Since we'll always be looking this up along with an <code>organization_id</code>, we add a <a href="https://en.wikipedia.org/wiki/Composite_index_database">composite index</a> including both.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="code"><pre><span class="k">class</span> <span class="nc">AddIdWithOrganizationToCustomers</span> <span class="o"><</span> <span class="no">ActiveRecord</span><span class="o">::</span><span class="no">Migration</span>
<span class="k">def</span> <span class="nf">change</span>
<span class="n">add_column</span> <span class="ss">:customers</span><span class="p">,</span> <span class="ss">:id_within_organization</span><span class="p">,</span> <span class="ss">:integer</span><span class="p">,</span> <span class="ss">null: </span><span class="kp">false</span>
<span class="n">add_index</span> <span class="ss">:customers</span><span class="p">,</span> <span class="p">[</span><span class="ss">:organization_id</span><span class="p">,</span> <span class="ss">:id_within_organization</span><span class="p">],</span>
<span class="ss">unique: </span><span class="kp">true</span><span class="p">,</span> <span class="ss">name: </span><span class="s1">'index_customers_on_id_within_organization'</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>We will define an <code>ActiveSupport:Concern</code> to encapsulate the <code>id_within_organization</code> behavior described above.
If <code>create</code> fails for any reason, the <code>counter.increment!</code> will rollback because the <code>yield</code> will take place within a transaction.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre><span class="k">module</span> <span class="nn">IdentifierWithinOrganization</span>
<span class="kp">extend</span> <span class="no">ActiveSupport</span><span class="o">::</span><span class="no">Concern</span>
<span class="n">included</span> <span class="k">do</span>
<span class="n">around_create</span> <span class="ss">:set_id_within_organization</span>
<span class="kp">private</span>
<span class="k">def</span> <span class="nf">set_id_within_organization</span>
<span class="n">counter</span> <span class="o">=</span> <span class="n">organization</span><span class="p">.</span><span class="nf">object_counters</span><span class="p">.</span><span class="nf">whereklass</span><span class="p">:</span> <span class="nb">self</span><span class="p">.</span><span class="nf">class</span><span class="p">.</span><span class="nf">base_class</span><span class="p">.</span><span class="nf">name</span><span class="p">.</span><span class="nf">first_or_create</span>
<span class="nb">self</span><span class="p">.</span><span class="nf">id_within_organization</span> <span class="o">=</span> <span class="n">counter</span><span class="p">.</span><span class="nf">next_id</span>
<span class="k">yield</span>
<span class="n">counter</span><span class="p">.</span><span class="nf">increment!</span> <span class="ss">:next_id</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Finally we can <code>include IdentifierWithinOrganization</code> in our <code>Customer</code> model.</p>
<h3>Integrating cancan</h3>
<p>Having an <code>id_within_organization</code> for each instance of a model is useful on its own,
but if you're already using <a href="https://github.com/cancancommunity/cancancan">cancancan</a>'s <code>load_and_authorize_resource</code> method then switching to use it your paths is as easy as:</p>
<ol>
<li>Have links use <code>id_within_organization</code> as their <a href="http://apidock.com/rails/ActiveRecord/Base/to_param.">param</a>, by adding a default <code>to_param</code> implementation in the <code>IdentifierWithinOrganization</code> concern:</li>
</ol>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"> <span class="k">def</span> <span class="nf">to_param</span>
<span class="n">id_within_organization</span><span class="p">.</span><span class="nf">to_s</span>
<span class="k">end</span>
</code></pre></figure>
<ol>
<li>Use <code>id_within_organization</code> to load resources by specify a <code>find_by</code> option to <code>load_and_authorize_resource</code>:</li>
</ol>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"> <span class="n">load_and_authorize_resource</span> <span class="ss">through: :current_organization</span><span class="p">,</span> <span class="ss">find_by: :id_within_organization</span>
</code></pre></figure>
Prevent Rails From Caching in Developmenthttp://velocitylabs.io/blog/2015/05/20/preventing-rails-cache-fetch-caching-in-development2015-05-20T00:00:00+00:002015-05-20T00:00:00+00:00
We ran into a strange problem in our development environment recently that had us puzzled. Even though we'd turned off caching, we were still seeing cached results. What's going on?!
<p>In one of our applications, we have some controller code that looks
similar to:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="vi">@routes</span> <span class="o">=</span> <span class="no">Rails</span><span class="p">.</span><span class="nf">cache</span><span class="p">.</span><span class="nf">fetch</span> <span class="s1">'routes'</span><span class="p">,</span> <span class="ss">expires_in: </span><span class="mi">6</span><span class="p">.</span><span class="nf">hours</span> <span class="k">do</span>
<span class="no">Routes</span><span class="p">.</span><span class="nf">all</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Although we had <a href="http://guides.rubyonrails.org/caching_with_rails.html#basic-caching">controller caching turned off</a> in our development
configuration, we were still seeing cached results locally. So, when we
looked in the environment, it was confusing to see a setting for turning
caching off, but caching still taking place.</p>
<h3>Caching Gotcha</h3>
<p>Turns out that interacting with <code>Rails.cache</code> directly will not check
if caching is disabled. Additionally, since the <a href="http://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-filestore">default <code>cache_store</code> is
set to a file store</a> in development, there is indeed a cache available.</p>
<p>In order to get around this, we needed to set the cache store to something
that would always result in a cache miss. <a href="http://guides.rubyonrails.org/caching_with_rails.html#activesupport-cache-nullstore">Enter <code>NullStore</code></a>:</p>
<div class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="c1"># Disable caching for Rails.cache.fetch</span>
<span class="n">config</span><span class="p">.</span><span class="nf">cache_store</span> <span class="o">=</span> <span class="p">[</span><span class="ss">:null_store</span><span class="p">]</span>
</code></pre></div>
<p>I almost feel like this should be the default for the development (and
possibly test) environment settings. If you desire caching in those
environments, then it seems like an exception and not the rule.</p>
<p>Anyway, if you've encountered this problem, hopefully this post helps!</p>
Level Up With a New Podcasthttp://velocitylabs.io/blog/2015/05/18/level-up-with-a-new-podcast2015-05-18T00:00:00+00:002015-05-18T00:00:00+00:00
Chris keeps telling me I should listen to more podcasts, but I keep putting it off. No more! I've made it a goal to create a wondorous playlist of epic proportions. Here's a podcast I found recently that kept my attention, was interesting and fit perfectly into my commute.
<p>Velocity Labs partner, <a href="https://twitter.com/supairish">Chris Irish</a>, is constantly listening to
podcasts and challenges/encourages us to listen as well. I took him
up on it recently, searched around and found a new one I really
enjoyed.</p>
<p>This podcast is called <a href="http://leveluppodcast.com/">Level Up</a> and consists of interviews with
founders, investors and other people in the startup space. It explores
how they got started, challenges they faced, successes, failures and
some actionable advice.</p>
<p>There's a lot of good info packed into each episode, and it's short
enough (13-20 minutes) to keep your (er... <em>my</em> ;) attention.</p>
<p>It started in February this year, so there are only 6 real episodes
in the playlist, but I listened to them in the span of 2 days. If
you're into that sort of thing, <a href="https://itunes.apple.com/us/podcast/level-up-podcast/id971527978">give it a listen</a>.</p>
<p>Know of other good podcasts we should add to our playlists? Let us
know on twitter <a href="https://twitter.com/velocitylabs">@velocitylabs</a>.</p>
CodeDay Phoenix - May 23-24, 2015http://velocitylabs.io/blog/2015/05/15/codeday-phoenix2015-05-15T00:00:00+00:002015-05-15T00:00:00+00:00
CodeDay is an event geared toward students, where they go from idea to app in 24 hours. It's been organized in Phoenix a few times and we're really excited to see it back again. Check it out!
<p><strong>CodeDay is coming to Phoenix again!</strong> <em>And it's in our <a href="https://cohoots.com/">favorite coworking space</a> :)</em></p>
<p>This event gathers students interested in coding and gives them
24 hours to go from idea to app. It's an intense, overnight event
that really challenges the attendees.</p>
<p>It's a great opportunity for students who already code to
flex those skills toward a goal, within a short timeframe. And if
you don't know how to code, no problem! You can learn the basics
of coding, meet new people and gain experience in a fun learning
environment.</p>
<p>I attended the demos at the end of the last code day in Phoenix and
was really impressed with the results the students achieved in such
a short timeframe. I'm looking forward to seeing what they'll come
up with this time!</p>
<p>If this sounds interesting to you, or your student, check it
out on the <a href="https://codeday.org/phoenix">CodeDay Phoenix website</a> or <a href="https://codeday.org/phoenix/register">register for CodeDay</a>.</p>
<p>See you there!</p>
Pessimistic Prerelease Peculiaritieshttp://velocitylabs.io/blog/2015/05/08/pessimistic-prerelease-peculiarities2015-05-08T00:00:00+00:002015-05-08T00:00:00+00:00
We show that a commonly held assumption about the pessimistic requirement operator isn't necessarily always true, specifically when dealing with prerelease gem versions
<h3>The assumption</h3>
<p>There seems to be an <a href="https://robots.thoughtbot.com/rubys-pessimistic-operator">assumption</a> that the pessimistic requirement operator (aka <code>~></code>, aka the <em>twiddle-wakka</em>) is short hand for a pairing of <code>>=</code> and <code><</code>.</p>
<p>e.g: in a <code>Gemfile</code> we see <code>~> 1.1</code> as equivalent to: <code>'>= 1.1' '< 2.0'</code>.</p>
<p>That seems reasonable, but be warned, it breaks down when <a href="https://guides.rubygems.org/patterns/#prerelease-gems">prerelease</a> gems are involved.</p>
<h3>The code</h3>
<p>Firstly, here's a method to check a gem's version against some requirements:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="k">def</span> <span class="nf">check</span><span class="p">(</span><span class="n">version</span><span class="p">,</span> <span class="o">*</span><span class="n">requirements</span><span class="p">)</span>
<span class="n">requirements</span><span class="p">.</span><span class="nf">map</span> <span class="p">{</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span> <span class="no">Gem</span><span class="o">::</span><span class="no">Requirement</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">v</span><span class="p">).</span><span class="nf">satisfied_by?</span> <span class="no">Gem</span><span class="o">::</span><span class="no">Version</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">version</span><span class="p">)</span> <span class="p">}.</span><span class="nf">all?</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>We can use this to check if <code>~> 1.2</code> does behave indeed the same as <code>'>= 1.1' '< 2.0'</code>. Let's check both a good version (which meets the requirements) and a bad version (which does not):</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">></span> <span class="n">good_version</span> <span class="o">=</span> <span class="s1">'1.8'</span>
<span class="o">></span> <span class="p">[</span><span class="n">check</span><span class="p">(</span><span class="n">good_version</span><span class="p">,</span> <span class="s1">'~> 1.1'</span><span class="p">),</span> <span class="n">check</span><span class="p">(</span><span class="n">good_version</span><span class="p">,</span> <span class="s1">'>= 1.1'</span><span class="p">,</span> <span class="s1">'< 2.0'</span><span class="p">)</span>
<span class="o">=></span> <span class="p">[</span><span class="kp">true</span><span class="p">,</span> <span class="kp">true</span><span class="p">]</span></code></pre></figure>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">></span> <span class="n">bad_version</span> <span class="o">=</span> <span class="s1">'2.0'</span>
<span class="o">></span> <span class="p">[</span><span class="n">check</span><span class="p">(</span><span class="n">bad_version</span><span class="p">,</span> <span class="s1">'~> 1.1'</span><span class="p">),</span> <span class="n">check</span><span class="p">(</span><span class="n">bad_version</span><span class="p">,</span> <span class="s1">'>= 1.1'</span><span class="p">,</span> <span class="s1">'< 2.0'</span><span class="p">)]</span>
<span class="o">=></span> <span class="p">[</span><span class="kp">false</span><span class="p">,</span> <span class="kp">false</span><span class="p">]</span></code></pre></figure>
<p>So far, so good.</p>
<h3>The peculiarity with prerelease</h3>
<p>However, this equivalence doesn't hold when the version being checked is a <a href="https://guides.rubygems.org/patterns/#prerelease-gems">prerelease</a>:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">></span> <span class="n">pre_version</span> <span class="o">=</span> <span class="s1">'2.0.pre'</span>
<span class="o">></span> <span class="p">[</span><span class="n">check</span><span class="p">(</span><span class="n">pre_version</span><span class="p">,</span> <span class="s1">'~> 1.1'</span><span class="p">),</span> <span class="n">check</span><span class="p">(</span><span class="n">pre_version</span><span class="p">,</span> <span class="s1">'>= 1.1'</span><span class="p">,</span> <span class="s1">'< 2.0'</span><span class="p">)]</span>
<span class="o">=></span> <span class="p">[</span><span class="kp">false</span><span class="p">,</span> <span class="kp">true</span><span class="p">]</span></code></pre></figure>
<p>We can see that the pessimistic operator doesn't think this prerelease gem meets the requirement, but our supposedly 'equivalent' version does. What gives?</p>
<p>It happens because a <em>less than 2.0</em> requirement is <em>true</em> if the version is a prerelease of 2.0:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">></span> <span class="n">check</span> <span class="s1">'2.0.pre'</span><span class="p">,</span> <span class="s1">'< 2.0'</span>
<span class="o">=></span> <span class="kp">true</span></code></pre></figure>
<p>However a <em>pessimistic 1.1</em> requirement is false if the version is a prerelease of 2.0:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="o">></span> <span class="n">check</span> <span class="s1">'2.0.pre'</span><span class="p">,</span> <span class="s1">'~> 1.1'</span>
<span class="o">=></span> <span class="kp">false</span></code></pre></figure>
<h3>Making sense of it</h3>
<p>Rephrasing those two versions as questions, it's clear that both these answers make sense:</p>
<ul>
<li>Should a prerelease of N be considered a lower version N? <strong>Yes.</strong></li>
<li>Should a prerelease of N+1 be pessimistically compatible with version N? <strong>No.</strong></li>
</ul>
<p>It is in fact our initial assumption that <code>~> 1.1</code> is equivalent to: <code>'>= 1.1', '< 2.0'</code> that is incorrect.</p>
<p>So it seems in the absence of anyway to express <em>"less than version 2.0 and any prerelease thereof"</em> that the pessimistic operator is more than just a convenient short hand, it's the only correct way to specify a requirement pessimistically.</p>
5 Indispensible Rails Testing Gemshttp://velocitylabs.io/blog/2015/05/08/5-indispensible-rails-testing-gems2015-05-08T00:00:00+00:002015-05-08T00:00:00+00:00
There are certain Ruby gems that we've found to be useful and include in every project we work on. Here are a few of the testing gems we feel are indispensible.
<p>We work on a lot of different projects and are constantly shifting
between them. Whether we're starting an application from scratch
or picking up a 3 year old maintenance project, having a robust
and comprehensive test suite is essential for us to maintain our sanity
and ensure that changes are less likely to break something.</p>
<p>Here are a few of the testing gems that we add to every project we
work on. We find them extremely useful and think you will too.</p>
<h3>Should Matchers</h3>
<p>We've been including the
<a href="https://github.com/thoughtbot/shoulda-matchers">shoulda-matchers gem</a>
since the beginning and it has become an essential tool in our test suite.</p>
<p>This gem makes it simple to ensure that your model validations are
well defined, associations exist, controllers are doing what you think
they're doing, and more. For example:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre><span class="n">describe</span> <span class="no">User</span><span class="p">,</span> <span class="ss">type: :model</span> <span class="k">do</span>
<span class="n">context</span> <span class="s1">'associations'</span> <span class="k">do</span>
<span class="n">it</span> <span class="p">{</span> <span class="n">should</span> <span class="n">belong_to</span><span class="p">(</span><span class="ss">:organization</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
<span class="n">context</span> <span class="s1">'validations'</span> <span class="k">do</span>
<span class="sx">%w(first_name last_name email password phone)</span><span class="p">.</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">a</span><span class="o">|</span>
<span class="n">it</span> <span class="p">{</span> <span class="n">should</span> <span class="n">validate_presence_of</span><span class="p">(</span><span class="n">a</span><span class="p">.</span><span class="nf">to_sym</span><span class="p">)</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
<h3>State Machine RSpec</h3>
<p>This one is a little specific as it only works with
<a href="http://rspec.info/">RSpec</a>
and the
<a href="https://github.com/pluginaweek/state_machine">state_machine gem</a>,
but we almost always have a state machine somewhere in our
applications. Golden hammer!</p>
<p>Simply put, the
<a href="https://github.com/modocache/state_machine_rspec">state_machine_rspec gem</a>
helps ensure that you have the proper states defined in your state machine,
with conditions, as well as the transitions between states.</p>
<h3>Simplecov</h3>
<p>While it's true that code coverage does not ensure a well tested
application, it is still a useful metric. The
<a href="https://github.com/colszowka/simplecov">simplecov gem</a>
helps us understand where and when we've slacked off on testing in
a nicely consumable HTML format.</p>
<p><a href="http://colszowka.github.com/simplecov/devise_result-0.5.3.png">
<img src="https://cloud.githubusercontent.com/assets/137793/17071162/db6f253e-502d-11e6-9d84-e40c3d75f333.png" alt="SimpleCov coverage report" class="img-responsive">
</a></p>
<p>Additionally, by setting the
<a href="https://github.com/colszowka/simplecov#minimum-coverage">minimum coverage percentage</a>,
you can have <code>simplecov</code> return a non-zero status if coverage falls
below a set limit. This is great for continuous integration and gives
you a heads up quickly that recently introduced code is probably not
tested well enough.</p>
<h3>Timecop</h3>
<p>For applications that are time-sensitive, like public transit
applications, the
<a href="https://github.com/travisjeffery/timecop">timecop gem</a>
is a must-have. This gem allows you to freeze time, jump back
to a specific point in time to begin execution, or accelerate the
passage of time.</p>
<p>At times, we've encountered intermittent problems with time-sensitive
tests (e.g., when we run our tests after 5pm and UTC has rolled over
to the next day). This gem has allowed us to mitigate these issues and
make our test suite more reliable. If you're dealing with time then give
this a try.</p>
<h3>Zonebie</h3>
<p>Similar to our need for <code>timecop</code>, we added the
<a href="https://github.com/alindeman/zonebie">zonebie gem</a>
to our testing toolbox this year after we encountered intermittent
test failures in timezone-sensitive tests.</p>
<p>Now, at the beginning of our test suite, we set a random timezone. This
helps us make a robust test suite when dealing with users in different
timezones. If you do encounter a problem in a specific timezone for a
test run, it's easy to rerun the tests in that timezone using the <code>ZONEBIE_TZ</code>
env var that is output at the beginning of each run. For example:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="o">[</span>Zonebie] Setting timezone: <span class="nv">ZONEBIE_TZ</span><span class="o">=</span><span class="s2">"Eastern Time (US & Canada)"</span>
...
<span class="c"># Rerun tests in the same timezone</span>
<span class="nv">$ ZONEBIE_TZ</span><span class="o">=</span><span class="s2">"Eastern Time (US & Canada)"</span> rspec spec
</pre></td></tr></tbody></table></code></pre></figure>
<p>In an effort to make our own lives easier, we test like crazy. These
gems have all proven themselves over time and our test suite would be
sad without them.</p>
<p>Have any testing gems or nuggets of wisdom to share? Hit us up on
twitter
<a href="https://twitter.com/velocitylabs">@velocitylabs</a>,
we love to hear about it!</p>
STI store: Putting attributes in their placehttp://velocitylabs.io/blog/2015/04/20/sti-store2015-04-20T00:00:00+00:002015-04-20T00:00:00+00:00
We show some of the disadvantages which arise when adding attributes to models which use single table inheritance, and introduce a new approach which resolves them.
<h3>The problem</h3>
<p>You have a model which uses <a href="http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html">single table inheritance</a>, and you need to add an attribute which exists for some sub-classes, but not others.</p>
<p>Firstly, it's worth noting that <a href="http://railscasts.com/episodes/394-sti-and-polymorphic-associations">using polymorphic associations</a> may be a better fit for your use case, but if you're sticking with STI, read on. For the remainder of the post, we'll implement <a href="http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html">Martin Fowlers's example class hierarchy</a>.</p>
<p>A Rails app has been created to demonstrate the approaches below, which can be found <a href="https://github.com/velocity-labs/sti_store/commits/master">here</a>. For our starting point we've already added the <code>Player</code> model, which uses STI to define two sub-classes: <code>Cricketer</code> and <code>Footballer</code>.</p>
<h3>The 'extra column' solution</h3>
<p>We'd like to add batting average, but only for the <code>Cricketer</code> model. Normally in this situation, we're stuck adding the column to the <code>players</code> table:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">add_column</span> <span class="ss">:players</span><span class="p">,</span> <span class="ss">:batting_average</span><span class="p">,</span> <span class="ss">:float</span></code></pre></figure>
<p>This has a few disadvantages, though. For example, both the <code>Player</code> and <code>Footballer</code> models will now respond to the <code>batting_average</code> accessor methods, even though they shouldn't:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="code"><pre><span class="mf">2.1</span><span class="o">.</span><span class="mi">2</span> <span class="p">:</span><span class="mo">001</span> <span class="o">></span> <span class="no">Player</span><span class="p">.</span><span class="nf">first</span>
<span class="o">=></span> <span class="c1">#<Footballer id: 1, name: "Alice", created_at: "2015-04-18</span>
<span class="mf">2.1</span><span class="o">.</span><span class="mi">2</span> <span class="p">:</span><span class="mo">002</span> <span class="o">></span> <span class="no">Player</span><span class="p">.</span><span class="nf">first</span><span class="p">.</span><span class="nf">batting_average</span>
<span class="o">=></span> <span class="kp">nil</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Also, whenever we have a non-cricketer, they'll always have a null <code>batting_average</code>:</p>
<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
</pre></td><td class="code"><pre><span class="n">sti_store_development</span><span class="o">=#</span> <span class="k">SELECT</span> <span class="n">name</span><span class="p">,</span> <span class="k">type</span><span class="p">,</span> <span class="n">batting_average</span> <span class="k">FROM</span> <span class="n">players</span><span class="p">;</span>
<span class="n">name</span> <span class="o">|</span> <span class="k">type</span> <span class="o">|</span> <span class="n">batting_average</span>
<span class="c1">-------+------------+-----------------</span>
<span class="n">Alice</span> <span class="o">|</span> <span class="n">Footballer</span> <span class="o">|</span>
<span class="n">Bob</span> <span class="o">|</span> <span class="n">Cricketer</span> <span class="o">|</span> <span class="mi">61</span><span class="p">.</span><span class="mi">2</span>
<span class="p">(</span><span class="mi">2</span> <span class="k">rows</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>This isn't so bad for one column, but following on with the example and adding bowler model it's easy to see how this becomes compounded as we add additional sub-class specific attributes:</p>
<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="code"><pre> <span class="n">sti_store_development</span><span class="o">=#</span> <span class="k">SELECT</span> <span class="n">name</span><span class="p">,</span> <span class="k">type</span><span class="p">,</span> <span class="n">batting_average</span><span class="p">,</span> <span class="n">bowling_average</span> <span class="k">FROM</span> <span class="n">players</span><span class="p">;</span>
<span class="n">name</span> <span class="o">|</span> <span class="k">type</span> <span class="o">|</span> <span class="n">batting_average</span> <span class="o">|</span> <span class="n">bowling_average</span>
<span class="c1">-------+------------+-----------------+-----------------</span>
<span class="n">Alice</span> <span class="o">|</span> <span class="n">Footballer</span> <span class="o">|</span> <span class="o">|</span>
<span class="n">Bob</span> <span class="o">|</span> <span class="n">Cricketer</span> <span class="o">|</span> <span class="mi">61</span><span class="p">.</span><span class="mi">2</span> <span class="o">|</span>
<span class="n">Carol</span> <span class="o">|</span> <span class="n">Bowler</span> <span class="o">|</span> <span class="mi">12</span><span class="p">.</span><span class="mi">3</span> <span class="o">|</span> <span class="mi">80</span><span class="p">.</span><span class="mi">3</span>
<span class="p">(</span><span class="mi">3</span> <span class="k">rows</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>An implementation of this this solution may be found <a href="https://github.com/velocity-labs/sti_store/commits/extra_columns">here</a>.</p>
<h3>The 'STI store' solution</h3>
<p>For our new approach we will use the lesser known <code>store_accessor</code> method from <a href="http://api.rubyonrails.org/classes/ActiveRecord/Store.html"><code>ActiveRecord::Store</code></a>, let's see how we can use it to add a <code>batting_average</code> only to the <code>Cricketer</code> model:</p>
<p>Firstly, instead of adding a separate column for each sub-class specific attribute, we'll add a single column to <code>players</code> called <code>sti_store</code>, give it type <code>json</code>:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="n">add_column</span> <span class="ss">:players</span><span class="p">,</span> <span class="ss">:sti_store</span><span class="p">,</span> <span class="ss">:json</span></code></pre></figure>
<p>Now we can use the <code>store_accessor</code> to generate accessor methods for <code>batting_average</code>. Crucially, we'll specify this on the <code>Cricketer</code> sub-class:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="code"><pre><span class="k">class</span> <span class="nc">Cricketer</span> <span class="o"><</span> <span class="no">Player</span>
<span class="n">store_accessor</span> <span class="ss">:sti_store</span><span class="p">,</span> <span class="ss">:batting_average</span>
<span class="err">…</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>By doing this we have a solution to the first issue given above: The <code>Cricketer</code> model responds to the <code>batting_average</code> accessor, but <code>Footballer</code> and <code>Player</code> <a href="https://github.com/velocity-labs/sti_store/blob/cfe3e3c37c3ab981178659a5cf5612f5a0a5d3d3/test/models/player_test.rb">do not</a>.</p>
<p>At this point it'd be nice to add a validation in the sub-class for our new attribute, and it works with <code>store_accessor</code> as expected:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
</pre></td><td class="code"><pre><span class="k">class</span> <span class="nc">Cricketer</span> <span class="o"><</span> <span class="no">Player</span>
<span class="n">store_accessor</span> <span class="ss">:sti_store</span><span class="p">,</span> <span class="ss">:batting_average</span>
<span class="n">validates</span> <span class="ss">:batting_average</span><span class="p">,</span> <span class="ss">numericality: </span><span class="p">{</span> <span class="ss">less_than_or_equal_to: </span><span class="mi">100</span> <span class="p">}</span>
<span class="err">…</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Following the example, let's add the <code>Bowler</code> model, as a sub-class of <code>Cricketer</code>, again we just specify its <code>bowling_average</code> attribute with <code>store_accessor</code>:</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="code"><pre><span class="k">class</span> <span class="nc">Bowler</span> <span class="o"><</span> <span class="no">Cricketer</span>
<span class="n">store_accessor</span> <span class="ss">:sti_store</span><span class="p">,</span> <span class="ss">:bowling_average</span>
<span class="err">…</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Note that the <code>Bowler</code> model inherits its parent's <code>batting_average</code> attribute, as <a href="https://github.com/velocity-labs/sti_store/commit/fb95ff07e39aeb340500b792e3415aae3b8c3bf0#diff-3b15f39dcd54b7cced69d2d5629fe4f4">shown here</a>.</p>
<p>Looking at this in <a href="https://www.postgresql.org/docs/9.4/static/app-psql.html"><code>psql</code></a> we now see the single <code>sti_store</code> column, showing only the appropriate attributes for each <code>type</code>:</p>
<figure class="highlight"><pre><code class="language-sql" data-lang="sql"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="code"><pre><span class="n">sti_store_development</span><span class="o">=#</span> <span class="k">SELECT</span> <span class="n">name</span><span class="p">,</span> <span class="k">type</span><span class="p">,</span> <span class="n">sti_store</span> <span class="k">FROM</span> <span class="n">players</span><span class="p">;</span>
<span class="n">name</span> <span class="o">|</span> <span class="k">type</span> <span class="o">|</span> <span class="n">sti_store</span>
<span class="c1">-------+------------+-------------------------------------------------</span>
<span class="n">Alice</span> <span class="o">|</span> <span class="n">Footballer</span> <span class="o">|</span>
<span class="n">Bob</span> <span class="o">|</span> <span class="n">Cricketer</span> <span class="o">|</span> <span class="p">{</span><span class="nv">"batting_average"</span><span class="p">:</span><span class="mi">61</span><span class="p">.</span><span class="mi">2</span><span class="p">}</span>
<span class="n">Carol</span> <span class="o">|</span> <span class="n">Bowler</span> <span class="o">|</span> <span class="p">{</span><span class="nv">"batting_average"</span><span class="p">:</span><span class="mi">12</span><span class="p">.</span><span class="mi">3</span><span class="p">,</span><span class="nv">"bowling_average"</span><span class="p">:</span><span class="mi">80</span><span class="p">.</span><span class="mi">3</span><span class="p">}</span>
<span class="p">(</span><span class="mi">3</span> <span class="k">rows</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Much better.</p>
<h3>Errata:</h3>
<p>Is PostgreSQL's JSON type required?</p>
<p>Not necessarily, we use <code>store_accessor</code> directly, but it should be
possible to use <code>store</code> instead (see the <a href="http://api.rubyonrails.org/classes/ActiveRecord/Store.html"><code>ActiveRecord::Store</code></a> documentation).</p>
3 Things We're Looking Forward to at RailsConf 2015http://velocitylabs.io/blog/2015/04/17/3-things-were-looking-forward-to-at-railsconf-20152015-04-17T00:00:00+00:002015-04-17T00:00:00+00:00
Velocity Labs is traveling to Atlanta for RailsConf 2015. Here are a few things we're excited about for the conference!
<p>Next week we'll be traveling as a company to RailsConf in Atlanta. It's
been a few years since I've been to one, but I'm really looking forward
to it. Beyond the opportunity to network and refresh our excitement about
Rails, we've been perusing the schedule and found some things that
piqued our interest!</p>
<h3>Rails 5</h3>
<p>We know that the next version of Rails is coming up and RailsConf is
a great place to learn about, and get a sneak peek at, some of the
new features.</p>
<p>While there will probably be discussion in general about the next
version and DHH will probably talk about it in his keynote, we're
interested to hear more about
<a href="http://railsconf.com/program#prop_1082">how attributes and type casting will be changing</a>
as well as <a href="http://railsconf.com/program#prop_849">how callbacks will be changing</a>.</p>
<p>We always look forward to hearing about the progression and evolution
of the framework we love!</p>
<h3>Process</h3>
<p>While it's not the most glamourous topic, process is an important
part of software development. As Velocity Labs has begun growing, we've
been evaluating and refining how we work.</p>
<p>Recently, we've defined some of our processes, including a branching
strategy, built-in code review cycle, continuous integration testing,
issue tracking and release deployment process.</p>
<p>In particular, the code review cycle is now an integral part of how we
work, but we know that there's always room for improvement. That's why
we're intrigued to learn how we can improve and
<a href="http://railsconf.com/program#prop_1165">implement a stronger code review culture</a>,
from Derek Prior of thoughtbot.</p>
<h3>JavaScript + Rails</h3>
<p>We figured JavaScript would be a hot topic this year and we weren't
disappointed. There are sessions on integrating some of the popular
frameworks like
<a href="http://railsconf.com/program#prop_962">react.js on Rails</a> and the
<a href="http://railsconf.com/program#prop_977">EmberCLI Rails gem</a>.</p>
<p>There's also a session on
<a href="http://railsconf.com/program#prop_1223">using ECMAScript 6</a>,
in your Rails app, which sounds very interesting.</p>
<p>And then, if you're tired of the JS framework hype, you can get a
side-by-side comparison of AngularJS, Ember.js and Rails server-side JS
to find out
<a href="http://railsconf.com/program#prop_999">why your front-end framework is overkill</a>.</p>
<p>If you're in Atlanta, hit us up during the conference or on Twitter
<a href="https://twitter.com/velocitylabs">@velocitylabs</a>!</p>
Easily retrieve tags from a PostgreSQL array in Railshttp://velocitylabs.io/blog/2015/04/10/easily-retrieve-tags-from-a-postgresql-array-in-rails2015-04-10T00:00:00+00:002015-04-10T00:00:00+00:00
This article will show you how to select array values easily, and efficiently, from a PostgreSQL array type using Ruby on Rails.
<p>PostgreSQL array type support makes it easy to have an
array of tags on a Rails model.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="code"><pre><span class="n">create_table</span> <span class="ss">:posts</span> <span class="k">do</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>
<span class="n">t</span><span class="p">.</span><span class="nf">string</span> <span class="ss">:tags</span><span class="p">,</span> <span class="ss">array: </span><span class="kp">true</span><span class="p">,</span> <span class="ss">default: </span><span class="p">[]</span>
<span class="k">end</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Now that you're storing some tags on your <code>Post</code>
(hand-waving...), you might want to grab an array of
unique tags entered on posts.</p>
<p>You might start down the path of selecting the post tags,
then collecting them, something we see often in Rails code.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Post</span><span class="p">.</span><span class="nf">select</span><span class="p">(</span><span class="s1">'tags'</span><span class="p">).</span><span class="nf">collect</span><span class="p">(</span><span class="o">&</span><span class="ss">:tags</span><span class="p">).</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">uniq</span></code></pre></figure>
<p>You have an array of the tags, but that's kinda ugly. There's
a lot of Ruby manipulation going on after the tags are retrieved.
The <a href="http://apidock.com/rails/ActiveRecord/Calculations/pluck"><code>pluck</code> method</a>
can help with this, by giving us just the attributes we're looking for.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Post</span><span class="p">.</span><span class="nf">pluck</span><span class="p">(</span><span class="s1">'tags'</span><span class="p">).</span><span class="nf">flatten</span><span class="p">.</span><span class="nf">uniq</span></code></pre></figure>
<p>Same result as before, but much nicer. We still have that pesky
<code>flatten.uniq</code> hanging off the end, though. PostgreSQL has
<a href="https://www.postgresql.org/docs/9.2/static/functions-array.html#ARRAY-FUNCTIONS-TABLE">an array function called <code>unnest</code></a> that will give us the same
result as our Ruby <code>flatten</code> method and <code>distinct</code> will ensure
they're unique.</p>
<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="no">Post</span><span class="p">.</span><span class="nf">pluck</span><span class="p">(</span><span class="s1">'distinct unnest(tags)'</span><span class="p">)</span></code></pre></figure>
<p>There we go, offload some of those operations onto the database.
This should be much more efficient than Ruby, so we ran some
simple benchmarks against 10000 posts, with 20382 tags (183 unique).</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
</pre></td><td class="code"><pre> user system total real
old: 0.240000 0.000000 0.240000 <span class="o">(</span> 0.245174<span class="o">)</span>
new: 0.000000 0.000000 0.000000 <span class="o">(</span> 0.010110<span class="o">)</span>
</pre></td></tr></tbody></table></code></pre></figure>
<p>Looks much better!</p>
{Event} Startup Book Clubhttp://velocitylabs.io/blog/2012/03/09/startup-book-club2012-03-09T00:00:00+00:002012-03-09T00:00:00+00:00
A monthly book club is starting in downtown Phoenix geared specifically toward topics on startups and entrepreneurship. Take a look!
<p>There are a lot of great books out there about startups, entrepreneurship
and small business and a new book club at CO+HOOTS <a href="https://cohoots.com">coworking in Phoenix</a>
aims to introduce and discuss some of those books with the local community. The
meetup will take place on the first Monday of each month for 1 - 1.5 hours.</p>
<p>Here's the full book list:</p>
<h3>2012</h3>
<ul>
<li><strong>April</strong> — <em>The Lean Startup</em> by Eric Ries</li>
<li><strong>May</strong> — <em>Rework</em> by Jason Fried & David Heinemeier Hanson</li>
<li><strong>June</strong> — <em>Ignore Everybody: and 39 Other Keys to Creativity</em> by Hugh MacLeod</li>
<li><strong>July</strong> — <em>The Startup Owner's Manual</em> by Steve Blank & Bob Dorf</li>
<li><strong>August</strong> — <em>The Art of the Start</em> by Guy Kawasaki</li>
<li><strong>September</strong> — <em>Getting Real</em> by Jason Fried, David Heinemeier Hanson & Matthew Linderman</li>
<li><strong>October</strong> — <em>Do More Faster</em> by Brad Feld & David Cohen</li>
<li><strong>November</strong> — <em>The E-Myth Revisited</em> by Michael E. Gerber</li>
<li><strong>December</strong> — <em>Founders at Work</em> by Jessica Livingston</li>
</ul>
<h3>2013</h3>
<ul>
<li><strong>January</strong> — <em>Crush It!</em> by Gary Vaynerchuk</li>
</ul>
<p>The first meetup will take place <strong>April 2, 2012 at 6pm</strong>, so pick up a
copy of <em>The Lean Startup</em> from your local library or independent bookseller
today and start reading!</p>
<p>If you have suggestions for the book club, please <a href="mailto:curtis@velocitylabs.io">let us know</a>!</p>
Entrepreneurship Series: Interview with Jenny Poonhttp://velocitylabs.io/blog/2012/01/31/entrepreneurship-series-interview-with-jenny-poon2012-01-31T00:00:00+00:002012-01-31T00:00:00+00:00
Jenny Poon — a woman who admittedly likes to tell people hell no, make little children cry and says the best part of being a business owner is, "I'm the BOSS!" Read our interview with creative director and designer Jenny Poon, and you'll see what makes this woman so successful (here's a hint -- she's won several Folio awards for best design, but for her, it's not entirely about design).
<p>Jenny Poon — a woman who admittedly likes to tell people hell no, makes little children cry and says the best part of being a business owner is, "I'm the BOSS!"</p>
<p>But she won't spit in your hair, she'll admit her faults, and she'll go out of her way to make your day. She's won several design awards, but for her, it's not entirely about design.</p>
<p><strong>Tell us about yourself</strong></p>
<p>I double majored at the University of Minnesota in advertising and journalism. Although I like writing and understanding the advertising industry, I still wasn't able to do what I love, which is design. So I went to design school. But by my second year, teachers were asking me how to do things, and I asked myself, "Why am I paying $4000 a semester to teach a class?"</p>
<p><span class="pullquote-right" data-pullquote="I don't recommend people dropping out of school, ever… but it was the best decision for me">
So I stopped that and got an internship to do design work. I don't recommend people dropping out of school, ever… but it was the best decision for me, because I learned more at my internship than I ever did at school.
</span></p>
<p><strong>What did you do after that?</strong></p>
<p>I worked for quite a few different magazines. It was great seeing how businesses work. But I got to the point where I was happy with my design work, but I hated the companies I was working for. Day after day, my anger grew. I would make suggestions, and it wasn't anything they were excited about. So I left.</p>
<p>I went to the Arizona Republic. I was art director of their local magazine, <a href="http://www.azcentral.com/style/azmag/azmagdex.html">AZ Magazine</a>. It was great because I got to meet entrepreneurs and small business people while learning about Phoenix, which really got me excited.</p>
<p><strong>Did that make you want to start your own business?</strong></p>
<p><span class="pullquote-left" data-pullquote="I think you have to work for somebody, so you know how to run your own business.">
Yes, but I remember growing up thinking, "I'll never be in a job I really love, cause that just doesn't happen." Kids grow up thinking that. They say, "I'm going to be a doctor because everyone says I should be a doctor." And they think about how they're going to work for somebody else. I think you have to work for somebody, so you know how to run your own business. I love running my own business!
</span></p>
<p>My parents own a business. They've run their own business for a really long time… since I was born. I had to go work in the restaurant every day after school. I remember thinking, "This is terrible. My parents are always at work and I always have to be there helping. I never want to run my own business."</p>
<p>Friends would ask, "Do you want to run a business when you get older?" and I'd say, "No! That's a 24/7 job for the rest of your life. When you take a vacation, it's not really a vacation cause you're getting docked pay for not working." So I was very much against it. But then you start working for other people, and you realize maybe you're crazy and selfish and there are things you want to do your way, and you don't like doing things that don't have an effect on people.</p>
<p>That's how I felt, so I decided to start my own business.</p>
<p>But this isn't a restaurant, so I still take vacations, and I work from home when I want to. I'M THE BOSS! It's scary going off on your own, but I was lucky.</p>
<p><strong>Tell us about that.</strong></p>
<p>My company, <a href="http://www.eekostudio.com/">eeko studio</a> grew out of one of those things I was passionate about: sustainability. I see the benefit in solar and being more sustainable, so I thought, "How can I help them with their cause?" I started searching out sustainable companies and hearing their stories, and learning about what they do. The ones that won me over were the ones I ended up working with.</p>
<p>And that's what eeko ended up being about — finding clients and things to do that I'm passionate about. Anything that needs design help. Good ideas need good help. eeko studio has made a name out of helping small businesses brand themselves. We do logos, print advertising, web design and online advertising.</p>
<p><strong>How did you fund your business?</strong></p>
<p>I was fortunate because I knew that I wanted to do this before I left my job, so I had time to prepare and a good cushion. It was in the back of my mind; I was mentally prepared for it. It was really exciting and the hardest work I've ever done. So I loved doing it. I had money set aside, about 6-8 months, and I already had a computer.</p>
<p><strong>How did you market your business?</strong></p>
<p>I've been really fortunate to not have to do much marketing. From the time I launched eeko studio, I had clients waiting to work with me. So when I launched, I had a flurry of work. It was great, but in the back of my head, I was always nervous about where my next paycheck was coming from. I wondered, "Am I going to be able to pay the bills in six months?" So I always have 6-8 months leeway on everything.</p>
<p><span class="pullquote-left" data-pullquote="I love being in this community of people that are passionate about everything they're doing.">
I love being in this community of people that are passionate about everything they're doing. So I asked, "How do I continue that?" The answer was by working with clients who are passionate about what they do.
</span></p>
<p>I ask, "What cool things can we do that maybe don't raise a lot of revenue, that opens all these ideas out?" Hopefully that leads to bigger projects. Big agencies are impersonal. A lot of stuff that comes out of agencies has been done and done and done again. I'm really big on keeping our team and creativity fresh. We have the time to focus on you and focus on developing ideas, 'cause they're like us: passionate.</p>
<p>I have lots of collaborators. That's how CO+HOOTS works. You work together with other people that are really strong in other skill sets and focus on your own skill set.</p>
<p><strong>Tell us more about CO+HOOTS, collaboration and coworking.</strong></p>
<p><span class="pullquote-right" data-pullquote="When people see what we're doing, they see it's really different.">
At first, I was working from home and I was doing great. But as I was growing and hiring on people, I knew it just wasn't going to work from home. Meeting at coffee shops just wasn't going to work either. I talked to a few people and looked into sharing space, just for the sake of small business and not taking crazy risks. So we looked at a few spaces, ran the numbers and it seemed to work. If everyone pitched in, we could pay the rent, pay for a few supplies and everyone could focus on their business and not worry about where they were working out of.</p>
<p>That turned into CO+HOOTS, a <a href="https://cohoots.com">coworking space in downtown Phoenix</a>.</p>
<p>We gave it a try and its been successful. It's kinda magical… but honestly, the concept isn't hard to sell, especially for the people who understand coworking and are looking for it. When people see what we're doing, they see it's really different.
</span></p>
<p><strong>Give me an example of how collaboration works.</strong></p>
<p>I've always been the art designer and creative director; I've never been the business side person. I can do it, but its not what I'm passionate about. So I don't really want to do it. So I hand that over to one of my collaborators and I'm good. We're all working together, side by side, in the same space. So there's not as much miscommunication as you might have with a distributed team.</p>
<p><strong>What's the biggest mistake you've made?</strong></p>
<p>Thinking I could do it all by myself. I learned quickly that it's okay to ask for help. People really like being asked to help other people. It gives them a level of respect and increases their reputation.</p>
<p><span class="pullquote-left" data-pullquote="It's not about competing, it's about producing a good, quality product. If you create a quality product, there's no way for others to compete.">
Try to think of it as not everyone's your competitor, but everyone's someone who could possibly help you. Tell me how I can help you, and here's what I need help with. It's not about competing, it's about producing a good, quality product. If you create a quality product, there's no way for others to compete.
</span></p>
<p>I always ask myself, "How can I get really excited about this project that I'm working on?" And then it flows… and if we're not excited about doing something, I can always pass it to someone who's a better fit; one of our collaborators at CO+HOOTS.</p>
<p><strong>What's been the most important thing you've learned?</strong></p>
<p><span class="pullquote-right" data-pullquote="If there's any red flag that goes up at any point, then don't just say no, say hell no.">
Knowing my worth and knowing when to say no. I read this book called <a href="http://www.amazon.com/Hell-Yes-Little-Simpler-Happier/dp/0740779192">Hell Yes</a>! If you're going to do something, it shouldn't just be a 'yes,' it should be a'hell yes.' If there's any red flag that goes up at any point, then don't just say no, say hell no. There are plenty of people who will take up the project, and if you take it on, then you know you'll be kicking yourself later. So its okay if we starve for a day or two until we get a good project. But that hasn't happened yet… <strong><em>knocks on wood</em></strong>
</span></p>
<p><strong>What's the best marketing you've done?</strong></p>
<p>Connecting with <a href="http://www.localfirstaz.com/">Local First Arizona</a> was huge! Because it's right along side what I believe in — working with people that are passionate about what they're doing, working with local businesses, helping improve connections between small businesses.</p>
<p>The next best thing was doing good work and having good client relationships, because those always spin off to more work. When you're working with a client, you need to make sure you're on your best behavior. Its simple things like being polite, respectful; don't spit in anyone's hair!</p>
<p><strong>What do you do when a client isn't on their best behavior?</strong></p>
<p>It always needs to be equal balance. The client has to hold up their end of the deal and if they're not, you have to stand your ground. That goes back to knowing your worth. If someone's stepping on you, you have to put your foot down and sometimes it's just not going to work out, because you're a different personality or there's different expectations. That's okay, that comes with every single business. We've had to fire a few clients.</p>
<p><strong>How do you set expectations?</strong></p>
<p>I'm a strong believer in being harsh in the beginning. I learned that from being a teacher. You have to be a bitch and set the ground rules right at the get-go. Then there's no surprises in the future. What I did when I was a teacher is be really stern, you know, make kids cry, lay down the law, then the rest of it is easy.</p>
<p>You just make it very clear from the beginning, here are all our expectations, all our deliverables, all our deadlines. If you're honest with people, they'll understand it. Admitting that you have faults, people are willing to help you overcome them. You tell them, this is what you're going to get from me, this is what I'm going to get from you, and then you over deliver, and they'll be happy.</p>
<p><span class="pullquote-left" data-pullquote="I never say I'm going to do something if I don't know I'm going to do it.">
I never say I'm going to do something if I don't know I'm going to do it. In a trusted relationship, I think of it as friendships and personal relationships quite honestly. It is business, and money is exchanged, but in the beginning, if we're clear about expectations and we build a good relationship, then hell yes!...I'll help you because I know what you're doing is good.
</span></p>
<p>If that means I think of a cool promotional item I think you should do, and I don't tell you about it and I just do it and give it to you, and it makes your day, then hell yeah! I think that's just being a good person. What would you do for your brother or sister? That's what you should be doing for your client.</p>
<p><strong>What advice would you offer someone starting their own business?</strong></p>
<p>It's okay to say no.</p>
<p>Always stay focused on what you're passionate about.</p>
<p>Don't give your boss the finger on your way out the door.</p>
<p>Oh yeah, and you can be happy at your job! It is possible! You can do this and be happy at your job. It's going to be hard work and you better be driven to do it, but you CAN do it!</p>
Funding Pitch Videos from the 2012 ASU Startup Summithttp://velocitylabs.io/blog/2012/01/27/asu-startup-summit-pitches2012-01-27T00:00:00+00:002012-01-27T00:00:00+00:00
Fifteen startup companies pitch for funding to a panel of three judges at the 2012 ASU Startup Summit at SkySong.
<p>We recently attended the 2012 ASU Startup Summit and were fortunate enough to have an excellent seat for recording the pitches. There were some really interesting pitches this year so, if you missed it, check out the companies and their funding pitches below.</p>
<h3>List of Funding Pitches at the 2012 ASU Startup Summit</h3>
<p>We realize you may not want to watch all 15 funding pitch videos, so just click on the one you're interested in below, and it will take you to the video of the company doing their funding pitch. Or watch all the funding pitch videos. Just know there are about 2.5 hours of video here; each one is about 10 minutes.</p>
<table class="table">
<tr>
<td><a href="#renature">ReNature</a></td>
<td><a href="#arizona-pro-djs">Arizona Pro DJs</a></td>
<td><a href="#bright-evolutions">Bright Evolutions</a></td>
</tr>
<tr>
<td><a href="#baby-cate">Baby Cate</a></td>
<td><a href="#mercury-innovative">Mercury Innovative</a></td>
<td><a href="#bridge-communities">Bridge Communities</a></td>
</tr>
<tr>
<td><a href="#arbsource">Arbsource</a></td>
<td><a href="#ellens-technologies">Ellens Technologies</a></td>
<td><a href="#bebe-ecoposh">Bebe EcoPosh</a></td>
</tr>
<tr>
<td><a href="#seymour-enterprises">Seymour Enterprises</a></td>
<td><a href="#bluedream">BlueDream</a></td>
<td><a href="#kvz-sports">KVZ Sports</a></td>
</tr>
<tr>
<td><a href="#g3box">G3Box</a></td>
<td><a href="#eecosphere">eEcosphere</a></td>
<td><a href="#vantage-realized">Vantage Realized</a></td>
</tr>
</table>
<h3 id="renature">reNature</h3>
<p><a href="https://www.crunchbase.com/organization/renature">reNature</a> is a Phoenix startup that industrializes the natural processes of decomposition to provide sustainable alternatives to land filling food waste while simultaneously creating alternatives to petrochemical based agricultural products.</p>
<iframe title="reNature startup funding pitch" width="560" height="315" src="http://www.youtube.com/embed/WALk7EUiWlI" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="arizona-pro-djs">Arizona Pro DJs</h3>
<p><a href="http://www.azprodjs.com/">AZ Pro DJs</a> offers entertainment services including lights, sound and DJ services.</p>
<iframe title="AZ Pro DJs pitches for funing" width="560" height="315" src="http://www.youtube.com/embed/WZjgX4CQxBU" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="bright-evolutions">Bright Evolutions</h3>
<p>Bright Evolutions pitches their healthy, flying food app.</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/y71xbmgRv1E" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="baby-cate">Baby Cate</h3>
<p>Baby Cate, also known as Simple Swaddle, pitches their custom baby swaddling blankets.</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/NvmK9MUlLzY" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="mercury-innovative">Mercury Innovative</h3>
<p><a href="http://www.mercuryinnovative.com/">Mercury Innovative</a> creates, tests, and offers unique language analysis and language arts software.</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/_rTW-zzzIb4" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="bridge-communities">Bridge Communities</h3>
<p>Bridge Communities pitches for their online community for baby boomers.</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/C9ONbNCLzAo" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="arbsource">Arbsource</h3>
<p><a href="http://www.arbsource.us/">Arbsource</a> pitches for funding for their ARBCell biological reactor system, which can cut the cost of wastewater treatment for many food and beverage manufacturing companies by 50% or more through reduced energy consumption and maintenance.</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/cA6Ly0HCzAM" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="ellens-technologies">Ellens Technologies</h3>
<p>Ellens Technologies pitches their veterinarian app.</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/7d-CzdvX8PA" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="bebe-ecoposh">Bebe EcoPosh</h3>
<p><a href="https://www.linkedin.com/company/bebe-ecoposh/">Bebe EcoPosh</a> pitches for funding for their green luxury e-boutique for baby.</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/b4bvmr5TB9Y" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="seymour-enterprises">Seymour Enterprises</h3>
<p><a href="http://seymourenterprises.us/">Seymour Enterprises</a> pitches for funding for their biomedical firm which is designing a low cost respiratory monitor designed for infants at elevated risk of Sudden Infant Death Syndrome.</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/D8FH4xnXXUI" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="bluedream">BlueDream</h3>
<p><a href="http://bluedream.tv/">BlueDream</a> pitches for funding for their one-stop production facility which provides branding, production and marketing.</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/O6sVGENz5oE" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="kvz-sports">KVZ Sports</h3>
<p><a href="http://kvzsports.com/">KVZ Sports</a> pitches for funding:</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/_-I5QMtgz48" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="g3box">G3Box</h3>
<p><a href="http://g3box.org/">G3Box</a> provides engineering design and manufacturing towards the conversion of steel shipping containers into low-cost, modular, and mobile medical clinics.</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/8vA1FxiXlMg" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="eecosphere">eEcosphere</h3>
<p>eEcosphere pitches their social came for sustainability.</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/dS_EtnmlxAc" frameborder="0" allowfullscreen></iframe>
<p><hr /></p>
<h3 id="vantage-realized">Vantage Realized</h3>
<p><a href="http://www.vantagerealized.com/">Vantage Realized</a> provides products for people with disabilities.</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/yxj2hffcClY" frameborder="0" allowfullscreen></iframe>
<p>Read more about <a href="http://studentventures.asu.edu/ventures">ASU Student Ventures</a>.</p>
Entrepreneurship Series: Interview with Brian Websterhttp://velocitylabs.io/blog/2012/01/24/entrepreneurship-series-interview-with-brian-webster2012-01-24T00:00:00+00:002012-01-24T00:00:00+00:00
In the second of our Entrepreneurship Series, we interview Brian Webster of Fat Cat Software. Brian shares his insights about being a developer and founder, the need for delegation as your business grows, and the importance of allowing user feedback to drive product development.
<p>Brian Webster's a one-man-show, who's done everything right without maybe even intending to. What's his secret? He attributes his success to making a quality product and responding well to customer feedback.</p>
<p><strong>Tell us about your company. How did you get started?</strong></p>
<p>I'm currently a one-man software company, <a href="http://fatcatsoftware.com%22">Fat Cat Software</a>. I do programming for Mac OS X. I have three applications that I sell:</p>
<ul>
<li> <a href="http://www.fatcatsoftware.com/iplm/">iPhoto Library Manager</a></li>
<li> <a href="http://www.fatcatsoftware.com/powertunes/">PowerTunes</a></li>
<li> <a href="http://www.fatcatsoftware.com/plisteditpro/">PlistEdit Pro</a></li>
</ul>
<p>I wish I could say I had some grand plan like I'm going to start my own business, but it really just kinda happened...</p>
<p>iPhoto Library Manager originally started out as a programming project to teach myself Mac OS X programming. iPhoto had just come out when I graduated from college, and I hadn't found a job yet, so I had a lot of free time on my hands. I wrote this little program that would allow people to switch between libraries, because iPhoto only allows for one library. I did it as freeware and Apple updated a year later and I did an update and still remained free.</p>
<p><span class="pullquote-left" data-pullquote="Eventually I said, what the heck, I'll try charging $20 for it, and it just kinda took off.">
As time went along, I got email, and people asking to be able to do other things. Eventually, it was a combination of me getting enough of these emails and iPhoto expanding its capabilities. At that time I was working for a Mac development company for a couple years. My then girlfriend, now wife, was up in grad school, and I had plenty of free time in the evenings; basically I had no life. I'd go to my job and program all day, then I'd come home and work on my own programming. I was just kinda plunking away on it as a side project. Eventually I said, what the heck, I'll try charging $20 for it, and it just kinda took off. Lots of people needed it; lots of people liked it. Within about a year, I was making as much for that as I was from my current job. Once it got to that point, I decide to transition over and do that full time. And I've been doing it ever since.
</span></p>
<p><strong>Where did you get the idea from?</strong></p>
<p>That's the irony of the whole thing; I'm not into photography at all. My wife is, but my own photo collection is not much, so she gathers test data for me to work on. Initially it was just a fun project to teach myself. But it's been a lot of fun and its turned into a useful, troubleshooting tool for people using iPhoto. Users keep me pretty aware of breaking iPhoto issues.</p>
<p><span class="pullquote-right" data-pullquote="I allowed user feedback to influence the development of my next product.">
Once I had Library Manager out there a common question was do you have something for iTunes library management. So I allowed user feedback to influence the development of my next product. I wanted to diversify a little bit, since there's always the possibility with one product that Apple could add everything I do into their next release. It's always a possibility, but it's been nine years and it hasn't happened yet. I think they don't want to add too much to iPhoto because it would compete with their professional photo software, Aperture.
</span></p>
<p><strong>Did having one successful project under your belt help you with your next product launch?</strong></p>
<p>Yes, both in terms of programming the app and marketing it. I was able to reuse the concepts and code from the previous app, as well as the whole process of putting up a website and offering it for sale in an online store. Plus I learned from all my previous mistakes.</p>
<p><strong>How did you market your app?</strong></p>
<p><span class="pullquote-left" data-pullquote="I haven't done a whole lot of marketing in the traditional sense.">
I haven't done a whole lot of marketing in the traditional sense. For people with corrupted iPhoto libraries, I get a fair portion of my traffic that comes directly from Apple's own discussion forums, where there are users helping each other. There are 2-3 regular users who will help a lot of people with iPhoto stuff, and for a lot of common iPhoto problems, they say, <em>"Go try iPhoto Library Manager."</em>
</span></p>
<p>I also have quite a few reviews, including a <a href="http://www.macworld.com/article/53474/2006/10/iphotolibrarymanager.html">review for iPhoto Library Manager on MacWorld</a>. Of course, it always helps when those reviews are positive, 4.5 mice or whatever rating scale they use.</p>
<p>Now, there's enough juice behind my product name that if you start to type iPhoto, Google will give you a suggestion for iPhoto Library Manager. It's a keyword that gets over 20,000 searches each month.</p>
<p><strong>How did you transition to a paid model?</strong></p>
<p>I was a little worried whether there would be backlash, so what I ended up doing was making sure everything you could do in the free version of the software you could still do for free in the updated version and it was just the new features you had to pay to access.</p>
<p>I go back and forth on the best way to do trial limitations for software, because you have to have something in there. PowerTunes is time-limited, so it's fully featured but after 30 days it locks down and you have to pay. Whereas with iPhoto Library Manager there's no time limit. It's just if you want to do X, Y or Z you have to pay. I'm not a huge fan of that model, but it seems to work pretty well.</p>
<p><strong>How do you sell your app?</strong></p>
<p>I started by selling everything through my own online store. For that I use Potion Store, an <a href="http://www.potionfactory.com/potionstore">open source Ruby on Rails web store</a>. It's on GitHub, and I'm not Rails expert, but I was able to learn enough to make the change and updates I need.</p>
<p><strong>Why not sell through the Mac App Store?</strong></p>
<p>When I first started, the app store didn't exist. And of course, the Mac app store has guidelines for what apps in the store are allowed to do. I fall outside of those guidelines. So I've never tried to sell there.</p>
<p>But the curve for the number of users for my app has gone up over time. Apple's selling more and more computers with people using iPhoto. As their user base grows, my customer base grows too. I'm in a really good situation.</p>
<p><strong>How do you spend your day?</strong></p>
<p><span class="pullquote-right" data-pullquote="Writing code is a big part of the business, but replying to emails is the second largest chunk of time.">
Writing code is a big part of the business, but replying to emails is the second largest chunk of time. In the beginning I did that myself, but then I hired a company to provide the first level of support. That's been nice since I don't have to spend as much time on emails. Apt Folk is the name of the company and they offer <a href="http://aptfolk.com/">customer service management for developers</a>.
</span></p>
<p>I'm working on a big release for iPhoto Library manager. Then I think I'll definitely take a look at developing an app for the iPhone or iPad.</p>
<p><strong>What advice would you share with other entrepreneurs, startups, and developers?</strong></p>
<p>When you're doing software, there aren't a whole lot of upfront costs if you're a developer, so you don't necessarily have to raise tons and tons of money. You can usually start as one person, write an application and get it out there.</p>
<p>When you first start out, you'll probably be doing everything yourself, so start looking for opportunities to delegate stuff out, especially after you've gotten your product out there and have some income coming in. That way you're not running around doing absolutely everything yourself.</p>
<p>And ship early and ship often. By that, I mean, don't try to make everything perfect on the first launch, or try to stuff your app with features. Whittle it down to core features and get that out there. Make it good, but not perfect. Once you start getting feedback from people, then just iterate, iterate, iterate.</p>
Three Upcoming Opportunities for your Startuphttp://velocitylabs.io/blog/2012/01/22/startup-opportunities-phoenix2012-01-22T00:00:00+00:002012-01-22T00:00:00+00:00
Here are three upcoming opportunities in Phoenix to get support, publicity, training, and possible funding for your startup. Take a look!
<p>Thinking about starting a business in the Phoenix area? Or maybe you have an existing startup that just needs a little help to expand? You're in luck, because in this post, we'll cover three upcoming opportunities for your startup business to get some awesome training, experience, publicity and in some instances, possible funding. These all take place in the Phoenix area, including Mesa, Tempe and Gilbert.</p>
<h3>Mesa New Venture Accelerator</h3>
<p>This is an opportunity for Mesa startup businesses to get ten weeks of training beginning February 2, 2012. You'll meet every Thursday evening from 5:30-8:30pm for the founder roundtable, which aims to help you start and grow your business. Experienced mentors will be available to help in the realms of retail, manufacturing, health care, software and information technology businesses.</p>
<p>Check out <a href="http://blog.stealthmode.com/work-with-francine/fasttrac-new-venture-in-mesa-enrolling-now/">Stealthmode's Mesa New Venture Accelerator</a> for more information.</p>
<h3>Roadmap to Launch 2012</h3>
<p>Roadmap to Launch starts February 3rd with a weekend kickstart product development. Then, you'll meet every Tuesday from 5:30-8:30pm. In addition, you'll have weekly meetings with a team counselor to track your progress, three opportunities to practice your pitch, and a full day of PR training.</p>
<p>Check out <a href="http://gangplankhq.com/2012/01/roadmap-to-launch-2012/">Roadmap to Launch</a> for more information.</p>
<h3>ASU Maker Week</h3>
<p>Maker Week begins February 16th and is put on through the ASU College of Technology and Innovation (CTI).</p>
<blockquote>
<p>[Maker Week] is designed to inspire a passion for building, creating and making among students, teachers, and anyone who ever looked at a mousetrap and thought: "You know, I could make that better."</p>
</blockquote>
<p>Events will include Maker Pitch, Maker Faire and the Making Your Future conference.</p>
<p>The Maker Pitch is and idea pitch event where entrepreneurs can get experience and practice pitching their innovations and startup ideas. If you wish to pitch, <a href="https://technology.asu.edu/makerweek/makerpitch/form">apply here</a>.</p>
<p>The Faire will consist of demonstrations, workshops and other hands-on challenges. If have a project you'd like to be included, here's the <a href="https://docs.google.com/spreadsheet/viewform?formkey=dHMzR0lZUHZxeFY1NzFmWjRoV2FVVWc6MQ">Maker Faire Call for Entries</a>.</p>
<p>In conjunction with Maker Week, there will also be a Making Your Future conference for high school students and their teachers, with speakers including entrepreneurs from across the country speaking on tech, science and business. Don't miss it, February 23, <a href="https://docs.google.com/spreadsheet/viewform?hl=en_US&formkey=dENDMlFrZlRyQUhTOTlQeXlMSnVabXc6MQ#gid=0">RSVP now</a></p>
<p>For more information, check out <a href="https://technology.asu.edu/makerweek">ASU's Maker Week</a>.</p>
Rapid Pitch Event at LaunchSpothttp://velocitylabs.io/blog/2012/01/19/rapid-pitch-event-at-launchspot2012-01-19T00:00:00+00:002012-01-19T00:00:00+00:00
Launchspot is an incubator in Tempe offering coworking space and funding to startups. Read more about how the program works and how to apply.
<p>LaunchSpot is a tech incubator in Tempe, AZ. Although they got rolling about a year ago, they've spent a lot of time and money developing their space, located at Broadway and 52nd Street in Tempe, Arizona. In fact, here's a video tour of the coworking space before it was built out:</p>
<div class="text-center">
<iframe width="560" height="315" src="http://www.youtube.com/embed/FjUTyUsubk4" frameborder="0" allowfullscreen></iframe>
</div>
<h3>What is the LaunchSpot Rapid Pitch Event?</h3>
<p>As a part of the <a href="https://www.facebook.com/TempeGeeks/">Tempe Geeks Night Out</a>, LaunchSpot will be offering 6 early stage startups the opportunity to pitch entrepreneurs and investors on their idea. They will be given four minutes to pitch and six minutes for Q&A.</p>
<p>They'll compete to receive a chance to get venture funding and other prizes including free office space at LaunchSpot, legal consultation with lawyers from Wong, Fuji & Carter, PC on business law, legal consultation with Wright Law Group, PLLC on intellectual property law, and more.</p>
<h3>So, how do I apply?</h3>
<p>You need to do just two things to be considered for the LaunchSpot incubator.</p>
<ol>
<li>Create a two-minute video about your idea. No getting out of it, this is required… make sure you address what your idea is, how it helps people, who it helps and why you're so passionate about it.</li>
<li>Fill out the online application form by the February 3rd deadline.</li>
</ol>
<p>You need to do the video first, because once you've made your video, you must upload it to YouTube and include a link to the video in your online application.</p>
<p>LaunchSpot will feature video submissions on their site and allow users to vote on the ones they like most. The video with the most likes will be offered an automatic place in the Rapid Pitch event.</p>
<p>When you're ready, <a href="http://rapidpitch.launchspot.com/entry-form/">apply for the LaunchSpot Rapid Pitch Event</a>.</p>
<h3>Wait, who are the judges?</h3>
<p>And the <a href="http://rapidpitch.launchspot.com/judges/">judges</a> are...</p>
<ul>
<li> Dr, Matt Kim, Founder and President QuanTera</li>
<li> Steven G Mihaylo, CEO Crexendo</li>
<li> Jeffrey Pruitt, Partner & CEO Tallwave</li>
<li> Allan Kaplan, Managing Partner KG Investments, LLC</li>
</ul>
<p><a href="http://www.facebook.com/LaunchSpot">Connect with LaunchSpot on Facebook</a>.</p>
Entrepreneurship Series: Interview with Paul Kenjorahttp://velocitylabs.io/blog/2012/01/12/entrepreneurship-series-interview-with-paul-kenjora2012-01-12T00:00:00+00:002012-01-12T00:00:00+00:00
In the first of our Entrepreneurship Series blog posts, we interview Paul Kenjora, an entrepreneur who raised $1.5 million in Arizona for his tech startup. In this interview, he talks about lessons learned from running a funded startup.
<p>Sitting casually across from me, he could be anyone, anywhere. Just looking at him, or even speaking with him, you'd never know that this is a man who went from using his media center as a server for his startup to raising $1.5 million for that very same tech startup… and in Arizona!</p>
<p>Although Paul takes a relaxed attitude, he's not afraid to speak his mind and stand by his beliefs. In this article, he shares his intimate thoughts on entrepreneurship, startups, funding and, most of all, what worked and what didn't.</p>
<p><strong>What startups have you been involved with in the past?</strong></p>
<p>My first startup was Cork Office. It was suppose to be an online office. Ironically, it was a lot like Facebook; right product, wrong market. So I came close… but it's not quite the same.</p>
<p>The next one was an IT service. We were able to remotely analyze what kind of software you were running and tell when you needed an update.</p>
<p><span class="pullquote-right" data-pullquote="I realized the industry is more about connections.">
After that one, I got tired of doing B2B projects, just going in and competing against companies like Microsoft, and I realized the industry is more about connections. At that time I was only 23, and someone who's 23 doesn't have a lot of connections. So at some point I realized this project was DNR and just let it go.
</span></p>
<p>It was a learning experience, and after that I said no more B2B. That's when I did something called One-a-Month, which I think every developer should do.</p>
<p><strong>Tell me more about One-a-Month.</strong></p>
<p>I basically developed a project each month. And the rules were simple, one weekend, and it couldn't interfere with anything social. So if a friend called me and said, let's go to coffee, I'd go. I had to start it on Friday night and finish it by Sunday night. And finish it meant push it out to the web.</p>
<p>Sometimes I did really quick projects, but they started getting more elaborate.</p>
<p>That was fun, but it was a learning experience. That's when I was learning <a href="http://python.org/">Python</a> and <a href="https://www.djangoproject.com/">Django</a>. And a couple months into it, that's when I created this last start up that I just came out of. I put it up and it was a terrible website; just download this plugin and run it on WordPress. But then the Denver Post started using it.</p>
<p>I logged in one day and the server was down. The server was a machine inside my house that was also my media center. So while I'm watching Netflix, it's serving this startup. That's called bootstrapping, right?</p>
<p>You do what you have to do, right? Because it worked. So when my server went down, I moved everything to Amazon EC2. Of all the One-a-Months I was doing, this is the one that got traction. And I thought, why not go with it?</p>
<p>So I started pursuing it. Raised a lot of money, put together a team, and made all the mistakes that a funded startup can make.</p>
<p><strong>What was your experience raising money for a tech startup?</strong></p>
<p>I spent a year, just working on raising money. Going to investor pitches, writing business plans, putting together one pagers, rehearsing my elevator pitch, walking into every writing workshop on how to write a pitch and pitch investors. I was raising money in Arizona, this wasn't Silicon Valley… so it's not like I could walk over to a different investor event ever week.</p>
<p>There are a lot of investor events in Arizona, but they're more like get togethers for people who have money to invest. So if you just find one or two, people will invite you to the rest. You may go to some events that aren't investor events, but when you look around at all the people, you think, this is just like an investor event.</p>
<p>The reason for that is Phoenix was historically a real estate town. So the predominant story among investors is: <em>"I made a lot of money in real estate, I can't make money in real estate now, my friend told me I can make money in startups."</em> And that's pretty much the way it works.</p>
<p><strong>So what would you say was the key factor for you to raise money?</strong></p>
<p>A lot of it is luck, and a lot of persistence. I applied at ASU, through something called Angelsoft, now called <a href="http://www.gust.com/">Gust</a>. I submitted my information and someone found it. Then I got a call from someone at ASU saying, <em>"We'd like to set up an interview."</em></p>
<p><span class="pullquote-right" data-pullquote="When you meet your first investor, it's a lot like dating.">
We met for lunch. We had Greek or Italian or something. When you meet your first investor, it's a lot like dating. You go out, you see if you like them. You have to be nice, they're trying to be nice, then you go through a period where you're both not so nice, then you realize what their true colors are.
</span></p>
<p>So you date your first investor for a while, then he decides to introduce you to his friends. It's just like a girlfriend, you date her and then she decides that you're good enough to meet her friends. So you end up meeting his or her friends, and at some point you don't actually do the money raising. It's the first investor that you bring in that sees the potential, then they reach out to their friends and convince them. That surprised me cause I always thought you were looking for lots of investors, and what you're really looking for is an investor rep. I've talked with a lot of other entrepreneurs, and they've corroborated that.</p>
<p><span class="pullquote-left" data-pullquote="The moment you decide to raise money, you know what you need to do, you just don't know what's going to happen.">
After a while it's autopilot. The moment you decide to raise money, you know what you need to do, you just don't know what's going to happen. So you just keep going through the motions, and at some point you get that one investor that starts introducing you to other investors. Then you wake up one day and you get a phone call from your investor/girlfriend who says, <em>"ok, I'm going to write the first check, he's going to write the second check"</em>, and you set up the office and start hiring a team.
</span></p>
<p><strong>What was the negotiation process like?</strong></p>
<p>It's painful… I don't know if I can talk about the specific details about our contract, but it comes down to getting a good lawyer. Get a good lawyer if it's your first funding. 'Cause if it's your first time taking funding, chances are the investor is going to recommend a lawyer and chances are they're going to be friends with that lawyer, so you need to be careful about what you sign.</p>
<p>So it's unavoidable you're going to need a lawyer. Because just as you're afraid, the investor and his friends are just as wary. It's not that they don't trust you, they just don't want any loopholes.</p>
<p>You may want to do convertible debt notes, that's the big thing right now.</p>
<p>Now, having said all that, I would say, don't raise money. The reason I say don't raise money is, we had a startup that went for three years with funding. And we started running out of money, so we had a decision: do we raise more money or pivot?</p>
<p>Looking back, what surprised me is that we made more progress in the last 2 two months then we did in the previous three years. So that set off red flags in my head. I said, <em>"if we can do as much with two employees in a few months what I did in three years with eight employees, why would I raise money?"</em></p>
<p><span class="pullquote-right" data-pullquote="Having a lot of money gives you permission to mess up. Not having money, and having to bootstrap it forces you to think really, really hard.">
That's stuck with me. It's about having the right team and making the right choices. Having a lot of money gives you permission to mess up. Not having money, and having to bootstrap it forces you to think really, really hard.
</span></p>
<p>Of course, I feel fortunate that I had a multi-million dollar education about how <strong>not</strong> to do a startup. So it's kind of a catch-22. If its your first start up, raise money, but go into it knowing that you're going to make a lot of mistakes, and make sure you take notes along the way.</p>
<p>When we were raising the money it was euphoria, and it's exciting. And if you have the ability to consistently turnaround startups, then yeah, raise money. But don't raise money thinking the money's going to solve your problems. Raise money because you know exactly how you're gong to apply it and exactly what you're going to use it. And the answer isn't: development…</p>
<p>Take marketing… even the word marketing is too general. Instead, say, here's our travel budget, these are the seven companies were going to meet with, here's how much we're going to spend on dinners. It's gotta be ironclad. The reason you do that is because if something doesn't work, you stop doing it.</p>
<p>Of course, crazy ideas work. Look at Groupon. They spent a ridiculous amount of money throwing parties and throwing crazy ad campaigns. I still contend they haven't really made money because they've made less than they spent, but by their standards, they're a success.</p>
<p>So there will always be the argument that if you raise enough money, you can pay your way to success. But that's not how businesses really work. Those are outliers. You can say, this company succeeded doing this… well, yes, but the number of companies that didn't succeed doing that is much greater, and chances are, you're going to fall into that last group.</p>
<p><strong>What are your thoughts on bootstrapping?</strong></p>
<p><span class="pullquote-left" data-pullquote="To me, bootstrapping is about finding a business model that you can execute, within your means. Period.">
A lot of people think bootstrapping is about not raising money. To me, bootstrapping is about finding a business model that you can execute, within your means. Period.
</span></p>
<p>What that means is, well, for developers, it's like, I can code that. But just because you can code it doesn't mean you can sell it. It doesn't mean you know the right people. I could probably develop software for the Boeing 747. But I'm not going to knock on Boeing's door and some guy's going to buy it from me. So I could spend a lot of energy on it, but odds are, I'm not going to be able to sell it. But some guy who used to work for Boeing, who's their ex-chief engineer, could probably sell it in 10 minutes.</p>
<p>So find something that fits your ability to create it, sell it, maintain it, promote it. That's where funding can come in. Sometimes blind luck works, but you have to find the right team and make sure your product is the right fit.</p>
<p>In a bootstrap, the most important part is, you have to make some money on it. So make sure you have a viable product, and it's selling. Then, raise money to scale it.</p>
<p><strong>What advice do you have for other entrepreneurs and startups?</strong></p>
<p>Launch your product to your customers, not your friends. Friends are great, but you should be more interested in marketing to actual customers than getting feedback about how cool your project is.</p>
<p>And make a profit from your first customer. A lot of people think, we're going to make money eventually. If you spend a dollar, and you make two, your odds are much better than if its the other way around. Make it a clear, simple monetization model.</p>
<p>Look at apps, they do one thing. And they'll charge you 99 cents to get it. Mobile devices are just connections to the Internet and apps are just a clean way of selling little snippets of functionality.</p>
<p>I think websites are going to go down that route too. They're going to learn something from the mobile space. Instead of building a website that does a million things under the sun, build a website that does one thing and charge people 99 cents for it. There are so many websites, that you really can't count on people repeatedly coming back to your website in order for you to make money from advertising.</p>
<p>So when you're picking a model, pick something that you can charge for right away and set the expectation that you're going to charge for it. The great thing about that is, you're going to be able to tell right away if people are interested in your product. If someone's not willing to pay $10, charge $5. If not $5, charge $1. If you can't sell it for 10 cents, stop selling it.</p>
Startup America Partnershiphttp://velocitylabs.io/blog/2012/01/09/startup-america-partnership2012-01-09T00:00:00+00:002012-01-09T00:00:00+00:00
The Startup America Partnership offers resources, tools, marketing opportunities and access to capital to startups to help America's top young companies grow and create jobs.
<p>The Startup America Partnership’s mission is to help tens of thousands of America’s top young companies grow and create jobs. They've assembled hundreds of partners and leaders across the United States focused on delivering knowledge, services, talent, customer relationships and access to capital to Startup America Firms.</p>
<p>It's free to become a Startup America Firm, which provides you with the opportunity to:</p>
<ol>
<li><p><strong>Access Tools</strong> - With their free Growth Kit, startups can access over
50 Partner Offers worth over $1.2 billion from companies like American
Express, Dell, Intuit and Microsoft, with more being added weekly.</p></li>
<li><p><strong>Join the Community</strong> - Startups will get connected with other
entrepreneurs to share notes, compare war stories, collaborate and
learn from each other. They currently have, both online and offline,
an actively engaged community of potential high-growth startups across
the country.</p></li>
<li><p><strong>Gain Exposure</strong> - Startup America provides startups an opportunity to
highlight their company across the Startup America website, promoted
guest blog posts and official Startup America seals for their website
and social media.</p></li>
</ol>
<p><a href="http://www.startupamericapartnership.org/">Sign up for free and become a Startup America Partner</a> to access the resources and participate in the community.</p>
Coworking in Phoenixhttp://velocitylabs.io/blog/2012/01/04/coworking-in-phoenix2012-01-04T00:00:00+00:002012-01-04T00:00:00+00:00
We're a small company, but we love connecting and contributing to our community. That's why we're regular drop-in members at CO+HOOTS coworking in central Phoenix.
<p>When we first went out on our own, we knew we wouldn't need a permanent office space for a while. We worked out of our homes, various coffee shops, grabbed an extra desk at businesses and dropped-in at free coworking spaces. Those were fine for a while, but eventually we craved being around other professional, creative people with which to interact.</p>
<p>From the first day I dropped-in to work at CO+HOOTS <a href="https://cohoots.com/">creative coworking in Phoenix</a>, I was impressed by the friendliness and professionalism of the companies at the space, and that kept me coming back almost every week since they opened. I was there so much I earned a special drop-in badge!</p>
<p>At the time, there were a broad range of companies including a <a href="https://www.tonyfelicepr.com/">public relations company</a>, an <a href="http://eekostudio.com/">eco-friendly design company</a>, a <a href="http://www.artifex10.com/">landscape architecture design firm</a> and several others. The rigorous application process for permanent desks seems to help gather companies that are dedicated to creating sustainable businesses and contributing to a growing community in the downtown Phoenix area.</p>
<p>Did I mention they were recently selected by Phoenix Magazine's <em>"Best of the Valley 2011"</em> as <a href="http://www.phoenixmag.com/best-of-the-valley/lifestyle-and-entertainment/">Best Shared Work Space in Phoenix</a>? If you haven't checked it out you should.</p>
<p>In fact, you can try it for free! We have 5 passes good for 3 free drop-in days and we're giving them to the first 5 people to request them. Simply email us, tweet us or find us around town and they're yours (one per person, while supplies last, of course :).</p>
<p><strong>Make 2012 your year to get out of the house and be productive!</strong></p>
AZ Disruptors Provides Funding For Startupshttp://velocitylabs.io/blog/2012/01/02/AZ-disruptors-funding-for-startups2012-01-02T00:00:00+00:002012-01-02T00:00:00+00:00
If you're a tech startup company, don't miss this opportunity for funding. In exchange for 10% of your startup, AZ Disruptors will give you cash, a place to work, and mentors to help guide you.
<p>Let's face it, many people have great ideas… but how many people have the funds required to turn that idea into reality? For those startups and entrepreneurs looking for a little assistance to get going, AZ Disruptors might be just the opportunity you're looking for.</p>
<p>If you have a product, prototype, or idea for licensed software, a web-based application or a smart phone app, AZ Disruptors would like to help you develop it. Or, if you want to be employed by a startup, partner with a startup, invest in a startup, or participate as a mentor or advisor, they'd like to hear from you as well.</p>
<h3>The Details</h3>
<p>In addition to funding Blogic, and <a href="http://www.memberdesk.com/">Member Desk</a> last year, AZ Disruptors is looking to fund up to 5 more startups this year.</p>
<p>If you apply, be prepared to move to Phoenix (if you're not already here) to complete the program.</p>
<h4>You Give:</h4>
<ul>
<li>Four months of your life</li>
<li>10% of your company</li>
</ul>
<h4>You Get:</h4>
<ul>
<li>Access to mentors</li>
<li>$20,000 cash</li>
<li>Office space and furniture</li>
<li>Internet access</li>
<li>Association with smart people, including other startups who've been where you are</li>
</ul>
<h3>The Deadline</h3>
<p><strong>January 13, 2012 is the next deadline to apply for the program</strong>.</p>
<h4>Application Tips</h4>
<ul>
<li>Provide wireframes and/or mockups if you have them</li>
<li>Create a prototype that shows the main functionality of your idea</li>
<li>Have at least 1 co-founder — 2-3 person teams are ideal</li>
</ul>
<h3>Are You Ready to Get Disruptive?</h3>
<p>We think Phoenix is fortunate to have a group like the AZ Disruptors working to fund startup ideas. If you're ready, apply for their January 13, 2012 deadline by filling out the <a href="http://www.azdisruptors.com/disruptive-company/">AZ Disruptor incubator application</a>!</p>
<p>If you want more information, here's a video presentation by AZ Disruptors, outlining their incubator program. It runs about 6½ minutes and covers all the main points.</p>
<div class="text-center">
<iframe width="560" height="315" src="http://www.youtube.com/embed/zlJ8FtsYAtM" frameborder="0" allowfullscreen></iframe>
</div>
3 Methods for Goal Settinghttp://velocitylabs.io/blog/2011/12/26/agile-goal-setting2011-12-26T00:00:00+00:002011-12-26T00:00:00+00:00
Although many people consider agile to be an approach to software development, agile methodologies can be applied to many different business processes, as well as personal goal setting.
<p>As an entrepreneur and leader, people look to you for the development of your company, it's vision and plan for moving forward. Of course, goal setting is a popular topic this time of year, so I thought I'd share a few approaches to goal setting, including a method for <strong>goal setting with agile</strong>.</p>
<p>With so many approaches to goal setting, it's important to consider the best approach for you and your business. If you fail to reach your goals, it may be due to which approach you choose. Here are just a few goal setting methods to consider.</p>
<h3>Zig Ziglar Goal Setting Formula</h3>
<p><a href="http://www.ziglar.com/">Zig Ziglar</a> is a known authority on personal motivation, management and sales training, and of course, goal setting. Here's his formula for goal setting:</p>
<ol>
<li>State your goal and the benefits for achieving it</li>
<li>Set a deadline for achieving it</li>
<li>Identify the obstacles</li>
<li>Identify resources that can assist you</li>
<li>Identify the skills you need to achieve the goal</li>
<li>Develop a plan</li>
</ol>
<h4>Zig Ziglar Goal Setting Videos</h4>
<p>These are great videos to watch if you have some time. Each video is about 4-5 minutes long.</p>
<ul>
<li><a href="http://youtu.be/Ae-VJ_lauCw">Goal Setting Video: Part 1</a></li>
<li><a href="http://youtu.be/kiQV0oTyd98">Goal Setting Video: Part 2</a></li>
<li><a href="http://youtu.be/su2UuP3aqZg">Goal Setting Video: Part 3</a></li>
</ul>
<h3>One Minute Manager Approach to Goal Setting</h3>
<p>The purpose of the One Minute Manager approach to goal setting is to establish clearly defined goals and expectations between a leader of an organization and the members of that organization. As a manager, establishing the objectives of your employees assures accountability in your organization.</p>
<ol>
<li>Define and agree upon your goals</li>
<li>Define what positive behavior looks like</li>
<li>Create a list of your goals</li>
<li>Read and re-read your goals</li>
<li>Take time to read your goals each day</li>
<li>Determine if your behavior is in alignment with your goals</li>
</ol>
<h3>The Agile Approach to Goal Setting</h3>
<p>We use an agile software development process, but it can also be applied to business and personal matters. To use an agile method for goal setting, you might take this approach.</p>
<ol>
<li>List all your goals. Yes, ALL of them.</li>
<li>Estimate the effort for each goal. Use the fibonacci sequence for rating: 1,2,3,5,8,13. If a goal has an effort higher than 8, break it down into smaller goals that require less effort</li>
<li>Prioritize each goal</li>
<li>Check in with your goals daily and work on the priorities first</li>
<li>Reassess and re-prioritize whenever you add new goals, gain new information or as needed</li>
</ol>
<p>This agile method is a great way for prioritizing multiple goals and assuring that you're working on the goals that have the highest value.</p>
<p>Of course, all three goal setting methods have a couple things in common; clearly establishing your goals, and constantly evaluating whether or not your actions are contributing to their achievement. That way you know you're on track to success this New Year.</p>
Presentation: The Jekyll Static Site Generatorhttp://velocitylabs.io/blog/2011/12/20/presentation-jekyll-static-site-generator2011-12-20T00:00:00+00:002011-12-20T00:00:00+00:00
Curtis gave a short presentation on the Jekyll static site generator for Ruby at the Dec 2011 Ruby AZ meetup. He discussed the benefits, usage and even a few gotchas found along the way.
<p>Co-founder and CEO, Curtis Miller, had the opportunity to present at the monthly
<a href="http://rubyaz.org/">Phoenix Ruby meetup</a>.
Having recently switched from Wordpress to the
<a href="https://github.com/mojombo/jekyll">Jekyll static site generator</a>, he presented his
thoughts and experience using Jekyll.</p>
<p>If you have any questions about Jekyll or how we're is using it to create our site,
please contact us and we'll be happy to help out. You can also peruse the source of the
<a href="https://github.com/velocity-labs/velocitylabs.io">Velocity Labs site on GitHub</a>.</p>
<p>In the meantime, check out the presentation:</p>
<div style="width:510px" id="__ss_10653919">
<strong style="display:block;margin:12px 0 4px">
<a href="http://www.slideshare.net/Flatterline/jekyll-presentation-slides" title="Jekyll Presentation Slides" target="_blank">Jekyll Presentation Slides</a>
</strong>
<iframe src="http://www.slideshare.net/slideshow/embed_code/10653919" width="510" height="426" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div>
<p>And the video:</p>
<iframe width="560" height="315" src="http://www.youtube.com/embed/7mXeJlFdZ2c" frameborder="0" allowfullscreen></iframe>
Gathering Requirements Through Story Workshopshttp://velocitylabs.io/blog/2009/12/16/gathering-requirements-through-story-workshops2009-12-16T00:00:00+00:002009-12-16T00:00:00+00:00
We use a technique called a story workshop to gather project requirements. Find out more about how we use this technique to provide value to our clients.
<p>Traditional requirements gathering can take an enormous amount of time, keeping developers occupied for long periods documenting the requirements instead of writing code. Sometimes this produces hundreds of written pages of documentation that can become obsolete once the first line of code is written.</p>
<p>At Velocity Labs, we believe that working software is more important that a mountain of documentation. We understand that requirements change and we're flexible enough to handle those changes, whenever they may come. We're big proponents of the <em>Agile Methodology</em> and one of the principles of agile software development states:</p>
<blockquote>
<p>Welcome changing requirements, even late in development. Agile processes harness change for the customer's competitive advantage.</p>
</blockquote>
<h3>The Story Workshop</h3>
<p>In order to not spend time and money unnecessarily on a heavy requirements gathering process, we use a technique called a <em>Story Workshop</em>. It is a technique for eliciting <strong>roles</strong>, <strong>functionality</strong> and <strong>values</strong> of the system from key stakeholders.</p>
<p>We try to limit the initial story workshop to no more than two hours and we gather every idea for functionality in the system, even if we know that it's not something we'll be doing any time soon. At this point, the stories are at a pretty high level; more details of the story will come later.</p>
<h3>Anatomy of a Story</h3>
<p>A story is kept simple and follows a pretty standard formula.</p>
<div class="highlight"><pre><code class="language-" data-lang="">As a ROLE IN THE SYSTEM
I should be able to ACCOMPLISH SOMETHING
So that I GET SOME VALUE
</code></pre></div>
<h4>Roles</h4>
<p>Roles are all of the different types of actors in the system. These could be users, managers, administrators, system administrators or even the system itself.</p>
<h4>Functionality</h4>
<p>Simply put, this is what the system allows an actor to do. Examples of functionality might include authenticating, sending an email, inviting another user, uploading files or creating data in the system.</p>
<h4>Values</h4>
<p>This is where things get really interesting. By attempting to record the value that a certain piece of functionality brings to the system, we begin to understand the motivation of our clients and their intended users. What problems are they solving and how does it fit in with the rest of the functionality? This key piece of information is extremely useful when it comes to prioritizing the stories.</p>
<h3>Estimating</h3>
<p>After about two hours we usually have a long list of desired functionality, a list of roles in the system and a better understanding of the problem this product will be attempting to solve.</p>
<p>We take these stories, estimate the effort we believe it would take to implement the functionality and arrive at an estimated time and cost for the product. We use the term <em>estimate</em> continuously here because we know that the requirements will inevitably change, and as the requirements change so do the time and cost of completion.</p>
<h3>Summary</h3>
<p>We find that the story workshop process can often be a cathartic experience for our clients. Not only have they captured all of their ideas regarding the functionality and roles in the system, but they have also had to think critically about why those things are important, or if they even are.</p>
<p>It's a challenging two hours for our development team and our clients, but in the end, it is an essential part of how we build great software.</p>
Developer Ignite Phoenix: Real World Cryptographyhttp://velocitylabs.io/blog/2009/11/11/developer-ignite-phoenix-real-world-cryptography2009-11-11T00:00:00+00:002009-11-11T00:00:00+00:00
Hear our (video) take on cryptography in the real world as voiced by Chandler at the first Developer Ignite in Phoenix. You might be surprised at what you learn.
<p>Our friend <a href="http://chrischandler.name">Chris Chandler</a> gave a presentation called <em>Real World Cryptography</em> at Developer Ignite #1 in Phoenix back in July. We recently found out <a href="https://software.intel.com/en-us/search/site/language/en/type/bds_video?query=developer%20ignite">Intel has made the videos available</a>, so please enjoy.</p>
<p><strong>(Real World Cryptography starts 7 minutes in)</strong></p>
<p><object id="flashObj" width="486" height="412" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,47,0"><param name="movie" value="http://c.brightcove.com/services/viewer/federated_f9?isVid=1" /><param name="bgcolor" value="#FFFFFF" /><param name="flashVars" value="videoId=1127676507001&playerID=741496470001&playerKey=AQ~~,AAAArH1stHk~,LuRqJUw7MaeYQkat5frTpWWPINh71g7p&domain=embed&dynamicStreaming=true" /><param name="base" value="http://admin.brightcove.com" /><param name="seamlesstabbing" value="false" /><param name="allowFullScreen" value="true" /><param name="swLiveConnect" value="true" /><param name="allowScriptAccess" value="always" /><embed src="http://c.brightcove.com/services/viewer/federated_f9?isVid=1" bgcolor="#FFFFFF" flashVars="videoId=1127676507001&playerID=741496470001&playerKey=AQ~~,AAAArH1stHk~,LuRqJUw7MaeYQkat5frTpWWPINh71g7p&domain=embed&dynamicStreaming=true" base="http://admin.brightcove.com" name="flashObj" width="486" height="412" seamlesstabbing="false" type="application/x-shockwave-flash" allowFullScreen="true" swLiveConnect="true" allowScriptAccess="always" pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash"></embed></object></p>
<p><strong>Let us know your thoughts on this talk in the comments below.</strong></p>
Desert Code Camp Followuphttp://velocitylabs.io/blog/2009/06/20/desert-code-camp-followup2009-06-20T00:00:00+00:002009-06-20T00:00:00+00:00
We presented in two separate tracks at Desert Code Camp in Phoenix to a great turnout. You can checkout the slides here.
<p>Thanks to everyone who attended the two sessions taught by us at <a href="http://desertcodecamp.com">Desert Code Camp</a>! We hope that you had a great time and learned a lot. I know we did!</p>
<p>As promised, here are the slides from the Understanding Functional Programming session:</p>
<p><a href="http://www.slideshare.net/cchandler/understanding-functional-programming?type=presentation">Understanding Functional Programming</a></p>
<iframe src="//www.slideshare.net/slideshow/embed_code/key/1GTngDpF3CR7mm" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe>
<p><strong>What topic(s) would you like to see next time?</strong></p>
Desert Code Camphttp://velocitylabs.io/blog/2009/06/09/desert-code-camp2009-06-09T00:00:00+00:002009-06-09T00:00:00+00:00
Desert Code Camp is a locally organized event for developers to present on topics they're interested in. This year, we gave two presentations.
<p>Looks like it's almost time for <a href="http://desertcodecamp.com/">Desert Code Camp</a> again! What is this event, you ask? From their site:</p>
<blockquote>
<p>Code Camps have been taking place all over the country. This is a free, one-day event put on by the local Phoenix community to help promote software development in general. There is no right or wrong language, platform, or technology. If a topic relates in any way to the code that causes a machine to produce a desired result, it's welcome here.</p>
</blockquote>
<p>This year it will be held at the <a href="http://www.devry.edu/locations/campuses/loc_phoenixcampus.jsp">DeVry University Phoenix Campus</a> and is once again being organized by <a href="http://geekswithblogs.net/lorint/Default.aspx">Lorin Thwaits</a>.</p>
<p>We will be teaching two sessions this year in the 21st Century Software Development track: <a href="http://desertcodecamp.com/signUp.aspx?session=495">Developing with CouchDB</a> and <a href="http://desertcodecamp.com/signUp.aspx?session=477">Understanding functional programming</a>.</p>
<p>The schedule became available today, so if you haven't registered do so now and build your schedule for Saturday. It looks like there are a lot of great sessions this year and we can't wait to learn from all of our colleagues. See you there!</p>
2009-04-30: Recap of Phoenix OpenCoffee Clubhttp://velocitylabs.io/blog/2009/05/03/2009-04-30-recap-of-phoenix-opencoffee-club2009-05-03T00:00:00+00:002009-05-03T00:00:00+00:00
A recap of the first Phoenix OpenCoffee Club held at Fair Trade in downtown Phoenix. Find out what was discussed, pitched and what the next plans are for OpenCoffee.
<p>We had a great turnout for the first Phoenix OpenCoffee Club, which took place April 30th at <a href="http://www.azfairtrade.com">Fair Trade Cafe</a>. About a dozen people, both entrepreneurs and investors, attended and spent several hours getting to know each other over some great coffee.</p>
<p>We heard at least 6 ideas pitched and discussed during the 2 hours we were there. The assembled group wasn't shy about asking questions and raising concerns of the entrepreneurs pitching their ideas either. I know everyone appreciated hearing the perspectives of entrepreneurs and investors alike. We hope everyone who attended found the meetup engaging and useful. We sure did!</p>
<p>Special thanks to Fair Trade Cafe for allowing us to host our event there. The <a href="http://upcoming.yahoo.com/event/2561649/">May 7th Phoenix OpenCoffee Club</a> will be held at Hob Nobs Coffee House. We'll try to have OpenCoffee Club every Thursday, so it's not a big deal if there are times you can't make it. Hope to see you there!</p>
Optimizing hardware costs for Amazon EC2http://velocitylabs.io/blog/2009/04/27/optimizing-hardware-costs-for-amazon-ec22009-04-27T00:00:00+00:002009-04-27T00:00:00+00:00
Wondering what hardware you should provision for your application hosted in the cloud with Amazon EC2? We've got you covered with this handy optimal cost calculator.
<p>Velocity Labs believes heavily in the power of <a href="http://en.wikipedia.org/wiki/Cloud_computing">cloud computing</a>. Dynamically allocated hardware on a pay-for-what-you-need basis has tremendous advantages when it comes to helping clients manage and provision their clusters. The main advantage of cloud computing is dynamically growing, or shrinking, hardware as the needs of the application change.</p>
<p>Because of the dynamic nature of cloud computing, we don't need a guaranteed answer on hardware requirements up front. However, a client may want a ballpark figure in order to set aside the right amount of budget or let investors know the estimated operational cost. You could crunch the numbers yourself, but why would you do that when we've already automated the process for you?</p>
<h3>Determining optimal cost</h3>
<p>We've constructed a very basic model for minimizing the cost of <a href="http://aws.amazon.com/ec2/">Amazon EC2</a> hardware resources which satisfies a minimum number of <a href="http://aws.amazon.com/ec2/instance-types/">EC2 Compute Units</a> and a given amount of RAM per process.</p>
<p>The technique uses <a href="http://en.wikipedia.org/wiki/Linear_programming">linear programming</a> and the <a href="http://en.wikipedia.org/wiki/GNU_Linear_Programming_Kit">GNU linear programming kit (GLPK)</a>. <strong>Note</strong>: I'm a math geek that likes linear modeling, so if you're unfamiliar with either, I'd be happy to chat with you about them over lunch.</p>
<h3>Installation</h3>
<p>First, install the GLPK. On Ubuntu execute the command</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>aptitude <span class="nb">install </span>glpk</code></pre></figure>
<p>on Mac OS X execute the command</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nb">sudo </span>port <span class="nb">install </span>glpk</code></pre></figure>
<p>Next, create the following as <code>cloud_cost.txt</code>.</p>
<figure class="highlight"><pre><code class="language-text" data-lang="text"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
</pre></td><td class="code"><pre>## Amazon EC2 Cloud
## Optimizing cost per processor
# The set of instance types
set InstanceTypes;
# The costs per instance
param InstanceCosts{a in InstanceTypes};
# The compute units per instance
param InstanceCU{b in InstanceTypes};
# The amount of RAM per instance
param InstanceRAM{b in InstanceTypes};
# The number of compute units needed
param unitsNeeded;
# The amount of RAM required per instance of the application
param ramRequiredPerAppInstance;
# The quantity of each instance to purchase
var InstanceQuantity{q in InstanceTypes}, integer, >= 0;
# The objective function to minimize, in this case: cost
minimize cost: sum{i in InstanceTypes} InstanceCosts[i] * InstanceQuantity[i] * 720;
# Minimum total compute unit constraint
s.t. supply: sum{d in InstanceTypes} InstanceCU[d] * InstanceQuantity[d] >= unitsNeeded;
# Maximum RAM per instance constraint
s.t. ramrequired{d in InstanceTypes}: InstanceCU[d] * ramRequiredPerAppInstance * InstanceQuantity[d] <= InstanceRAM[d] * InstanceQuantity[d];
solve;
display{i in InstanceTypes}: InstanceQuantity[i];
data;
set InstanceTypes := Small Large XLarge HCPULarge HCPUXLarge;
param InstanceCosts := Small 0.1
Large 0.4
XLarge 0.8
HCPULarge .2
HCPUXLarge .8;
param InstanceCU := Small 1
Large 4
XLarge 8
HCPULarge 5
HCPUXLarge 20;
param InstanceRAM := Small 1700
Large 7500
XLarge 15000
HCPULarge 1700
HCPUXLarge 7000;
# The number of compute units our cluster will need
param unitsNeeded := 500;
# The amount of RAM each process requires
param ramRequiredPerAppInstance := 125;
end;
</pre></td></tr></tbody></table></code></pre></figure>
<h3>Computing the cost</h3>
<p>The model requires the specification of two variables: total number of EC2 Compute Units and RAM. Both variables are specified at the bottom with <code>param unitsNeeded</code> and <code>param ramRequiredPerAppInstance</code> respectively. Change these params to reflect your particular situation. <strong>Note</strong>: A future article will explore capacity planning in more detail.</p>
<p>When you're ready, execute the solver using the following command:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell">glpsol <span class="nt">--model</span> cloud_cost.txt <span class="nt">--output</span> result.txt</code></pre></figure>
<h3>Analyzing the results</h3>
<p>The program generates the result into a file called <code>result.txt</code>. Assuming 500 EC2 Compute Units with 125MB of RAM per process, the file will look something like the following:</p>
<figure class="highlight"><pre><code class="language-text" data-lang="text"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="code"><pre>Problem: cloud_cost
Rows: 7
Columns: 5 (5 integer, 0 binary)
Non-zeros: 15
Status: INTEGER OPTIMAL
Objective: cost = 14400 (MINimum)
1 InstanceQuantity[Small]
* 0 0
2 InstanceQuantity[Large]
* 0 0
3 InstanceQuantity[XLarge]
* 0 0
4 InstanceQuantity[HCPULarge]
* 0 0
5 InstanceQuantity[HCPUXLarge]
* 25 0
</pre></td></tr></tbody></table></code></pre></figure>
<p>The objective function was cost, so the optimal arrangement of hardware needed to get that computational power costs $14,400/month. The second column of the hardware arrangement indicates the number of instance needed. In this case we need 25 high-CPU, extra large instances.</p>
<p><strong>Did you find this useful? Let us know in the comments!</strong></p>
Disposable proxy for secure coffee shop browsinghttp://velocitylabs.io/blog/2009/04/23/disposable-proxy-for-secure-coffee-shop-browsing2009-04-23T00:00:00+00:002009-04-23T00:00:00+00:00
Have you ever used an unsecured wireless connection at a coffeeshop? Find out how you're putting your data at risk and the steps you can take to protect yourself.
<p>If you are a highly mobile laptop user, chances are you work out of a lot of varying public locations such as coffee shops, libraries, and just about anywhere that has public wifi. Are you concerned about the privacy of your data?If you're like me, and our clients, you're very concerned. Applications like <a href="http://www.circlemud.org/%7Ejelson/software/tcpflow/">tcpflow</a> and <a href="http://www.wireshark.org/">Wireshark</a> are not only particularly effective at grabbing content from the network, they also happen to be readily available.</p>
<p><strong>Here's a very simple scheme to leverage the inexpensive power of Amazon's EC2 to create a disposable, secure proxy.</strong></p>
<h3>Getting started</h3>
<p>You will need the following to make this recipe work:</p>
<ul>
<li><p>Amazon AWS account</p></li>
<li><p>A Ubuntu-based Amazon AMI with keypair (we are using publicami-7cfd1a15 for this article)</p></li>
<li><p>An EC2 security group allowing a minimum of port 22 for SSH</p></li>
</ul>
<p>To start, launch a small instance of your AMI of choice. Once again, we prefer Ubuntu so most of this article is going to be Ubuntu-centric. This instance will need to be setup with whatever key pair you plan on using as well as be placed in the security group that allows SSH access. If you need help with this the Amazon <a href="http://console.aws.amazon.com/">AWS console</a> is particularly useful.</p>
<h3>Putting the pieces together</h3>
<p>Once the instance is made available ssh to your newly created instance.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
</pre></td><td class="code"><pre>ssh <span class="nt">-i</span> identity_file <span class="nt">-L</span> 3128:localhost:3128 root@public_ec2_domain_name
</pre></td></tr></tbody></table></code></pre></figure>
<p>The noteworthy addition to the previous line is <code>-L 3128:localhost:3128</code>. This addition to the SSH command will open port 3128 locally and forward all traffic to the remote port 3128 across the open SSH connection.</p>
<p>Once the connection is open you will need to install a proxy, we prefer <a href="http://www.squid-cache.org/">squid</a>. Squid can be installed through the following command:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
</pre></td><td class="code"><pre>aptitude update
aptitude <span class="nb">install </span>squid
</pre></td></tr></tbody></table></code></pre></figure>
<p>The last remaining step is to configure your browser of choice to use proxy <code>localhost:3128</code>.</p>
<p>The final result is <strong>all local HTTP traffic will be relayed across local port 3128 across the encrypted SSH tunnel to the 10 cents / hour remote server</strong>. From there it will go out to the internet at large away from the prying eyes of nefarious coffee shop patrons. <strong>Keep the SSH connection open for as long as you need access to the proxy.</strong></p>
<h3>Cleaning up after yourself</h3>
<p>When your done at the coffee shop feel free to decommission the AMI instance and you're done. The machine will go away with all records of the proxy's cache.</p>
<p><em>If you're looking for a solution to more than just your HTTP traffic you have options such as OpenVPN. Look for an article from us soon.</em></p>
What you don't know is affecting your performancehttp://velocitylabs.io/blog/2009/04/09/what-you-dont-know-is-affecting-your-performance2009-04-09T00:00:00+00:002009-04-09T00:00:00+00:00
Want to eek a little performance out of your current hosting setup? Follow these easy steps to decrease swapping.
<p>Does your Linux-based virtualized hosting feel a little slow? Does it suffer from preemptive swapping? Here's a quick tip on getting some more performance out of your current setup. This is especially useful if you have a significant ratio of physical RAM to in-memory programs.</p>
<h3>Swappiness</h3>
<p>The Linux 2.6 kernel has a parameter called vm.swappiness that regulates the kernel's likelihood to swap memory to disk (e.g., to free up memory for disk/content caching, load other programs, etc). Valid values are between 0 and 100. On the current release of Ubuntu (Intrepid) this value has a default value of 60 that you can investigate with the following command:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
</pre></td><td class="code"><pre><span class="nv">$ </span><span class="nb">cat</span> /proc/sys/vm/swappiness
60
</pre></td></tr></tbody></table></code></pre></figure>
<p>This value is generally fine, but if your physical memory is significantly higher than what you need it's worth investigating other choices.I personally find a pretty significant performance increase in Rails behavior with a value of 0; essentially telling the kernel to not swap anything out until it becomes absolutely necessary.</p>
<p>The value can be changed either with:</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
</pre></td><td class="code"><pre><span class="nv">$ </span>sysctl vm.swappiness<span class="o">=</span>0
</pre></td></tr></tbody></table></code></pre></figure>
<p><strong>or</strong></p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
</pre></td><td class="code"><pre><span class="nv">$ </span><span class="nb">echo </span>0 &gt<span class="p">;</span> /proc/sys/vm/swappiness
</pre></td></tr></tbody></table></code></pre></figure>
<h3>A persistent lack of swappiness</h3>
<p>If you restart the server, then your change will be lost. If you want the change to remain persistent across restarts you'll either need to create a script that calls one of these commands or edit /etc/sysctl.conf to specify the swappiness. As always, exercise caution playing with kernel parameters :-).</p>
<h3>Swapping it back in</h3>
<p>If you change vm.swappiness and want to force the kernel to swap everything back in you can temporarily disable the swap partition and then immediately re-enable it.</p>
<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><table class="rouge-table"><tbody><tr><td class="gutter gl"><pre class="lineno">1
2
3
4
5
6
7
</pre></td><td class="code"><pre><span class="nv">$ </span>swapon <span class="nt">-s</span>
Filename Type Size Used Priority
/dev/sda2 partition 524280 33948 <span class="nt">-2</span>
<span class="nv">$ </span>swapoff /dev/sda2
<span class="nv">$ </span>swapon /dev/sda2
</pre></td></tr></tbody></table></code></pre></figure>
<p>If the machine doesn't have enough memory to accommodate the swap-in the command will fail with an error.</p>
<p><strong>Let us know if this helps you out!</strong></p>