<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Andy McKenna</title>
    <description>The latest articles on Forem by Andy McKenna (@andymckenna).</description>
    <link>https://forem.com/andymckenna</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F186558%2Fdbf92231-d655-40ac-a2b4-e06f5b6e8167.jpeg</url>
      <title>Forem: Andy McKenna</title>
      <link>https://forem.com/andymckenna</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/andymckenna"/>
    <language>en</language>
    <item>
      <title>Software Engineering in the Real World -or- How to Use Agile to Mow Your Yard</title>
      <dc:creator>Andy McKenna</dc:creator>
      <pubDate>Fri, 31 Jan 2020 21:20:01 +0000</pubDate>
      <link>https://forem.com/dealeron/software-engineering-in-the-real-world-or-how-to-use-agile-to-mow-your-yard-5d50</link>
      <guid>https://forem.com/dealeron/software-engineering-in-the-real-world-or-how-to-use-agile-to-mow-your-yard-5d50</guid>
      <description>&lt;h1&gt;
  
  
  Intro
&lt;/h1&gt;

&lt;p&gt;One of the really fascinating things about writing software is that after you've done it for a while, you realize how the processes and mindset that you use can apply to just about everything around you.  A perfect example is Bryan Watts' article on &lt;a href="https://dev.to/dealeron/finding-humanity-in-software-5a45"&gt;finding humanity in software&lt;/a&gt;.  Sometimes the software process helps you understand the non-software task and other times it's just the opposite.  Today I'm going to highlight how the Agile methodology fits in with cutting grass.  Before we go any further, the answer is yes, I wrote the rough draft in my head during multiple summer sessions of lawn mowing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F8kfy5fbth10sqt5kchpe.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F8kfy5fbth10sqt5kchpe.jpg" alt="A lawn mower demonstrating striping"&gt;&lt;/a&gt;&lt;/p&gt;
There's a lot of high-tech stuff going on here



&lt;h1&gt;
  
  
  Architecture Decisions
&lt;/h1&gt;

&lt;p&gt;Before you begin on any project you have to make a few upfront decisions that will set the tone for the entire process.  How big is my yard?  Do I need a riding mower or can I be just as successful with a walk-behind?  It's tempting to just say you need the best and most expensive tools but you'll have wasted a lot of money and storage space if you bought a rider to mow 100 sq. ft. of grass.  &lt;/p&gt;

&lt;p&gt;Some decisions can be swapped out relatively cheap and easy while others would require a massive effort to change.  If you decide later that you want to mulch the grass as you cut, most of the time you can just swap the blade out.  If you were too conservative with your budget up front and ignored the self-propelled mowers then you're out of luck and have to replace the whole thing.&lt;/p&gt;

&lt;p&gt;This same discussion happens at the start of any new software project.  What platform and data storage should we use?  Do we need a whole suite of cloud products or can we get by with lower level hardware?  It depends on how much grass you're planning to cut.  The analogy of our mulching blades applies here, as well:  if we start with SQL Server Express we can eventually graduate to Azure SQL databases that we can keep scaling up.  On the other hand if we learn that we should have gone with a NoSQL datastore, that's a larger effort.&lt;/p&gt;

&lt;h1&gt;
  
  
  Scoping/Refinement
&lt;/h1&gt;

&lt;p&gt;You've purchased and assembled the mower and you're rarin' to go.  Don't just drive out into the grass and start hacking at it.  You need to plan out your overall sense of attack and set your scope.  Are you also using the string trimmer and edging in this session?  Are you only committing to mowing the front yard first and saving the back yard as a stretch goal?  Setting your scope ahead of time helps you avoid making a mistake by over committing and cutting a straight line from the front yard to the back, requiring you to finish the entire yard.&lt;/p&gt;

&lt;p&gt;When you're ready to start writing code in a new project it's tempting to write a grand framework that handles everything you can think of but that creates a huge upfront investment with nothing shippable for multiple iterations.  Agile is focused on being able to ship a working version of the project at the end of each iteration and being able to pivot your priorities if they change over time.  If you only mow the front yard first, you could put the backyard on hold and deal with some other hot issue that springs up.&lt;/p&gt;

&lt;h1&gt;
  
  
  Iterations/Retro
&lt;/h1&gt;

&lt;p&gt;When I'm cutting the grass, I like to think of each line as a hyperspeed version of an agile iteration.  When you start the line, you look across to where the line will finish and aim the mower before you start moving.  This is sort of like sprint planning:  you've started adjacent from the previous line, you've made a very short term goal, you've decided how you're going to get there, and you walk the line with your eyes only on the end of that iteration.&lt;/p&gt;

&lt;p&gt;Let's stretch the analogy further and talk about how you committed to only cutting the grass in that line and you're not going to veer off in some random direction.  You might spot other things that need to be done later in the task but you're just mentally filing those away in your backlog.  Everyone loves the happy path but there is also the possibility of your iteration getting blown up when you discover that your kids left a toy in the yard.&lt;/p&gt;

&lt;p&gt;When you get to the end of your line and turn around, it's a great time to have a little retrospective on how that previous line went.  Was it straight?  Did you miss a spot?  If you followed the line before it and you still were curved, you might be a victim of grass debt and entitled to compensation.&lt;/p&gt;

&lt;h1&gt;
  
  
  Grass Debt
&lt;/h1&gt;

&lt;p&gt;Grass Debt isn't a real thing but I think of it as the analog of Tech Debt.  If you introduce a curve into your lines and all subsequent lines follow each other, you'll soon have a yard full of mismatched curves.  This is like introducing tech debt into your code that you just work around but never correct the core problem.  In both scenarios, it's generally preferred to pay back debt as soon as you can.  If your next line is perfectly straight but you have to drift back into the previous one, you've corrected the problem for all future iterations even if you didn't cut as much grass as possible. On the other hand, some debt is acceptable.  If you can tell it's going to rain soon or your software has a hard deadline approaching, it's better to finish the job and accept that not all of the lines are straight.  I'm a fan of the &lt;a href="https://www.dotnetrocks.com/" rel="noopener noreferrer"&gt;.NET Rocks&lt;/a&gt; podcast and one of their tenants is that "shipping is a feature".    &lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;As you might have guessed, mowing the lawn is generally a boring task if you forget to charge your headphones.  If you ever find yourself in a similar situation, think of how you would model that thing with software and then write an article about it.  It seems like a silly exercise but will only improve your skills at modeling real processes in the workplace.&lt;/p&gt;

</description>
      <category>agile</category>
    </item>
    <item>
      <title>The Courteous Consumer </title>
      <dc:creator>Andy McKenna</dc:creator>
      <pubDate>Fri, 30 Aug 2019 16:25:23 +0000</pubDate>
      <link>https://forem.com/dealeron/the-courteous-consumer-kbc</link>
      <guid>https://forem.com/dealeron/the-courteous-consumer-kbc</guid>
      <description>&lt;p&gt;As a developer on the DealerOn Reporting Team, I've gone through a lot of the pain points with importing data from other services. We automatically download data daily for thousands of accounts from some of the largest providers. The classic mistake that someone makes when designing a system like this is to build out a &lt;code&gt;Task&lt;/code&gt; for each report/account and &lt;code&gt;Task.WhenAll&lt;/code&gt;.  Eventually you realize that too many requests could flood the provider so you add in a semaphore and set it to something like 20.  Done and done, right?  &lt;/p&gt;

&lt;p&gt;This is dangerous because for the most part it will work.  You'll get a lot of data and think you're safe until you need to backfill a single account for more than a few days.  You'll watch the logs and after a few days worth you'll notice that your requests time out or have errors that don't make sense.  What's wrong?  &lt;/p&gt;

&lt;p&gt;Most of the large providers implement rate protections on their APIs that will warn a consumer when they're getting close to a limit and outright block them when they cross it.  If you don't know how to recognize and heed these warnings, you'll just end up cursing the provider's name in vain.  In this article, we'll examine how Facebook, Microsoft, and Google tell you when it's time to slow down so that you can be a courteous consumer.&lt;/p&gt;

&lt;h1&gt;
  
  
  Facebook
&lt;/h1&gt;

&lt;p&gt;Facebook's Graph API has many different &lt;a href="https://developers.facebook.com/docs/graph-api/overview/rate-limiting"&gt;throttling limits&lt;/a&gt; based on what you're accessing.  Depending on when you're reading this, Facebook is also phasing in a change to their rate limiting called Business Use Case Usage.  The basic premise of the old version and the new version is that every response to a call you make will have a header that tells you the current status of the account you are accessing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;ad&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;account&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;acc_id_util_pct&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;9.67&lt;/span&gt;     &lt;span class="c1"&gt;//Percentage of calls made for this ad account.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When that percentage hits 100, further calls will result in an error.  We decided to pause at 90% because we could have multiple requests running at once.  This older version will reset to 0% after 5 minutes. The new response header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;business&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="k"&gt;case&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;usage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;{business-id}&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;[{&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;call_count&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;//Percentage of calls made for this business ad account.&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;total_cputime&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     &lt;span class="c1"&gt;//Percentage of the total cpu time has been used.&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;total_time&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;//Percentage of the total time has been used.&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ads_insights&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                      &lt;span class="c1"&gt;//Type of rate limit logic being applied.&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;estimated_time_to_regain_access&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;       &lt;span class="c1"&gt;//Time in minutes to resume calls.&lt;/span&gt;
  &lt;span class="p"&gt;}]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If any of the first 3 numbers hit 100%, the account is restricted.  Facebook API v3.3 will use either response depending on what endpoint you are accessing.  Version 4.0 will just be the Business Use Case Usage style.  &lt;/p&gt;

&lt;p&gt;We check every response and use a &lt;code&gt;Dictionary&lt;/code&gt; of &lt;code&gt;SemaphoreSlims&lt;/code&gt; so that we can lock individual accounts until they are ready to resume.  Any other calls that we build for that account will check &lt;code&gt;_accountLocks&lt;/code&gt; and find it already in use.  The method below only handles the old style and will need to be updated for the new version (including incorporating the nice &lt;code&gt;estimated_time_to_regain_access&lt;/code&gt; field).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="nf"&gt;CheckResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="n"&gt;HttpResponseMessage&lt;/span&gt; &lt;span class="n"&gt;httpResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
           &lt;span class="n"&gt;BaseFacebookResponse&lt;/span&gt; &lt;span class="n"&gt;dataResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;accountLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonConvert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeserializeObject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FacebookAccountLimit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
                     &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetHeaderValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-ad-account-usage"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;appLimit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;JsonConvert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DeserializeObject&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FacebookAccountLimit&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
                 &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;httpResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetHeaderValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"x-app-usage"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;??&lt;/span&gt; &lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;appLimit&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;appLimit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Usage&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_USAGE_PERCENT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_appLock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;APP_SLEEP_MINUTES&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;_appLock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountLimit&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; 
   &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;accountLimit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Usage&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;WARNING_USAGE_PERCENT&lt;/span&gt; 
   &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;accountLimit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Usage&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;MAX_USAGE_PERCENT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ACCOUNT_SLOWDOWN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;accountLimit&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt; 
   &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;accountLimit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Usage&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MAX_USAGE_PERCENT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_accountLocks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;WaitAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ACCOUNT_SLEEP_MINUTES&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;_accountLocks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$@"Facebook API Error: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;dataResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                           URL: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                           Full Error: &lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="s"&gt;                           &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;JsonConvert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SerializeObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dataResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Google Adwords
&lt;/h1&gt;

&lt;p&gt;Adwords' &lt;a href="https://developers.google.com/adwords/api/docs/guides/rate-limits"&gt;rate limiting&lt;/a&gt; is based on watching for the &lt;code&gt;RateExceededError&lt;/code&gt; in the response.&lt;/p&gt;

&lt;p&gt;We're using the &lt;code&gt;googleads-dotnet-lib&lt;/code&gt; nuget package which has an example in the source on how to handle &lt;code&gt;RateExceededError&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//make your request&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;AdWordsApiException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Handle API errors.&lt;/span&gt;
  &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;innerException&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ApiException&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ApiException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;innerException&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s"&gt;"Failed to retrieve ApiError. See inner exception for more details."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ApiError&lt;/span&gt; &lt;span class="n"&gt;apiError&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;innerException&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!(&lt;/span&gt;&lt;span class="n"&gt;apiError&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;RateExceededError&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Rethrow any errors other than RateExceededError.&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Handle rate exceeded errors.&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rateExceededError&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RateExceededError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;apiError&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;//lock out other requests for this account&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_accountLocks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;WaitAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rateExceededError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;retryAfterSeconds&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;_accountLocks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;//we should retry this request now&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;On the other hand, the best offense is a good defense and we typically avoid the RateExceededError by using a semaphore to limit ourselves to 10 open Adwords report calls at any one time.&lt;/p&gt;

&lt;h1&gt;
  
  
  Google Analytics
&lt;/h1&gt;

&lt;p&gt;This product is made by the same company as Adwords so the APIs must be similar, right?  Once we're all done laughing, take a look at the &lt;a href="https://developers.google.com/analytics/devguides/reporting/mcf/v3/limits-quotas"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Each account has a hard daily limit as well as a real time Queries Per Second limit.  You can detect the daily or QPS limit getting exceeded by the status code 403 or 429 but even better would be limiting your requests so that you don't hit it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MakeRateLimitedRequest&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;Func&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_semaphore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WaitAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;Task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_timeSpan&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ContinueWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_semaphore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Release&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;action&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If your semaphore has 10 slots and the timespan is 1 second, you shouldn't be able to make more than 10 requests per second.  If you're handling multiple accounts, you'll want a dictionary of semaphores so that each can run at it's maximum allotment.&lt;/p&gt;

&lt;h1&gt;
  
  
  Bing Ads
&lt;/h1&gt;

&lt;p&gt;Microsoft has probably the &lt;a href="https://docs.microsoft.com/en-us/advertising/hotel-service/throttle-requests"&gt;simplest method&lt;/a&gt; to detect and handle throttled requests:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To ensure resources for everyone, the API limits the number of requests a customer ID may make per minute. The limit is not documented and is subject to change.  If you exceed the request per minute limit, the API returns HTTP status code 429. When you receive status code 429, you must wait 60 seconds before resubmitting the request.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A modified version of our Facebook method will handle this easily enough.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Something that might have come through in each section is that the documentation usually will explain how the provider will limit access to their servers.  This is necessary to prevent a malicious user or someone that accidentally wrote an infinite loop from crashing everything.  No matter what third party data you are accessing, make sure you fully test your code by running it until your rate limiters kick in so you can see them work.  By following some of the suggestions here you'll soon learn to implement rate limiting &lt;em&gt;before&lt;/em&gt; you notice a problem in production.&lt;/p&gt;

</description>
      <category>coding</category>
      <category>advice</category>
    </item>
    <item>
      <title>So you want to work from home…</title>
      <dc:creator>Andy McKenna</dc:creator>
      <pubDate>Fri, 04 Jan 2019 14:01:00 +0000</pubDate>
      <link>https://forem.com/dealeron/so-you-want-to-work-from-home-322k</link>
      <guid>https://forem.com/dealeron/so-you-want-to-work-from-home-322k</guid>
      <description>&lt;p&gt;Anyone who has worked in an office for a significant amount of time has had the same thought at some point, “&lt;em&gt;How great would it be to work from home every day?&lt;/em&gt;” It can be incredibly rewarding as long as you go into it with the right expectations and avoid some classic pitfalls.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ViC2b0yU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7zc2lui0v2zmn898jz2f.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ViC2b0yU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7zc2lui0v2zmn898jz2f.jpeg" alt=""&gt;&lt;/a&gt;Cutting this out of your life is usually the #1 reason to work remotely&lt;/p&gt;

&lt;p&gt;One of the first things you need to consider is if your company is prepared to handle remote workers. Check out our &lt;a href="https://medium.com/p/4430af74b837"&gt;previous article&lt;/a&gt; to see what the company can do to make things easier for everyone. The other major hurdle is if your job absolutely requires you to be on-site. It would be hard to be a construction worker that worked from home, I’d imagine. The Development team at DealerOn really only requires that we have a computer and an internet connection, so let’s dive in!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;This is the Best Decision I’ve Ever Made!&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Let’s start by listing some of the main benefits to working remotely. The carrot comes before the stick.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Commute&lt;/strong&gt;  — the average commute time in the United States is now &lt;a href="https://www.washingtonpost.com/news/wonk/wp/2017/02/22/the-american-commute-is-worse-today-than-its-ever-been/?noredirect=on&amp;amp;utm_term=.54fea048245f"&gt;26.4&lt;/a&gt; minutes each way. In larger/dense urban areas this can be as high as 60–90 minutes. A long commute can be stressful and doubly so when it’s due to traffic that can be frustrating and unpredictable. For most of the country that time is wasted by driving your own car due to lack of public transportation options. I’m sure you’d agree that you could find something better to do with an extra hour or three in your day!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comfort&lt;/strong&gt;  — even the nicest office probably isn’t as comfortable to you as your own place. You’re in sole control of the thermostat, you can blast your music as loud as you want, and no one will make a face if you skip the shoes all day. You also have the final say in your furniture options. Do make sure you still get dressed for work though. Nobody wants to see you on camera in your robe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Peace and Quiet &lt;/strong&gt; — some offices can be noisy and chaotic. Background conversations, phones, and printers can all provide a din that hangs over you like a cloud, sapping your concentration. One solution is to wear headphones to drown everything else out but nothing beats the silence of your own private space.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multitasking/Flexibility&lt;/strong&gt; —if you’re home all day it becomes much easier to stay on top of certain chores. Laundry and dishes are two tasks that can be started before work and then finished on your lunch break. You’re already there if an important package needs a signature or is at risk of being stolen. If you have pets, they’ll love just having you around and the more frequent bathroom breaks. You can also be there if you have a child staying home sick and still be productive, depending on their age.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What Could Go Wrong?&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Telecommuting sounds amazing, why aren’t we all doing this right now?!?! Besides resistance by management, there are a few reasons why it doesn’t work for everyone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--goXFU8Yb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/413mk44zo83c050w5tma.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--goXFU8Yb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/413mk44zo83c050w5tma.gif" alt=""&gt;&lt;/a&gt;Kids and dogs love to sneak in when you’re on-camera&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Distractions&lt;/strong&gt;  — in the multitasking section, I made sure to only mention before/after work and lunch breaks because it can also be easy to get distracted by all of the things to do in your house. I’ve got an amazing library of video games, movies, and TV shows. I also have more housework than I have desire to do said housework at night. Don’t fall into the trap of neglecting your work for home projects and entertainment just because it’s so convenient now.&lt;/p&gt;

&lt;p&gt;Children are another giant distraction that can tank your concentration. It’s easy to think of working from home as a great way to save money on babysitting but kids under a certain age need constant supervision and mental stimulation. They don’t understand that you’re “at work” and you don’t have time to hear a full recap of the latest episode of PJ Masks. If they’re usually in school or daycare but have to stay home sick it can be less of an issue because they’ll spend most of the day in bed. There’s no hard and fast rule on what the minimum age is for kids to stay home but understand that you have work to do and be able to entertain themselves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Communication&lt;/strong&gt; —if your coworkers are all in the same office and you work remotely, it can be hard to have the same quality of communication as your teammates. We covered some of the bigger roadblocks in the previous article, like using Slack and video conferencing, but just having the tools isn’t enough. Since your manager/teammates can’t physically see you at any given time, it’s easier for them to forget about you or not know what you’re working on. Combat that by being almost overly-communicative. Be extra talkative in the chat rooms, direct message teammates with questions, have your camera on whenever possible for the meetings. You’re a member of the team so make sure you’re constantly accessible and in the conversation (during work hours).&lt;/p&gt;

&lt;p&gt;Another wrinkle that can affect communication with your team is time zone differences. Maybe you’re working remotely in another part of the country or on the other side of the planet. Your two options are to adjust to the home office times or work your local time. If you’re working your own times, make sure to get things you need from your teammates during the overlap and save items you can do during your alone time for the offset hours. Nothing is more frustrating than needing someone else to answer something but knowing they won’t be available for half a day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Loneliness&lt;/strong&gt;  — this one affects people differently but it’s worth noting because many new remote workers don’t consider it before making the change. When you’re working in an office, there’s almost always someone to talk to about work or just what’s going on in the news or pop culture. Too much of this might be why you want to telecommute in the first place. The problem is that too much isolation can have negative effects on you. If your company isn’t using chat/video conferencing, you might go an entire day without saying anything out loud. Make sure to break this up by meeting someone for lunch or doing social things after hours. It’s hard to know if this will be an issue for you until you go through it. Don’t brush this one off, there’s a reason solitary confinement is seen as a punishment in prison.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Blurred lines&lt;/strong&gt;  — when your home and office are the same place, it can be easy to never “leave” work. Having a room dedicated as &lt;em&gt;The Office&lt;/em&gt; can help (and might be a nice tax write off**) but even if you don’t have space for that, just holding yourself to normal working hours is important. Take a break every hour or two to get out of your seat and stretch your legs, get away from your desk to eat lunch, and know when you’re going to stop for the night. Avoiding mental burnout is better for your health than the short term gains of the extra work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I Still Want to Do This?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n4kgzNj4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6mwhb13i5o62qfw64k0u.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n4kgzNj4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6mwhb13i5o62qfw64k0u.gif" alt=""&gt;&lt;/a&gt;I know, it’s not an easy decision&lt;/p&gt;

&lt;p&gt;Working from home can be a pretty sweet gig if your company is open to it and you know what you’re getting in to. All of the complications that I listed are manageable as long as you set some ground rules for yourself. If you’re still unsure, try working from home one or two days a week as a trial. Once you adjust, the hardest obstacle you’ll face is not responding with a &lt;a href="https://www.youtube.com/watch?v=7SS24_CgwEM"&gt;Nelson Muntz&lt;/a&gt; image anytime somebody complains about their commute.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;** Consult a real life CPA on how to deduct a home office from your taxes&lt;/em&gt;&lt;/p&gt;




</description>
      <category>officeculture</category>
      <category>telecommuting</category>
      <category>remoteworking</category>
    </item>
    <item>
      <title>Remote Workers Are Not That Far Away</title>
      <dc:creator>Andy McKenna</dc:creator>
      <pubDate>Fri, 28 Dec 2018 14:01:00 +0000</pubDate>
      <link>https://forem.com/dealeron/remote-workers-are-not-that-far-away-57fk</link>
      <guid>https://forem.com/dealeron/remote-workers-are-not-that-far-away-57fk</guid>
      <description>&lt;p&gt;As your company grows, you might find yourself struggling to fill your open positions in the local job market. If you already have offices in other regions, hiring remote employees that work out of those offices can be a huge win. The key to making it work is having the right &lt;em&gt;culture&lt;/em&gt; and &lt;em&gt;communication structure&lt;/em&gt;. I’m a developer in DealerOn’s Grand Rapids, MI office while the majority of my team is based in the Maryland home office. I’ll give an overview of what steps DealerOn has taken as a company to set the stage and soon we’ll look at what it takes to be a remote worker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ynI1Oj1S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/yfewq4vz76bt7pfypmbz.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ynI1Oj1S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/yfewq4vz76bt7pfypmbz.jpeg" alt=""&gt;&lt;/a&gt;DealerOn has satellite offices in Texas and Michigan as well as individual remote workers all over the world.&lt;/p&gt;

&lt;h3&gt;
  
  
  Embrace the chat client
&lt;/h3&gt;

&lt;p&gt;A big hurdle that remote workers struggle with is feeling like they’re out of the loop. They’re not there for any of the informal discussions that happen at people’s desks or impromptu meetings that can pop up while a problem is being solved. The old adage stills applies here — &lt;em&gt;out of sight, out of mind&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Fortunately, you can preempt this by integrating a chat client like &lt;a href="https://slack.com/"&gt;Slack&lt;/a&gt; into your day-to-day operations. Slack moves your discussions and collaboration to a digital audience rather than just to who is in earshot at the time. It also removes the necessity for someone to physically be there to answer a question or offer feedback. If your employees grow accustomed to having discussions in a team specific chat room then all of the team is on equal footing. Remote workers can observe and comment without the friction or interruption of a phone call.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Slack moves your discussions and collaboration to a digital audience rather than just to who is in earshot at the time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most people know that when they see two people talking, they should not interrupt and should either wait for the conversation to end or come back later. The problem here is this is inefficient for everyone involved. If you really need an answer you’ll be put in the tough spot of feeling terrible about interrupting or you’re stuck and need to find something else to do while you wait. With Slack, that 1 on 1 conversation could be one of the 4 or 5 I’m having simultaneously. If someone needs to know when my current ticket will be completed, I can give them a quick answer without the social stigma of someone “butting in.” This might sound intimidating but it’s actually very freeing. If someone walks up to your desk, they’re demanding your attention at that moment. If someone sends me a direct message on Slack, I can finish my thought before addressing them.&lt;/p&gt;

&lt;p&gt;Another benefit is you’ll naturally reduce the number of meetings where you think to yourself, “this could have been replaced with an email.” A team lead can say something in their channel with an @ tag and everyone in that list will get a notification. If someone has followups to that, it’s instant and visible to everyone.&lt;/p&gt;

&lt;p&gt;At this point you might be thinking, “We already have email, isn’t that good enough?” Email and chat are two tools on your workbench but they aren’t interchangeable. Email is more formal by having a subject line, usually a signature, and each one is weighted the same in your inbox. Chat is typically informal and I can add a 👍response with one click and nothing added to your inbox. There’s also no need to curate your chat, whereas my blood pressure rises proportionally with the number I see in Outlook.&lt;/p&gt;

&lt;h3&gt;
  
  
  Video Conferencing
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NHDu0xG9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1ssppv6a0qw580bhm4o9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NHDu0xG9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/1ssppv6a0qw580bhm4o9.jpeg" alt=""&gt;&lt;/a&gt;A stock image of photogenic people&lt;/p&gt;

&lt;p&gt;While chat can cut down on your number of meetings, you’ll never get rid of them entirely. Sometimes you need to get people in a room and discuss something face to face. This is where the difference between local and remote workers is apparent, but there are things you can do to smooth out the burrs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#1: Use video if at all possible&lt;/strong&gt;  — Humans are visual creatures and it’s easier to understand someone if you can see the facial cues that go along with what they’re saying. It’s also easier sometimes to just nod or throw a thumbs up without breaking the speaker’s momentum.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#2: Rock the mic(s) &lt;/strong&gt;— Each conference room involved will need a different microphone strategy based on the size of the room. A good starting point is a conferencing phone that includes wireless satellite mics. These can be placed around the table but only pick up people actually sitting at the table. Anyone standing away from the table will sound like one of the adults from the Peanuts cartoons. Pass the mic to them or look at adding hanging microphones around the perimeter of your room. On-table mics are also especially good at picking up taps and bumps so aspiring drummers will have to keep it in check.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;#3: Limit the background noise&lt;/strong&gt; —If the remote workers are dialing in from their desk, they can bring a lot of extra noise whenever they unmute their microphone. A headset/mic combo helps but doesn’t totally eliminate it. One solution that we built into our brand new Michigan office is the inclusion of single person “phone booths.” These soundproofed rooms are great to take your laptop into when you have a meeting with the team and you want to eliminate any background noise. It’s also perfect for a 1-on-1 with your boss when you don’t want to broadcast your business to everyone else.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FAKc3SO7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/11vu0xy0or8arr1waau8.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FAKc3SO7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/11vu0xy0or8arr1waau8.jpeg" alt=""&gt;&lt;/a&gt;Our new Michigan office is being built with private video conferencing in mind&lt;/p&gt;

&lt;h3&gt;
  
  
  Change is hard
&lt;/h3&gt;

&lt;p&gt;If you’ve read through all this and still worry that this sounds like a big disruption, fear not! Everything gets easier with repetition. Dialing in to a web conference, learning mic discipline, and adjusting to people working in different time zones will all become second nature after a while. You can also start with a few fake meetings so that everyone can play with their sound levels and get configured without the pressure of trying to actually discuss something work related.&lt;/p&gt;

&lt;p&gt;An extra benefit of this setup and preparation is that you’re now prepared if any of your local employees need to work from home. Remote workers can be a huge asset to your team as long as you properly include them. They still can’t participate in the company potluck but I’m confident a research team exists that is focusing on making that a reality.&lt;/p&gt;




</description>
      <category>officeculture</category>
      <category>telecommuting</category>
      <category>remoteworking</category>
    </item>
  </channel>
</rss>
