<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Cooking Healthy Code</title>
    <description>This blog is about my three passions in life: Food, Health &amp; Fitness, and writing great software.  If you are into the same stuff, then please feel free to leave a comment or two.
</description>
    <link>http://www.jefferydurand.com/</link>
    <atom:link href="http://www.jefferydurand.com/feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Tue, 10 Aug 2021 17:45:49 +0000</pubDate>
    <lastBuildDate>Tue, 10 Aug 2021 17:45:49 +0000</lastBuildDate>
    <generator>Jekyll v3.9.0</generator>
    
      <item>
        <title>Leaky boat</title>
        <description>&lt;p&gt;&lt;img src=&quot;http://www.jefferydurand.com/assets/it_org_chart.jpg&quot; alt=&quot;Organizational Chart&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Software systems mirror a leaky boat in many ways.  Most folks in the IT organization are trying to reduce and remove the risk that the boat will sink and all its occupants will meet their demise (software fails to meet objectives).&lt;/p&gt;

&lt;p&gt;I imagine this analogy has been used before in other contexts, or perhaps even this exact one, but I’m a bit too lazy to go digging through the interwebs for prior work. So for the sake of this article, let’s just assume this is the first time you’ve heard such an analogy.&lt;/p&gt;

&lt;p&gt;After engaging in some debates and discussions with colleagues about the proper engineering levels and organizational design, I felt inspired to share my thoughts on the subject openly and invite criticism of the viewpoint.&lt;/p&gt;

&lt;h2 id=&quot;tldr&quot;&gt;TL;DR&lt;/h2&gt;

&lt;p&gt;Technical organizations may want to consider risk assessment as a primary driver of the capital allocation process alongside project and team design.  Given a proper risk profile one can design financial guardrails which can be used to inform organizational composition and reduce the impact of risk factors.&lt;/p&gt;

&lt;h2 id=&quot;deeper-dive&quot;&gt;Deeper dive&lt;/h2&gt;

&lt;p&gt;Broadly speaking, IT professionals tend to fall into 1 of 4 groups as illustrated in my highly sophisticated stick figure diagram at the top of this article.  To simplify things, I’m focusing on engineering, because it is the department I have the most experience with.&lt;/p&gt;

&lt;p&gt;While there is a ton of nuance within a particular job role, I’m going to focus on how each group typically views risk and then suggest a portfolio based approach to managing that risk.&lt;/p&gt;

&lt;h3 id=&quot;group-1---it-operations&quot;&gt;Group 1 - IT Operations&lt;/h3&gt;

&lt;p&gt;This group is largely concerned with keeping several software systems running and applying patches and updates.  These folks tend to be concerned about risks that could impact the current revenue stream of the business.  This group typically favors stability and tends to view risk on a system by system basis.  They will often recommend a managed process for rolling out changes to production. This is where software engineers skilled in fixing the root cause can patch the software and reduce the overall workload for the operations team.&lt;/p&gt;

&lt;h3 id=&quot;group-2---software-engineers&quot;&gt;Group 2 - Software Engineers&lt;/h3&gt;

&lt;p&gt;Engineers are typically concerned with risk associated with feature production and release.  Engineers at most organizations are paid to deliver features to end users based on weekly or monthly sprint cycles.  Thus, a typical engineer is concerned with shipping features quickly and reliably while introducing as few defects as possible.  Software engineers tend to view risk at the project level.  This point of view can sometimes lead to local optimization at the expense of the entire system.  For this reason organizations often employ principal engineers to help align engineering efforts across the organization.&lt;/p&gt;

&lt;h3 id=&quot;group-3---principal-engineers&quot;&gt;Group 3 - Principal Engineers&lt;/h3&gt;

&lt;p&gt;Principal engineers typically try to mitigate risk associated with a system-wide inability to change over time.  Because of the multi-system level focus, these folks  typically focus on interface definition and reduction of technical debt as well as the creation of reusable abstractions and frameworks.  A common blind spot many principal engineers have is knowing when to pivot to a new technology given the current one still functions.  The decision isn’t obvious and can have long term consequences which is why it’s useful to have a big picture leader like a CTO to offer guidance.&lt;/p&gt;

&lt;h3 id=&quot;group-4---cto&quot;&gt;Group 4 - CTO&lt;/h3&gt;

&lt;p&gt;These folks tend to view risks from two primary perspectives in my experience. The first is from a personnel point of view.  If staff can’t or won’t support and work on a particular technology stack, the service that stack provides will fall behind competitor solutions.  The second risk a CTO is typically charged with mitigating is the risk of business model disruption via technical superiority.  This type of disruption typically happens on a long time scale and is often the result of organizations failing to assimilate new technologies into existing business processes which opens the door to nimble startups.&lt;/p&gt;

&lt;h2 id=&quot;why-talk-about-risk-at-all-&quot;&gt;Why talk about risk at all ?&lt;/h2&gt;

&lt;p&gt;The reason I’m choosing to talk about risk in this article is to suggest that perhaps it makes sense to think about staffing an IT organization from a risk point of view in addition to the more typical project based point of view.&lt;/p&gt;

&lt;p&gt;In modern portfolio management one decides on target asset allocation percentages based on a risk tolerance and then rebalances the asset allocation to match that risk tolerance.  Labor is an asset that shares many commonalities with other non fungible assets, thus it makes sense to think about it in similar terms.&lt;/p&gt;

&lt;p&gt;When we look at labor as an asset allocation problem where that allocation is weighted by risk, it gives us a flexible framework for introducing organizational change to meet our target allocation without getting into the details of every project or process the asset pool must support.&lt;/p&gt;

&lt;h2 id=&quot;example-labor-asset-allocation&quot;&gt;Example labor asset allocation&lt;/h2&gt;

&lt;p&gt;The following is a very basic example with nice round numbers to illustrate how a company might staff their organization based on this type of asset allocation strategy.  A real analysis would take more time, have more risk factors and have numbers associated with real world conditions.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;factor 1 year&lt;/th&gt;
      &lt;th&gt;frequency&lt;/th&gt;
      &lt;th&gt;impact per incident&lt;/th&gt;
      &lt;th&gt;yearly cost&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;downtime incident&lt;/td&gt;
      &lt;td&gt;6&lt;/td&gt;
      &lt;td&gt;$100,000&lt;/td&gt;
      &lt;td&gt;$600,000&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;delayed feature&lt;/td&gt;
      &lt;td&gt;2.5&lt;/td&gt;
      &lt;td&gt;$200,000&lt;/td&gt;
      &lt;td&gt;$500,000&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;can’t change&lt;/td&gt;
      &lt;td&gt;0.5&lt;/td&gt;
      &lt;td&gt;$600,000&lt;/td&gt;
      &lt;td&gt;$300,000&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;disrupted&lt;/td&gt;
      &lt;td&gt;0.05&lt;/td&gt;
      &lt;td&gt;$5,000,000&lt;/td&gt;
      &lt;td&gt;$250,000&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;Depending on how these numbers change from year to year a company might look to rebalance its labor pool.  For example, if downtime became even more expensive and  frequent, you’d likely want to increase staff levels to mitigate the impact.&lt;/p&gt;

&lt;p&gt;Given our cost analysis we might end up with the following allocation.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;level&lt;/th&gt;
      &lt;th&gt;cost per staff member&lt;/th&gt;
      &lt;th&gt;staff count&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;IT Operations&lt;/td&gt;
      &lt;td&gt;$100,000&lt;/td&gt;
      &lt;td&gt;6&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Software Engineers&lt;/td&gt;
      &lt;td&gt;$125,000&lt;/td&gt;
      &lt;td&gt;4&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Principal Engineers&lt;/td&gt;
      &lt;td&gt;$150,000&lt;/td&gt;
      &lt;td&gt;2&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;CTO&lt;/td&gt;
      &lt;td&gt;$250,000&lt;/td&gt;
      &lt;td&gt;1&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
</description>
        <pubDate>Tue, 10 Aug 2021 16:00:00 +0000</pubDate>
        <link>http://www.jefferydurand.com/management/technology/engineering/2021/08/10/leaky-boat.html</link>
        <guid isPermaLink="true">http://www.jefferydurand.com/management/technology/engineering/2021/08/10/leaky-boat.html</guid>
        
        
        <category>management</category>
        
        <category>technology</category>
        
        <category>engineering</category>
        
      </item>
    
      <item>
        <title>The knowledge gap</title>
        <description>&lt;p&gt;If you are creating software, business processes, or digital content for profit, then you are a member of the knowledge economy and may find this article to be worth your time.&lt;/p&gt;

&lt;p&gt;Knowledge workers can typically be found in the following departments (sorry if I’m forgetting any):&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Human resources&lt;/li&gt;
  &lt;li&gt;Information Technology&lt;/li&gt;
  &lt;li&gt;Data Science &amp;amp; Analytics&lt;/li&gt;
  &lt;li&gt;Executive Team&lt;/li&gt;
  &lt;li&gt;Legal&lt;/li&gt;
  &lt;li&gt;Marketing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given the pervasiveness of knowledge workers in most post-industrial companies, all else being equal, it is reasonable to conclude that companies that hire, develop, and retain the most productive knowledge workers will see improved business metrics over the long term and may ultimately dominate their respective industries.  &lt;/p&gt;

&lt;p&gt;If you buy into this argument, then the rest of the article will likely resonate with you.&lt;/p&gt;

&lt;p&gt;Historical knowledge is useful but insufficient in an ever-changing world.  Career paths regularly pop into and out of existence.  For example, when I first started working as a software developer, there was no such thing as a cloud architect, because compute clouds didn’t exist.  Given a quickly changing digital world, adaptation becomes crucial for individuals, teams, and entire companies.&lt;/p&gt;

&lt;p&gt;Learning is the process that sentient beings employ to acquire the knowledge needed to adapt to new circumstances.  Since adaptation is required, learning is critical.  &lt;/p&gt;

&lt;p&gt;Given how critical learning is to success, one would think that every individual and organization participating in the knowledge economy would optimize their behavior to become the best learners they could be.  In theory, this makes sense, but in practice, I have found there to be a stubbornly persistent learning gap, which ultimately leads to a knowledge gap.  I believe the gaps exist because individuals are never taught how to learn effectively and organizations often don’t prioritize it.&lt;/p&gt;

&lt;h2 id=&quot;the-truth-about-learning&quot;&gt;The truth about learning&lt;/h2&gt;

&lt;p&gt;A recurring truth I’ve seen repeated in almost all the books I’ve read, as well as experienced first hand, is that &lt;strong&gt;learning is slow.  &lt;/strong&gt;There seems to be no getting around this simple fact. Slow learning is a cognitive sciences first principle.&lt;/p&gt;

&lt;p&gt;It’s also a bit intuitive right?  Parents regularly experience this while teaching their children how to read.  If your children are anything like mine, you’d have observed that it took a minimum of 2 years to become a reasonably good reader.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://www.allthingsdistributed.com/&quot;&gt;CTO of AWS&lt;/a&gt;&lt;a href=&quot;https://www.allthingsdistributed.com/&quot;&gt; Werner Vogels&lt;/a&gt; sums it up with the quote, “There is no compression algorithm for experience” and for years I have been telling project managers that the task they are looking to accomplish, “requires 10 years of experience and then a few weeks”.&lt;/p&gt;

&lt;p&gt;The fact that learning can take a long time has a few important repercussions:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Knowledge transfers are largely impossible if performed at the end of knowledge acquisition.&lt;/li&gt;
  &lt;li&gt;The churn of knowledge workers is particularly expensive for companies.&lt;/li&gt;
  &lt;li&gt;Training seminars aren’t learning environments.&lt;/li&gt;
  &lt;li&gt;Small gains in the learning rate can have a huge return on investment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I will address each point in this list in order, saving my favorite topic, learning rate improvement, for last.&lt;/p&gt;

&lt;h3 id=&quot;knowledge-transfers-&quot;&gt;Knowledge Transfers  &lt;/h3&gt;

&lt;p&gt;Knowledge transfers are impossible because knowledge resides in one’s brain and is largely the product of facts and experiences.  While individuals may be able to acquire some facts, they likely won’t be able to experience those facts in the same order or under the same conditions as someone else. &lt;/p&gt;

&lt;p&gt;Poor knowledge sharing at the organizational level is a form of intellectual debt.  Similar to technical, cultural, and fiscal debt, the debt tends to go unnoticed in the beginning and accrue over time.  Individual research is often a high-value activity for exploring possibilities in the short term.  However, failure to build a team around a promising direction can create an unmanageable intellectual debt burden that ends up costing an organization more money in the long run.&lt;/p&gt;

&lt;p&gt;Given knowledge transfers are impossible, workers should shift the narrative and ask their managers what they really want.  It’s likely the manager doesn’t actually want a transfer of knowledge, but to instead just get access to key systems and documents.&lt;/p&gt;

&lt;p&gt;An alternative approach to knowledge transfers that can help address intellectual debt, is to size projects properly and pay down the costs of knowledge transfers on an ongoing basis.  Proper sizing leads to &lt;strong&gt;teams of at least 3&lt;/strong&gt; with some overlap in skill set.  For maximum return on this investment, knowledge workers should be conducting peer reviews of the work being performed.  &lt;/p&gt;

&lt;p&gt;Lastly, teams of knowledge workers should &lt;strong&gt;a&lt;/strong&gt;&lt;strong&gt;ggressively build abstractions&lt;/strong&gt; that reduce the amount an individual needs to know in order to be productive.  For marketing, this might look like a press release template.  For legal this might be standard language around similar contracts and for software developers, this means leveraging pre-built libraries.&lt;/p&gt;

&lt;h2 id=&quot;churn-is-expensive&quot;&gt;Churn is expensive&lt;/h2&gt;

&lt;p&gt;I have written at length on &lt;a href=&quot;http://www.jefferydurand.com/organizational/architecture/2019/08/09/turnover.html&quot;&gt;my blog&lt;/a&gt; about this subject so I won’t bore you here with all the ways that churn happens at a company and will instead focus on the intellectual cost of knowledge worker churn.&lt;/p&gt;

&lt;p&gt;Consider that in order for a knowledge worker to be productive they must become familiar with the general business processes, including all the basics that all workers would have to learn.  Then that same worker added to a new team, must acquire the knowledge to use the technologies favored by that particular team.  After that, a new hire will also need to learn the standards and practices of the team.  Lastly, the person may have to learn the history of the work they are being charged with bringing into the future because without a grasp of history one is doomed to repeat it.&lt;/p&gt;

&lt;p&gt;In practice, I have found that it takes a minimum of 2 months for most knowledge workers to contribute meaningfully to a project and much more time for projects that are more complex.  Learning in a complex project does not scale linearly because you have to learn about each part of the project and the network of connections those parts make.  I’ll leave it up to managers to decide how much churn is acceptable and to employ strategies to reduce it, as that isn’t the central focus of this article.&lt;/p&gt;

&lt;p&gt;In short, knowledge leaves the organization alongside employees; thus, &lt;strong&gt;churn makes organizations dumber.&lt;/strong&gt;&lt;/p&gt;

&lt;h2 id=&quot;training-isnt-learning&quot;&gt;Training isn’t learning&lt;/h2&gt;

&lt;p&gt;Training should not be confused with learning.  In the knowledge professions, this tends to take the role of conferences, 3-day seminars, and lunch and learns.  All three of these things serve an important purpose, but they &lt;strong&gt;won’t enable folks to learn meaningfully. &lt;/strong&gt;In the book &lt;a href=&quot;https://www.amazon.com/Pragmatic-Thinking-Learning-Refactor-Programmers/dp/1934356050/ref=sr_1_1?crid=3P8HY4VIQMH8R&amp;amp;dchild=1&amp;amp;keywords=pragmatic+thinking+and+learning&amp;amp;qid=1609881045&amp;amp;sprefix=pragmatic+think%2Caps%2C159&amp;amp;sr=8-1&quot;&gt;Pragmatic Thinking &amp;amp; Learning&lt;/a&gt; they call this &lt;a href=&quot;https://optimatraining.co.uk/sheep-dip-training/#:~:text=Sheep%20dipping%20refers%20to%20a,implement%20their%20new%20learnt%20skills.&quot;&gt;“sheep dip training”.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I like to think about these types of events as solving the following problems.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Legal compliance&lt;/li&gt;
  &lt;li&gt;Marketing and sales opportunity for sponsors&lt;/li&gt;
  &lt;li&gt;A chance to network with colleagues&lt;/li&gt;
  &lt;li&gt;Initial exposure to new ideas&lt;/li&gt;
  &lt;li&gt;Certification pre-requisite&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Given it solves a number of problems, training is still relevant and useful, it’s just insufficient to truly learn and by extension create knowledge.  In order for an organization to create lasting knowledge, it must prioritize and promote learning as part of the typical workday instead of facilitating sheep dip training.&lt;/p&gt;

&lt;h2 id=&quot;bend-the-curve-to-close-the-gap&quot;&gt;Bend the curve to close the gap&lt;/h2&gt;

&lt;p&gt;Congrats if you are still with me, we are finally going to get into some meaningful steps folks can take to help improve their learning rates.  Improving the learning rate of a team or individual is one of the most productive activities any knowledge worker can engage in.  &lt;/p&gt;

&lt;p&gt;The reason should seem obvious.  Increasing the learning rate allows folks to acquire knowledge more quickly which may help reduce the impact of the previous three issues associated with slow learning.  Additionally, improved learning habits will benefit the individual for their entire career. &lt;/p&gt;

&lt;p&gt;Learning is still going to be slow compared to most things in life, but it can be a bit faster if one&lt;strong&gt; learns how to learn&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;press-pause&quot;&gt;Press pause&lt;/h3&gt;

&lt;p&gt;Folks assume that re-reading and drilling will improve their learning and their ability to retain knowledge.  &lt;a href=&quot;https://www.amazon.com/Make-Stick-Science-Successful-Learning/dp/0674729013/ref=sr_1_1?dchild=1&amp;amp;keywords=make+it+stick&amp;amp;qid=1609881079&amp;amp;sr=8-1&quot;&gt;Research suggests&lt;/a&gt; that in the short term this works, but in the long term, this behavior can decrease one’s ability to retain information.  &lt;/p&gt;

&lt;p&gt;Instead of drilling, folks should &lt;strong&gt;take breaks of varying lengths between study sessions&lt;/strong&gt;. The literature suggests that if one waits a while and then tries to recall what was learned before studying again, retention can be greatly improved. &lt;/p&gt;

&lt;p&gt;I recently started using &lt;a href=&quot;https://www.babbel.com/&quot;&gt;babbel.com&lt;/a&gt; to learn Italian and noticed they employ this technique.  It seems reasonable to assume that a company devoted to teaching languages, would employ the technique, based on evidence of its efficacy.&lt;/p&gt;

&lt;p&gt;What’s interesting is that one need not actually remember the content during the break period to see retention gains.  The act of pausing and then trying to remember is actually enough to offer meaningful improvements over the long term.&lt;/p&gt;

&lt;h3 id=&quot;get-moving&quot;&gt;Get moving &lt;/h3&gt;

&lt;p&gt;Exercise has been shown to improve one’s capacity to absorb information. In the &lt;a href=&quot;https://www.amazon.com/Spark-Revolutionary-Science-Exercise-Brain-ebook/dp/B000SFD21Q/ref=sr_1_6?dchild=1&amp;amp;keywords=spark&amp;amp;qid=1610135743&amp;amp;sr=8-6&quot;&gt;book Spark&lt;/a&gt; the author makes the argument and provides compelling evidence that &lt;strong&gt;exercise can increase neuron growth and help forge new connections&lt;/strong&gt;.  Additionally, students who exercised before an exam performed better than those that didn’t.  &lt;/p&gt;

&lt;p&gt;For this reason, I often go for walks during times when I want to increase my capacity to learn.  The quiet helps me focus and the exercise helps forge the neural connections that are necessary for long-term learning and growth.  I’d encourage others to engage in some form of physical activity often.&lt;/p&gt;

&lt;h3 id=&quot;become-a-teacher&quot;&gt;Become a teacher&lt;/h3&gt;

&lt;p&gt;Teachers tend to be excellent learners.  They understand through trial and error and formal training, how different people learn in different settings which they can use to refine their own learning style.  Additionally, a teacher is almost always required to become a subject matter expert. Moving from novice to expert is a process that many folks never experience, but it is common for educators.  Since becoming an expert is often a goal of learning, teaching can be instructive towards our learning goals.&lt;/p&gt;

&lt;p&gt;In practice, most knowledge workers won’t just quit their job to become a professor, but that doesn’t mean they won’t be presented with opportunities to teach others.  There are many ways we can all learn to be teachers and thus better students.  Some of my favorites include parenting, mentoring colleagues, conducting lunch and learns, and writing blog articles.  In short, &lt;strong&gt;learning to teach helps folks learn to learn&lt;/strong&gt;.&lt;/p&gt;

&lt;h3 id=&quot;write-it-down&quot;&gt;Write it down&lt;/h3&gt;

&lt;p&gt;Writing things down can be used in two ways, and both are effective.  The first is to actively take notes on a notepad.  Handwritten notes may feel inefficient, but it is exactly this &lt;strong&gt;inefficiency that leads to better learning and retention&lt;/strong&gt;.  Handwriting notes is a technique that seems obvious, but most folks don’t do it these days because they adopted the practices of reading on an e-reader, tablet, or phone and conducting meetings via computer with screen recording enable. &lt;/p&gt;

&lt;p&gt;Typing during a zoom meeting is distracting to all the participants and highlighting on an e-book you never go back to doesn’t offer any long term value.  Instead, folks might consider taking purposeful notes in a physical notebook that can be easily referenced at a later date.  One of the great things about taking notes this way is that you can quickly draw a diagram to illustrate complex connections between topics which helps reinforce one’s understanding of the subject.&lt;/p&gt;

&lt;p&gt;The second way we can use writing to help with learning is to simply write a blog article or book report, in the same vein as a college student writing papers as part of their coursework.  The practice of writing should not stop with graduation. Knowledge workers should continue to write about their most interesting projects and thoughts in order to reinforce their own knowledge and help others gain initial exposure to ideas.  Ask yourself this;  who do you think is going to remember more of this article, you or me?  The act of writing is &lt;strong&gt;not just a way to share knowledge, but to acquire it&lt;/strong&gt; as well. &lt;/p&gt;

&lt;h3 id=&quot;difficulty-matters&quot;&gt;Difficulty Matters&lt;/h3&gt;

&lt;p&gt;The authors of &lt;a href=&quot;https://www.amazon.com/Make-Stick-Science-Successful-Learning/dp/0674729013/ref=sr_1_1?dchild=1&amp;amp;keywords=make+it+stick&amp;amp;qid=1609881079&amp;amp;sr=8-1&quot;&gt;Make it Stick&lt;/a&gt;, make the argument that learning should be difficult, which implies that, easy learning is an oxymoron.  Said another way, &lt;strong&gt;if it’s easy you likely haven’t learned very much&lt;/strong&gt;.  The book calls these, “desired difficulties”.  One example the author highlights is how folks were able to recall more of what they learned when the text they were reading was slightly blurry or skewed.  &lt;/p&gt;

&lt;p&gt;The book also discusses how struggling to generate answers to tough test questions produced more long term retention.  It turns out it’s better for teachers to give hard tests, not only in the interest of assessing what the student learned but also in the interest of enabling them to learn in the first place. &lt;/p&gt;

&lt;p&gt;In the workplace, the test might be to apply your newly learned knowledge in a novel way to help solve business problems or field questions from senior leadership.  I always advise anyone looking to advance their knowledge and their career to seek out the most difficult projects they could possibly complete successfully.&lt;/p&gt;

&lt;p&gt;A failure to solve a difficult problem will yield value in the form of learning and if successful will yield value beyond just learning.  Either way, the individual will gain more from a harder challenge than from an easier one.  If managers wish for their reports to learn more, then they should work to increase the difficulty of each subsequent project the team is responsible for.  &lt;/p&gt;

&lt;h3 id=&quot;diversify&quot;&gt;Diversify&lt;/h3&gt;

&lt;p&gt;The book &lt;a href=&quot;https://www.amazon.com/Range-Generalists-Triumph-Specialized-World/dp/0735214484/ref=sr_1_1?dchild=1&amp;amp;keywords=range&amp;amp;qid=1609881213&amp;amp;sr=8-1&quot;&gt;Range&lt;/a&gt; explains, in great detail, how individuals that pursue a wide array of activities, having only minimal overlap, can often produce better learning effects than individuals who are comparatively narrowly focused.  The concept flies contrary to the dogma most of us have heard our entire lives.  From a very early age we are taught to focus, focus, focus and we’ll achieve our goals.  It turns out that this is a false narrative that can lead to successful learning in subjects that benefit from such a style and poor learning in most other areas.&lt;/p&gt;

&lt;p&gt;The reason it can be difficult to learn from a narrow focus is that humans use analogies and concepts from other subject areas to form general mental models.  Generalized mental models tend to stick with folks more than specific models since they are more widely applicable and therefore command more of the individual’s attention.&lt;/p&gt;

&lt;p&gt;In the book &lt;a href=&quot;https://www.amazon.com/gp/product/B003ZK58TA/ref=dbs_a_def_rwt_bibl_vppi_i2&quot;&gt;Where Good Ideas Come From&lt;/a&gt;, Steven Johnson makes the argument that most good ideas come from networks of people. A network of people is by definition more general than a single individual.  It thus seems reasonable that one would want to create teams with diverse perspectives in order to improve both learning and by extension idea generation.  Knowledge workers should look to a broad array of subjects in order to improve their learning, but also to a broad array of people.  &lt;strong&gt;Fields that exclude groups of people will fail to learn as effectively as those that embrace diversity&lt;/strong&gt;.&lt;/p&gt;

&lt;h1 id=&quot;book-recommendations&quot;&gt;Book Recommendations&lt;/h1&gt;

&lt;p&gt;Most of the content in this article was acquired through years of trial and error, lengthy team discussions, and individual study.  If you are looking to up your learning game, I would highly recommend reading the following books and corresponding chapters.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Slack-Getting-Burnout-Busywork-Efficiency/dp/0767907698/ref=sr_1_1?crid=3RLID49Q67P6T&amp;amp;dchild=1&amp;amp;keywords=slack+tom+demarco&amp;amp;qid=1609880991&amp;amp;sprefix=slack+tom+%2Cstripbooks%2C143&amp;amp;sr=8-1#customerReviews&quot;&gt;Slack&lt;/a&gt; by Tom DeMarco: chapter 26 - Where learning happens&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Pragmatic-Thinking-Learning-Refactor-Programmers/dp/1934356050/ref=sr_1_1?crid=3P8HY4VIQMH8R&amp;amp;dchild=1&amp;amp;keywords=pragmatic+thinking+and+learning&amp;amp;qid=1609881045&amp;amp;sprefix=pragmatic+think%2Caps%2C159&amp;amp;sr=8-1&quot;&gt;Pragmatic Thinking &amp;amp; Learning&lt;/a&gt; by Andy Hunt:  chapter 6 - Learn Deliberately&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Make-Stick-Science-Successful-Learning/dp/0674729013/ref=sr_1_1?dchild=1&amp;amp;keywords=make+it+stick&amp;amp;qid=1609881079&amp;amp;sr=8-1&quot;&gt;Make it stick&lt;/a&gt; by Peter C. Brown, Henry L. Roediger III, and Mark A. McDanial&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Range-Generalists-Triumph-Specialized-World/dp/0735214484/ref=sr_1_1?dchild=1&amp;amp;keywords=range&amp;amp;qid=1609881213&amp;amp;sr=8-1&quot;&gt;Range&lt;/a&gt; by David Epstein: Chapter 4 - Learning Fast and Slow&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/Spark-Revolutionary-Science-Exercise-Brain-ebook/dp/B000SFD21Q/ref=sr_1_6?dchild=1&amp;amp;keywords=spark&amp;amp;qid=1610135743&amp;amp;sr=8-6&quot;&gt;Spark&lt;/a&gt; by John J. Ratey MD: Chapter 2 - Learning&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.amazon.com/gp/product/B003ZK58TA/ref=dbs_a_def_rwt_bibl_vppi_i2&quot;&gt;Where Good Ideas Come From&lt;/a&gt; by Steven Johnson&lt;/li&gt;
&lt;/ul&gt;
</description>
        <pubDate>Fri, 15 Jan 2021 16:00:00 +0000</pubDate>
        <link>http://www.jefferydurand.com/learning/management/knowledge/2021/01/15/knowledge-gap.html</link>
        <guid isPermaLink="true">http://www.jefferydurand.com/learning/management/knowledge/2021/01/15/knowledge-gap.html</guid>
        
        
        <category>learning</category>
        
        <category>management</category>
        
        <category>knowledge</category>
        
      </item>
    
      <item>
        <title>The season for code</title>
        <description>&lt;p&gt;It’s currently Dec 24th, 2019 and Santa is getting ready to make his rounds.  While presents and overeating are great, this time of year is also a time for reflection, and with that in mind, I’d like to share my thoughts about software development and empathy and hopefully make a convincing argument that having empathy will make you a better developer.&lt;/p&gt;

&lt;p&gt;Software developers are constantly being tasked with empathizing with others whether they know it or not.  In the workplace there are generally three categories of people, that developers, need to empathize with on any given day.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Themselves&lt;/li&gt;
  &lt;li&gt;Coworkers&lt;/li&gt;
  &lt;li&gt;Customers&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All three of these categories have multiple people functioning in different job roles at different points in their career and existing in the current or future tense.  That last point requires some clarification.  You need to show empathy to these groups in the moment but also take steps that show you are considering the future when making software development decisions.  This may seem obtuse at first, however, entire job functions exist for the sole purpose of aligning current activity with future goals because it can have a large impact on productivity.&lt;/p&gt;

&lt;p&gt;Having a deep sense of empathy, and by extension, a deep sense of responsibility to the people surrounding you, is in my opinion, a pre-condition for doing great work.  Additionally, empathy is foundational to maintaining a collaborative organization.  Without empathy, a culture of shortcuts and blame can quickly take root.  In the simplest of scenarios:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Junior manager sets aggressive deadline (a deadline lacking empathy)&lt;/li&gt;
  &lt;li&gt;Developer writes sloppy code (code lacking empathy)&lt;/li&gt;
  &lt;li&gt;Bug finds its way into production (lack of empathy for customer)&lt;/li&gt;
  &lt;li&gt;Senior manager fires Junior manager (lack of empathy for system as a whole)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If one repeats this pattern enough times, a lack of empathy will quickly devolve into a nightmare work environment.&lt;/p&gt;

&lt;p&gt;As a developer you might be wondering about what you can do to break the sociopathic cycle and introduce more empathy into your codebase.  In the spirit  of Christmas, my gift to my readers, is a list of my favorite things for showing software project empathy.  When I encounter a codebase that does all of the following things, that codebase just screams:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I CARE ABOUT YOU, MYSELF, AND OUR CUSTOMERS&lt;/em&gt;.&lt;/p&gt;

&lt;h1 id=&quot;test-your-code&quot;&gt;Test your code&lt;/h1&gt;

&lt;p&gt;In 2019, this should be obvious but any code that can’t be tested, can’t really be iterated on.  Lack of automated tests is a special kind of negligence. Lack of testing is a very clear indication that you are only thinking about yourself in the present moment.&lt;/p&gt;

&lt;p&gt;That said there are some real challenges to fully testing your code. Testing external services is usually what trips people up the most.  I’ve found that for these situations it’s best to make liberal use of mocks.  At my company &lt;a href=&quot;https://www.agero.com/&quot;&gt;agero&lt;/a&gt; we use the following languages with the following mocking libraries.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Python &lt;a href=&quot;https://docs.python.org/3/library/unittest.mock.html&quot;&gt;unittest.mock&lt;/a&gt;, &lt;a href=&quot;http://docs.getmoto.org/en/latest/&quot;&gt;moto&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Ruby &lt;a href=&quot;https://github.com/rspec/rspec-mocks&quot;&gt;rspec double&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Javascript &lt;a href=&quot;https://sinonjs.org/&quot;&gt;sinon&lt;/a&gt;, &lt;a href=&quot;https://github.com/theKashey/rewiremock&quot;&gt;rewiremock&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Given these great testing libraries, 2020 sure is looking bright.&lt;/p&gt;

&lt;h1 id=&quot;static-code-analysis&quot;&gt;Static code analysis&lt;/h1&gt;

&lt;p&gt;Any modern software shop should be using a number of static code analysis tools.  These tools help keep your codebase clean uniform and reduce security vulnerabilities.  A clean organized codebase implies that the person working on that code is thinking about there own and others future mental health.  Just as too much clutter and disorganization in the physical world causes stress, so too, does clutter and disorganization in the digital world.   These are a few of the tools that have helped folks at my company get a handle on code clutter.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Python &lt;a href=&quot;http://flake8.pycqa.org/en/latest/&quot;&gt;flake8&lt;/a&gt;, &lt;a href=&quot;https://pypi.org/project/black/&quot;&gt;black&lt;/a&gt;, &lt;a href=&quot;https://sonarcloud.io/&quot;&gt;sonarcloud&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Ruby &lt;a href=&quot;https://github.com/rubocop-hq/rubocop&quot;&gt;rubocop&lt;/a&gt;, &lt;a href=&quot;https://codeclimate.com/quality/&quot;&gt;codeclimate&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Javascript &lt;a href=&quot;https://eslint.org/&quot;&gt;eslint&lt;/a&gt;, &lt;a href=&quot;https://www.npmjs.com/package/eslint-config-airbnb&quot;&gt;eslint-airbnb&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;document-your-code&quot;&gt;Document your code&lt;/h1&gt;

&lt;p&gt;This one is simple.  Write comments for your code that clearly explains what it does.  Additionally, for languages like python, you can use inline docstrings that can easily be rendered to html.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;
&lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;My Module&quot;&quot;&quot;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Foo&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;My Class&quot;&quot;&quot;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;My Constructor&quot;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Return a + b&quot;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Along these same lines, use variable names for magic numbers and strings.  For example, the following code requires the reader to understand what the number 25 means.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;day&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calculate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;day&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The following code is a bit longer but clearly explains through variable naming what the number twelve means.&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;CHRISTMAS&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;day&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calculate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;day&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CHRISTMAS&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;write-a-little-less-code&quot;&gt;Write (a little) less code&lt;/h1&gt;

&lt;p&gt;This is tricky and taken to an extreme, writing less code can make your code unreadable, so don’t go too far.  Less code means less to manage and less bugs for others to fix in the future which shows that you are thinking about others as you develop a solution to your current problem.  Three strategies can help you write less code.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Use stable, versioned libraries.&lt;/li&gt;
  &lt;li&gt;Write idiomatic code that takes advantage of the standard library.&lt;/li&gt;
  &lt;li&gt;Use stable managed services instead of rolling your own (using stable managed services also shows empathy to operations folks).&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;example-of-non-idiomatic-ruby&quot;&gt;Example of non-idiomatic ruby&lt;/h4&gt;
&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;square_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;new_list&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;new_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;new_list&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h4 id=&quot;example-of-idiomatic-ruby&quot;&gt;Example of idiomatic ruby&lt;/h4&gt;

&lt;p&gt;This second example is easier to understand and simpler to maintain.&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;square_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;map&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;write-infrastructure-as-code&quot;&gt;Write infrastructure as code&lt;/h1&gt;

&lt;p&gt;Writing your infrastructure as code instead of clicking through screens shows empathy for others, by making it clear, that you realize they are human, and might accidentally change the wrong config setting otherwise.  Coding infrastructure means anyone can look at that configuration, propose changes, and reason about how those changes may impact other systems.&lt;/p&gt;

&lt;p&gt;This practice shows that you care about the mental health of everyone that needs to have a working environment.  Some tools we’ve used at &lt;a href=&quot;https://www.agero.com/&quot;&gt;Agero&lt;/a&gt; to great affect are:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://serverless.com/&quot;&gt;serverless framework&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/helm/helm&quot;&gt;helm&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/cloudformation/&quot;&gt;cloudformation&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;extract-reusable-libraries&quot;&gt;Extract reusable libraries&lt;/h1&gt;

&lt;p&gt;No matter the size of the team, it is a good idea to identify common patterns in your codebase and pull them out of that codebase into an installable, reusable, module.  Every major language comes with some form of package management in order to extend the standard library (npm, maven, rpm, pip…) so the tools for sharing are built in.&lt;/p&gt;

&lt;p&gt;Extra points for open sourcing anything your company allows.  Allowing others to reuse your work shows empathy for another persons time.  By giving someone else work you may have spent weeks perfecting, you are saving them the trouble of doing the same.&lt;/p&gt;

&lt;h1 id=&quot;adhere-to-known-interfaces&quot;&gt;Adhere to known interfaces&lt;/h1&gt;

&lt;p&gt;This is something I am a firm believer in and I believe shows great empathy for the person tasked with consuming your interface.  Put another way; extend and wrap.  Don’t reimplement if an existing standard is already in place.&lt;/p&gt;

&lt;p&gt;If you extend an interface without breaking existing functionality, you remove the burden of someone using your software interface having to write special integrations into their codebase.  Recently, my team wrote a common logger that is now being used across the organization.&lt;/p&gt;

&lt;p&gt;I believe that the reason it was adopted so readily by teams is that the interface remained that of the standard logging library while only changing the output as required by our logging specification.  Had we not created this shared module with a stable known interface, every team would have had to implement the interface individually wasting weeks of time or worse they spec would be ignored altogether.&lt;/p&gt;

&lt;p&gt;As another example, my team is currently developing a solution that we can use to proxy to 3rd party API’s while extending functionality where it makes sense. The result to developers is that they will be able to use their existing tools to interface with our API and we’ll be able to add value as appropriate.&lt;/p&gt;

&lt;h1 id=&quot;cache-like-its-your-money&quot;&gt;Cache like it’s your money&lt;/h1&gt;

&lt;p&gt;Caching is foundational to efficient computer systems.  Efficient computer systems means customers get information faster, systems do less work, causing them to use less energy (empathy for mother nature), and often end up costing less than than uncached systems.  Caches do create an added layer of complexity, that left unaddressed, will reduce your overall empathy score for the system.  But, through clever cache invalidation approaches you can eliminate much of the headache.&lt;/p&gt;

&lt;p&gt;Rails uses &lt;a href=&quot;https://blog.appsignal.com/2018/04/03/russian-doll-caching-in-rails.html&quot;&gt;russian doll caching&lt;/a&gt; to help reduce complexities associated with cache invalidation. Fastly, uses a technique called &lt;a href=&quot;https://docs.fastly.com/en/guides/getting-started-with-surrogate-keys&quot;&gt;surrogate keys&lt;/a&gt; to greatly improve efficiencies with cache invalidation.  Lastly, simple tools like &lt;a href=&quot;https://www.geeksforgeeks.org/memoization-using-decorators-in-python/&quot;&gt;memoization&lt;/a&gt; can help you cache the result of a method call that would otherwise yield the same result.  There is no one size fits all, but cache invalidation strategies are often worth getting right in order to make caching easier to use in your project.&lt;/p&gt;

&lt;h1 id=&quot;mentor-based-programming&quot;&gt;Mentor based programming&lt;/h1&gt;

&lt;p&gt;If you are a more advanced programmer, you can show great empathy for your coworkers by helping them level up.  Selfishly, this will help you in the long run as they will produce better more maintainable code, that you may be tasked with adding too, in the future.  Spreading knowledge is a powerful display of empathy that can help others for the rest of their careers.  Some habits that help cement this philosophy include:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Detailed code reviews&lt;/li&gt;
  &lt;li&gt;Pair programming&lt;/li&gt;
  &lt;li&gt;Open office hours&lt;/li&gt;
  &lt;li&gt;Lunch and learns&lt;/li&gt;
  &lt;li&gt;Book sharing&lt;/li&gt;
  &lt;li&gt;Technical Blogging ;)&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;use-type-hinting&quot;&gt;Use type hinting&lt;/h1&gt;

&lt;p&gt;This is a simple coding technique that applies to Python, PHP, and perhaps a few other scripting languages.  The premise is simple: Types are hinted in your code by annotating variables.  IDE’s and linters can then be used to surface potential data type mismatches in your code.&lt;/p&gt;

&lt;p&gt;Adding type hints to your code doesn’t make the code function any differently, but it does help document your code and make is more useable.  Thus, given type hinting is completely optional, it really is an act of kindness to your fellow developers when you include it. The following is an example of code with and without type hinting.&lt;/p&gt;

&lt;p&gt;As an aside, parsing data to a typed object is often very useful, I’ve found &lt;a href=&quot;https://pydantic-docs.helpmanual.io/&quot;&gt;pydantic&lt;/a&gt; to be extremely helpful in reducing errors associated with parsing JSON documents.&lt;/p&gt;

&lt;p&gt;With hinting&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Hello '&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Without hinting&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Hello '&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;As you can see the difference is minimal, but in larger projects the impact can be huge.&lt;/p&gt;

&lt;h1 id=&quot;named-parameters&quot;&gt;Named parameters&lt;/h1&gt;

&lt;p&gt;Named parameters is similar to type hinting in that it documents your code and allows IDE’s deliver more value, which save the developer time and headaches debugging code and searching through docs.  Again your code will function without named parameters but will be more difficult for future folks to maintain. Named parameters simply means that you should prefer an actual parameter if you know what it’s going to be ahead of time over an array or hash containing similar information.&lt;/p&gt;

&lt;p&gt;In the simplest case I’ll illustrate via a python function.&lt;/p&gt;

&lt;p&gt;With named parameters&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firstname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Hello '&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;firstname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;' '&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastname&lt;/span&gt;


&lt;span class=&quot;c1&quot;&gt;# call this function via
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;firstname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Jeff&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastname&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Durand&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Without named parameters&lt;/p&gt;
&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;firstname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'Hello '&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;firstname&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;' '&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;lastname&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# this function via (no idea what the parameters represent)
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;greeting&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Jeff&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Durand&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A codebase that passes around a bunch of loosely understood data-structures would quickly drive other developers crazy.  This craziness could end in any number of ways, but I’d like to think named parameters helped the next developer looking at it avoid a lifetime of isolation and alcoholism after slowly losing her mind.&lt;/p&gt;

&lt;h1 id=&quot;avoid-long-regular-expressions&quot;&gt;Avoid long Regular expressions&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.codinghorror.com/regex-use-vs-regex-abuse/&quot;&gt;This article says it all&lt;/a&gt;.  Regular expressions can be great when you are looking to filter out a little text or perform some simple validation.  But very quickly they get out of hand and when they do, they are special kind of hell.  They are impossible to reason about and often very difficult to fully test since matching can be nondeterministic. In general pre-filtering and tokenizing strings before running them through a regex, can greatly simplify things and make your code much easier to reason about.&lt;/p&gt;

&lt;h1 id=&quot;use-docker-for-development-environments&quot;&gt;Use docker for development environments&lt;/h1&gt;

&lt;p&gt;I find the usage of docker for development environments to be a very empathetic choice in technologies.  The idea is simple, developers can use pre-baked development environments instead of building one from scratch.  But docker goes a step further than that because it is also a cross platform technology.  I realize it might not be perfect in windows, but it does work, and given you can get docker working, you can generally execute all the required code for a given dev environment.&lt;/p&gt;

&lt;p&gt;Docker makes it clear that you don’t expect another developer to have a full understanding of the entire environment required to run the project.  That understanding and thoughtfulness makes others more productive including yourself.&lt;/p&gt;

&lt;h1 id=&quot;merry-christmas&quot;&gt;Merry Christmas&lt;/h1&gt;

&lt;p&gt;All of these things on my list required empathy and also improve software projects measurably.  So this year, think of someone else for a change!!&lt;/p&gt;

&lt;p&gt;To go fast, go alone. To go far, go together.&lt;/p&gt;

&lt;p&gt;Happy Coding in 2020&lt;/p&gt;
</description>
        <pubDate>Tue, 24 Dec 2019 16:00:00 +0000</pubDate>
        <link>http://www.jefferydurand.com/christmas/python/architecture/empathy/devops/2019/12/24/tis-the-season.html</link>
        <guid isPermaLink="true">http://www.jefferydurand.com/christmas/python/architecture/empathy/devops/2019/12/24/tis-the-season.html</guid>
        
        
        <category>Christmas</category>
        
        <category>python</category>
        
        <category>architecture</category>
        
        <category>empathy</category>
        
        <category>devops</category>
        
      </item>
    
      <item>
        <title>Musical Chairs</title>
        <description>&lt;p&gt;Most managers understand that employee turnover can be disruptive and expensive.  The cost to recruit, train, and provide a competitive wage is typically one of an organizations highest costs in the knowledge economy.  Failure to address employee churn and its many different causes can create a downward spiral.  If left uncorrected, the churn rate keeps increasing until the organization becomes a revolving door of employees.&lt;/p&gt;

&lt;p&gt;First let’s level set; I’m a technologist, software developer, and systems architect.  So it may be valid to ask what business I have commenting on organizational &amp;amp; business issues at all.  One might argue that these discussions are better left for the MBA folks and Accountants. Fair point, let me first lay out my reasoning around writing an article like this at all.&lt;/p&gt;

&lt;h2 id=&quot;conways-law&quot;&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Conway%27s_law&quot;&gt;Conway’s law&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
  &lt;p&gt;organizations which design systems, are constrained to produce designs which are copies of the communication structures of these organizations.&lt;br /&gt;
&lt;em&gt;Melvin Conway&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Melvin was a smart person and very influential in the early years of computer science.  If you buy into Conway’s law (which I do), it’s relatively easy to make the logical leap to the following axiom.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;organizational and technical architectures are inseparable&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;While they are not the same thing, I reason that one simple can’t have a solid technical architecture without a good organizational architecture, if one is producing technologies within the constraints of an organization whose size is greater than that of a single team.&lt;/p&gt;

&lt;p&gt;Thus, I believe that offering my perspective around organizational architecture is not only valuable, but a core part of my responsibility as an aspiring leader in the technology field.&lt;/p&gt;

&lt;h2 id=&quot;types-of-turnover&quot;&gt;Types of turnover&lt;/h2&gt;

&lt;p&gt;I believe their are at least two types of turnover that commonly occur at any organization.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;External&lt;/li&gt;
  &lt;li&gt;Internal (primary focus of this post)&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;external&quot;&gt;External&lt;/h3&gt;

&lt;p&gt;Most folks know what external turnover is, so I won’t spend a ton of time on it.  External turnover is when a team member decides to leave the company for a number of reasons, including but not limited to:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Life changing event&lt;/li&gt;
  &lt;li&gt;Lack of upward mobility&lt;/li&gt;
  &lt;li&gt;Underpaid for level of responsibility&lt;/li&gt;
  &lt;li&gt;Burnout&lt;/li&gt;
  &lt;li&gt;Cultural mismatch leading to infighting&lt;/li&gt;
  &lt;li&gt;Feeling like work doesn’t make a difference in the world&lt;/li&gt;
  &lt;li&gt;Loss of faith in senior leadership&lt;/li&gt;
  &lt;li&gt;Internal Turnover&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example, a life changing event, might include a new child or health issue that needs tending too for example. Burnout is usually caused by unrealistic deadlines and too much routine work, mixed with unhealthy work life balance.  Cultural mismatch, is typically the product of differing philosophies about the value of the product being produced, and the best way to produce it. Lastly, a loss of faith in senior leadership, can lead employees to sense the storm about to hit and trigger their flight reflex.&lt;/p&gt;

&lt;p&gt;Entire books have been written about reducing external turnover, so I won’t bore you with my take on it.  Instead, I’ll focus the rest of this article on Internal turnover, with the goal of explaining what it is, how it can damage a team, and how to reduce it.&lt;/p&gt;

&lt;h3 id=&quot;internal-turnover&quot;&gt;Internal Turnover&lt;/h3&gt;

&lt;p&gt;Internal turnover is primarily attributable to chaotic internal struggles, muddy product vision, or even, too much external turnover. Both internal and external turnover are related and reinforcing.  Teams are broken apart and moved around frequently, as management tries to solve a list of real or imagined problems.  This movement of individuals, makes it difficult for teams to reach peak performance.&lt;/p&gt;

&lt;p&gt;Team forming, and cohesion take a lot of time and effort.  &lt;a href=&quot;https://en.wikipedia.org/wiki/Bruce_Tuckman&quot;&gt;Bruck Tuckman&lt;/a&gt; was a famous psychologist who studied and wrote extensively about group dynamics.  His most notable work, known as &lt;a href=&quot;https://en.wikipedia.org/wiki/Tuckman%27s_stages_of_group_development&quot;&gt;Tuckman’s stages of group development&lt;/a&gt;, identified four key stages of group development that teams typically go through in order to become productive.  The following graphic provides some context around the process.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Note: you can’t skip stages, you must progress linearly.  This means that teams that are constantly changing are constantly being “reset”.&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.jefferydurand.com/assets/diagrams/TCTC-Forming-Storming.png&quot; alt=&quot;GroupDynamics&quot; /&gt;&lt;/p&gt;

&lt;p&gt;In Ray Dalio’s book &lt;a href=&quot;https://www.amazon.com/Principles-Life-Work-Ray-Dalio/dp/1501124021&quot;&gt;Principles&lt;/a&gt; he outlines how important it is for teams to get in sync.  His core principle, states that everyone doesn’t have to agree with one another, but that everyone does need to understand what’s happening and be able to argue for or against it and eventually fall in line when a decision is made.  The relevance of this insight is that if teams are continually changing they can’t get in sync, which leads to teams spending all of their time re-litigating things that have already been decided.&lt;/p&gt;

&lt;p&gt;Both Tuckman and Dalio speak to the idea, that if teams are constantly changing, they can never reach the performance stage and will be doomed to sub-par performance. To make matters worse, a common “remedy” managers often try to apply to improve teams that have not reached the performance stage, is to try and add more humans, either from within the company, or as new hires.  The result is illustrated in the following diagram.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.jefferydurand.com/assets/diagrams/teamturnover-color.png&quot; alt=&quot;GroupDynamicsProcess&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the addition of new team members over time could have the result of keeping the team locked in a constant state of &lt;b&gt;forming&lt;/b&gt;.  When teams are stuck in the earlier stages of development, they are unable to function on their own, and will require much more direction.  If managers are unable to move on to other teams, the organization may fail to scale properly.  Teams that never reach the performance stage, will always need a dedicated hands on manager.  Ideally, managers would be able to manage multiple teams, but may be unable too if teams can’t progress to the performing stage.&lt;/p&gt;

&lt;p&gt;Adding more people to a team that hasn’t reached the performance stage yet, reinforces a concept known as &lt;a href=&quot;https://en.wikipedia.org/wiki/The_Mythical_Man-Month&quot;&gt;The Mythical Man Month&lt;/a&gt;.  The Mythical Man Month states that; adding another person to an already late project will just make it later.&lt;/p&gt;

&lt;p&gt;The notion that managers can add another person and the project will be completed on time, is a hold out from the industrial age.  In the industrial age, adding another worker to a factory production floor would increase output and help fulfill an order that was running late.  In the knowledge economy, this is no longer true, since communication is at the center of all valuable work being done by the team.  Ultimately, when managers add another person, they increase the communication complexity, reset the team dynamic, and thus, slow the overall team production schedule.&lt;/p&gt;

&lt;p&gt;My own take on this subject, is that, years of evolution has conditioned humans to view outsiders as inherently less trustworthy than insiders.  Groups that are frequently having to assimilate new members, have to spend enormous amounts of time building trust, which can slow down the workflow and ultimately lead to even the top performers becoming frustrated.  This frustration will likely lead to top individuals leaving the company, which has widely been documented as an high &lt;a href=&quot;https://www.peoplekeep.com/blog/bid/312123/employee-retention-the-real-cost-of-losing-an-employee&quot;&gt;expense for companies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To restate the core problem simply:&lt;/p&gt;

&lt;p&gt;&lt;b&gt;If teams are changing membership frequently, the team will never reach peak performance, leading to top performers jumping ship, which will doom the organization to a lifetime of substandard results.&lt;/b&gt;&lt;/p&gt;

&lt;p&gt;Hopefully, the following graphic will help illustrate how all this movement can disrupt a teams ability to perform.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.jefferydurand.com/assets/diagrams/team_dynamics.png&quot; alt=&quot;GroupCycleDynamics&quot; /&gt;&lt;/p&gt;

&lt;p&gt;With luck, I’ve convinced you that high &lt;b&gt;internal turnover&lt;/b&gt; is a real thing and when it’s high, it can cost organizations dearly in the form of increased external turnover and sub-par team performance.  If you do not agree with my premise, you may stop reading here, I won’t be offended.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.jefferydurand.com/assets/diagrams/bluepillredpill.png&quot; alt=&quot;HowFar&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you are reading this sentence, I’ll assume you’d like a few pointers on how to limit &lt;b&gt;internal turnover&lt;/b&gt; to help your teams become more productive.  This list isn’t meant to be exhaustive so please feel free to leave a comment, if you think of other ways to reduce internal turnover rates.&lt;/p&gt;

&lt;h2 id=&quot;tips-for-reducing-internal-turnover&quot;&gt;Tips for reducing Internal Turnover&lt;/h2&gt;

&lt;h3 id=&quot;change-projects-not-teams&quot;&gt;Change Projects not teams&lt;/h3&gt;

&lt;p&gt;Often, it is better to let high performing teams take complete ownership of a new project than it is to try and stitch together a new team for a new project.  Reassigning teams will lead to some members of the team having less than perfect skills for the new project.  This should be viewed as a growth opportunity for those individuals and they should be required to learn the new skills.  Iterative learning, is at the core of the agile development process which has been shown to be an effective way to improve team output.  The strategy for success, is for teams to adjust the project requirements and their skillset over time rather adjusting team composition.&lt;/p&gt;

&lt;p&gt;The core principle is that it’s easier and thus more cost effective to learn new skills, and assimilate them, than it is to assimilate new team members.  Learning new skills is a win for everyone at the company. Conversely, changing teams, and as a result, being forced to go through the stages of group development again is costly and time consuming with no direct payoff for the employee or the organization.&lt;/p&gt;

&lt;p&gt;Tasking a stable team with a new project is much preferred, to breaking up the team, but reassignments can also be disruptive if done too often and without solid reasoning.  Moving from half finished project to half finished project can eat up large chunks of a teams time and make them feel like they are never really adding value to the organization.  In my experience, most projects have periods of lesser activity; if possible, use slower periods to switch projects if possible as it will be less disruptive and deflating to team moral.&lt;/p&gt;

&lt;h3 id=&quot;hire-learning-generalists-in-favor-of-specialists&quot;&gt;Hire learning generalists in favor of specialists&lt;/h3&gt;

&lt;p&gt;Learning generalists can complete many different tasks depending on what the project calls for. They are usually inquisitive by nature, which allows them to quickly pick up skills they might be lacking.  This makes it easy to task a team of generalists with a new project, removing the need to break teams apart in the first place, as project requirements change.&lt;/p&gt;

&lt;p&gt;Generalists will likely have to learn less overall than a team of specialists, since good enough knowledge of a subject is often good enough.  Secondly, because generalists have a wide range of knowledge they will have many common interests with one and other, which will help them socialize and form tighter bonds with each other more quickly (birds of a feather flock together).&lt;/p&gt;

&lt;p&gt;Many of today’s tasks in the workplace are tasks of integration, not invention.  These types of tasks require folks to process a large amount of information, socialize it to others, and integrate it into a complete picture.  Generalists tend to be much better at this, as they have a wide palette from which to paint their solutions from.&lt;/p&gt;

&lt;p&gt;Obviously, if you are trying to solve a very specific problem, then it may be worth hiring a specialist but I would advise all hiring managers to consider if a specialist is really needed or if it makes more sense to hire a generalist that could learn to specialize in the subject area of interest.&lt;/p&gt;

&lt;h3 id=&quot;hire-entire-teams&quot;&gt;Hire entire teams&lt;/h3&gt;

&lt;p&gt;If there is a project that demands a specific sized team, try to hire everyone for the project or form the team in one round of hiring.  Interview for all positions simultaneously and try to onboard the team as a unit to avoid being reset when each new member joins.&lt;/p&gt;

&lt;p&gt;Hiring entire teams can be a great strategy for organizations with the resources to purchase smaller, high preforming, undercapitalized startups.  Often the team that makes up a startup is of much higher value than the market share of that same startup.  If done correctly, large organizations can jumpstart their team building efforts by purchasing a company.  It’s a nice bonus if the company can provide a symbiotic product offering, but that may not even be required if the price is right.&lt;/p&gt;

&lt;p&gt;Be careful when purchasing a small startup to make sure a majority of people on the team are on board with the purchase and willing to work for the new company.  If the new team is combative with the parent company, this strategy could be more trouble than it is worth.&lt;/p&gt;

&lt;h3 id=&quot;avoid-mixing-outsourced-with-in-sourced&quot;&gt;Avoid mixing outsourced with in-sourced&lt;/h3&gt;

&lt;p&gt;At a very basic level, team members should have aligned incentives.   Without aligned incentives it can be extremely difficult to get team members to work together cohesively.  This can be hard or impossible to achieve in an environment where there are two different classes of employee, with different incentive structures, and different bosses; working on the same project.&lt;/p&gt;

&lt;p&gt;Often in-sourced employees are more permanent than outsourced.  This means that in-sourced employees tend to create solutions that not only meet today’s requirements but also allow for change and the meeting of future requirements.&lt;/p&gt;

&lt;p&gt;Outsourced employees are often tasked with spending only the minimum amount of hours to complete the task they have been assigned, after which, they can be assigned to a new team, project, or even another company altogether.  The turnover of the outsourced employees can throw off they dynamic of the in-sourced employees by causing them to constantly be adapting to new personality types and skill levels.&lt;/p&gt;

&lt;p&gt;The tension created from this dynamic can end up leading great employees to seek teams that don’t have outsourced team members and when that’s not available they may eventually leave the organization altogether.  The result is typically, increased turnover and lower overall productivity.&lt;/p&gt;
</description>
        <pubDate>Fri, 09 Aug 2019 16:00:00 +0000</pubDate>
        <link>http://www.jefferydurand.com/organizational/architecture/2019/08/09/turnover.html</link>
        <guid isPermaLink="true">http://www.jefferydurand.com/organizational/architecture/2019/08/09/turnover.html</guid>
        
        
        <category>Organizational</category>
        
        <category>Architecture</category>
        
      </item>
    
      <item>
        <title>How to be a mentor</title>
        <description>&lt;p&gt;I use this blog to organize my thoughts about my professional work, with the intent of helping others organize their thoughts, if they find themselves in a similar position.  While much of my work is related to writing code and connecting the technology dots, another large and growing part of my work is centered around technical and career mentorship.&lt;/p&gt;

&lt;p&gt;This article will focus on the mentorship part of my life.  First I’ll define what I believe the role of a mentor is in a technical setting (may be applicable to other industries). Second, I’ll attempt to impart some wisdom I’ve acquired during my years mentoring, with the hope that future mentors will have a fruitful experience with their mentees.&lt;/p&gt;

&lt;h2 id=&quot;the-role-of-a-mentor&quot;&gt;The role of a mentor&lt;/h2&gt;

&lt;p&gt;In my opinion the role of a mentor is to provide the following guidance - in order of importance:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Challenge the mentee’s thought process without breaking their spirit or making them feel stupid for not considering the alternative points of view.&lt;/li&gt;
  &lt;li&gt;Offer information recommendations (books and tutorials) and provide opportunities to learn new skills.&lt;/li&gt;
  &lt;li&gt;Introduce them to other people that may help shape their career.&lt;/li&gt;
  &lt;li&gt;Bring the mentee back from the metaphorical cliff’s edge just before they are about to jump (convince them not to quit).&lt;/li&gt;
  &lt;li&gt;Ask subject relevant questions, that help the mentee consider 2nd and 3rd or effects of the choices they are making.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I hope those bullet points are self explanatory, but if they are not hopefully the rest of the article will offer more practical advice.&lt;/p&gt;

&lt;h2 id=&quot;dont-give-them-the-answer&quot;&gt;Don’t give them the answer&lt;/h2&gt;

&lt;p&gt;Beginner mentor’s often make the mistake of thinking they are a dictionary to be used as a way for mentees to lookup the answer to a specifc question.  Don’t make the mistake of acting like a far inferior version of google or stack-exchange.  First you will never keep pace with the vast knowledge the internet can provide, so don’t even try.  Second, even if you had a photographic memory you would be doing your mentee a disservice by giving them the answer.&lt;/p&gt;

&lt;p&gt;I firmly believe that giving someone the answer to the question is a form of &lt;strong&gt;&lt;em&gt;intellecutal theft&lt;/em&gt;&lt;/strong&gt;.  Robbing someone of the pride and joy of struggling through, and ultimately solving a hard problem, is antithetical to your role as Mentor.  Try as hard as you can to avoid this trap.&lt;/p&gt;

&lt;h3 id=&quot;what-you-should-do-instead&quot;&gt;What you should do instead&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Guide them towards the answer by asking them questions they didn’t think to ask.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For example a mentee may ask if it’s worth writing a unit test for a specific piece of code slated for a production release.  You could respond by saying, “yes you always write unit tests, no exceptions”.  The outcome is likely, that a test gets written and the mentee is slightly better at writing unit tests.&lt;/p&gt;

&lt;p&gt;By telling them yes and giving them the answer, you have actually done them a slight disservice.  Giving them the answer inhibits their ability to forge the neural pathway themselves by discoving &lt;em&gt;the why&lt;/em&gt; on their own. If instead, you asked a question that explored what would happen if they didn’t write a test for the code, the mentee will typically convince themselves of the idea and cement the reasoning required to forge the idea into a habbit and eventual practice.&lt;/p&gt;

&lt;p&gt;Not giving the answer, takes in immense amount of patience, from both parties. It’s important that both the mentor and the mentee understand the reasoning behind taking the long way around, to avoid frustration and resentment.&lt;/p&gt;

&lt;p&gt;The movie inception is spot on, with this concept, in my opinion.  It’s very hard to convince an individual of an idea.  It’s often more effective to guide a person towards believing they had the idea all on their own.&lt;/p&gt;

&lt;h2 id=&quot;be-less-available&quot;&gt;Be less available&lt;/h2&gt;

&lt;p&gt;It’s tempting to think that always being their for your mentee at all hours of the day will provide them with the best, quickest path towards growth, and enlightenment.  However, I’ve found this to be completely untrue in practice.  If you are always available, you run a very high risk of creating a codependent mentee, where they are unable to complete work, without your input.  This is unhealthy and ultimately won’t serve to enrich the career of either party.&lt;/p&gt;

&lt;h3 id=&quot;what-you-should-do-instead-1&quot;&gt;What you should do instead&lt;/h3&gt;

&lt;p&gt;If the question being asked is simple, and your mentee is using you like a dictionary, I advise just ignoring them for a short period. An hour or so is usually sufficient, then follow up with them and ask if they still need help.  If they still need help, guide them with pointed questions, and perhaps a link to some reference material you have handy.&lt;/p&gt;

&lt;p&gt;If the question is non-trival and involves more thought, encourage them to schedule something for the “following day” on your calendar. This will give them time to think about questions they want to ask and ponder possible solution and literally, sleep on it. This process of sleeping on it, is really important.  What often ends up happening is that, the next day meeting ends up taking the tone of an informed discussion, where the mentee is just verifying their already cohesive thoughts about the orignal question, rather than trying to answer the orignal question.&lt;/p&gt;

&lt;p&gt;This process is valuable because it offers the mentor an opportunity to learn something from the mentee and vice versa.  In general, discussion is greater than lecture, in the context of your relationship with your mentee.&lt;/p&gt;

&lt;h2 id=&quot;discuss-your-history&quot;&gt;Discuss your history&lt;/h2&gt;

&lt;p&gt;Mentor’s often have more domain experience than mentee’s.  Openly discussing this experience can provide some great direction for self guided learning. For example, if you have extensive experience with a particular technology.  Covering the pros and cons, and lesson’s learned while using that technology, can be incredibly valuable to help focus an individual’s selection, in books or academic courses that can enrich their learning.&lt;/p&gt;

&lt;p&gt;Be careful to not to impose too much of your bias on a particular topic.  Statements like, technology X doesn’t do this so don’t use it are less helpful than guiding mentee’s towards the technology that would likely be adaquate.&lt;/p&gt;

&lt;p&gt;Adaquate is key here.  There may be 5 ways to solve a problem where each one is better than the next.  It’s not always important that your mentee jumps right to the best solution but that they implement something that solves their problem and then reflect on what they could have done better.  As with so many things in life, learning is about the journey and not the destination as long as immediate requirements are satisfied. &lt;strong&gt;Be okay with imperfection, but not negligence&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;dont-let-them-quit&quot;&gt;Don’t let them quit&lt;/h2&gt;

&lt;p&gt;Assuming they have chosen their career path and are pursuing some goal.  Don’t let them quit when the going gets tough.  Ultimately much of success in this world comes down to grit.  How many cycles folks are willing to put into solving the problem makes a big difference in reaching a successful outcome.&lt;/p&gt;

&lt;h3 id=&quot;help-them-achieve-smaller-wins-instead&quot;&gt;Help them achieve smaller wins instead&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Try to reframe success in the form of a smaller problem.&lt;/strong&gt; For example if your mentee says they would like to create a nuclear fusion reactor in the next year with zero prior knowledge of what’s involved, it’s your job to lay out some first steps that will lead them ultimately to conclude that their original plan was unrealistic and they lacked the skills, while still keeping them on a productive path.  Instead you might suggest they take an elementary physics course and meet with some experts in the field.&lt;/p&gt;

&lt;p&gt;Small wins will keep folks engaged and reduce the likelihood of a quitting and bring folks back from the brink of collapse.&lt;/p&gt;

&lt;h2 id=&quot;dont-mentor-an-unwilling-participant&quot;&gt;Don’t mentor an unwilling participant&lt;/h2&gt;

&lt;p&gt;Forced mentorship rarely works.  Several times, my mentees have decided I wasn’t the right person for helping them achieve their goals.  Additionally, I’ve had to drop certain mentees because they weren’t keeping up their end of the bargain.  Missed appointements and closed mindedness are usually the triggers. If the mentee can’t be present and is unwilling to engage in discussion with the mentor the relationship will not be fruitful and it should be terminated.&lt;/p&gt;

&lt;p&gt;In addition to not being a good fit in the first place, If you do your job correctly, your mentee will eventually outgrow you.  &lt;strong&gt;A mentor to mentee relationship may not be forever&lt;/strong&gt;.  Evaluating if the relationship is working every once in a while will help you figure out if it’s time for a change.
&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;hr /&gt;

&lt;p&gt;&lt;br /&gt;&lt;br /&gt;
That’s all I have for now, I have to give a shout-out to my current employer &lt;a href=&quot;https://www.agero.com/&quot;&gt;Agero&lt;/a&gt;, while we don’t yet have an official mentorship program, I’ve found that if you’re a willing mentee there are a number folks in leadership positions that openly offer guidance and support.  In the cut throat tech world this can be hard to find.  If you are looking for a job at a company that supports continual improvement and learning for its workforce, I would encourage you to &lt;a href=&quot;https://www.agero.com/careers/openings&quot;&gt;apply for an open position&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you wish to contribute to the conversation please leave a comment&lt;/strong&gt;&lt;/p&gt;

</description>
        <pubDate>Wed, 05 Jun 2019 16:00:00 +0000</pubDate>
        <link>http://www.jefferydurand.com/mentorship/learning/leadership/2019/06/05/how-to-be-a-mentor.html</link>
        <guid isPermaLink="true">http://www.jefferydurand.com/mentorship/learning/leadership/2019/06/05/how-to-be-a-mentor.html</guid>
        
        
        <category>mentorship</category>
        
        <category>learning</category>
        
        <category>leadership</category>
        
      </item>
    
      <item>
        <title>Counting on dynamodb</title>
        <description>&lt;p&gt;Recently, some folks at my company &lt;a href=&quot;https://www.agero.com/&quot;&gt;Agero&lt;/a&gt; starting discussing strategies for pre-aggregating data that could be used for statistical compuation based on business metrics.  Specifically, the question came up as to how we could maintain event counts.&lt;/p&gt;

&lt;p&gt;Counting has a long history in the field of probability and statistics and is in many ways the most foundational form of data required for data science teams.  A histogram is nothing more than bucketed counts and a probabilty distribution is just the continous version of the same thing.  Maintaining counts is often a requirement of useful statistical computation.  Thus, there is value in considering how one might count lots of things.&lt;/p&gt;

&lt;p&gt;Having drank the serverless cool-aid a while ago, I’ve been using lambda and dynamodb heavily in recent years.  I present the following as a possible solution to maintain counts in this type of environment:&lt;/p&gt;

&lt;h1 id=&quot;overview&quot;&gt;Overview&lt;/h1&gt;
&lt;p&gt;First a rough architecture:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://www.jefferydurand.com/assets/diagrams/dynamodb_counters.png&quot; alt=&quot;Architecture&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The infrastructure is pretty simple.  We have a table that recieves our event data.  This could be purchase data for a customer, ad impressions for a visitor or any other &lt;a href=&quot;https://en.wikipedia.org/wiki/Monotonic_function&quot;&gt;monotonic&lt;/a&gt; event source.&lt;/p&gt;

&lt;p&gt;When an event arrives in our event table, dynamodb will automatically invoke our lambda function with the operation type “INSERT” and the payload representing the data that was placed into the event table.  Based on this event data we construct a “PutItem” operation to upsert into our count table.  For our use case we wanted to know, “how many jobs have been placed for a specific customer in the last 24 hours”.&lt;/p&gt;

&lt;p&gt;For this demo I decided that minute level precision was close enough, but one could extend this solution to any level of precision.  Really it comes down to how much extra one is willing to pay for additional decimal places of precision.  There are trade-offs with any architecture and this architecture trades off a small bit of precision for a huge speed improvement on large datasets.  Similarly there are tradeoffs in precision and cost in the real world.  For example, one might use a tape measure for carpentry but a micrometer for machining.&lt;/p&gt;

&lt;p&gt;Given we require minute level precision, we bucket all counts by &lt;em&gt;both&lt;/em&gt; hour and minute.  To illustrate why, consider the following:
&lt;img src=&quot;http://www.jefferydurand.com/assets/diagrams/time_buckets_v2.png&quot; alt=&quot;Time Buckets&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Given this diagram, the problem we are solving for with different levels of granularity becomes clear.  When we move to the next hour in our series, we don’t have a full hour of data.  Thus, we need to look back at the historical minutes and fill them in appropriately.  This design means that our code will have to fetch between [24,24 + 59] dynamodb keys for every count we wish to calculate. The upper bound of 83 keys helps make our system performance more stable than if we had to make calculations based on 0..n number of events.   83 is also a good number as it can be queried without pagination from dynamodb.&lt;/p&gt;

&lt;p&gt;The requirement to query up to 83 keys means that if we have very few events per 24 hour period the advantage of pre-aggregating goes away and we likely wouldn’t need to pre-aggregate at all.  Thus, this type of counting is best used when we have a relatively high volume of event data coming in.&lt;/p&gt;

&lt;h1 id=&quot;implementation-details&quot;&gt;Implementation Details&lt;/h1&gt;
&lt;p&gt;This article is not about counting and aggregating generally.  It’s intended to help folks aggregate and count things on dynamodb specifically. In this section I’ll dive into some of the techy details.  I’m using dynamodb here but this same system could be used on any key value datastore that supports similar operations (Cassandra?).&lt;/p&gt;

&lt;p&gt;The first thing we need to do is set up the infrastructure as illustrated in the Architecture diagram above.  We use cloudformation to do that.  It should be clear that this is creating two tables and one lambda function.  The lambda function holds the code for translating events into counts.&lt;/p&gt;

&lt;h3 id=&quot;infrastructure&quot;&gt;Infrastructure&lt;/h3&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;na&quot;&gt;AWSTemplateFormatVersion&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;2010-09-09'&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Transform&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::Serverless-2016-10-31&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;Resources&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ProcessDynamoDBStream&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::Serverless::Function&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Handler&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;handle.handler&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;python3.7&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Policies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AmazonDynamoDBFullAccess&lt;/span&gt;
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWSLambdaDynamoDBExecutionRole&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;CodeUri&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;./streamHandler&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;Events&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;DynamoDB&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;Stream&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;!GetAtt&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;DynamoDBTableEvents.StreamArn&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;BatchSize&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;
            &lt;span class=&quot;na&quot;&gt;StartingPosition&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;TRIM_HORIZON&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;DynamoDBTableEvents&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::DynamoDB::Table&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
      &lt;span class=&quot;na&quot;&gt;TableName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;event-counter-demo-events-table'&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;AttributeDefinitions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AttributeName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;id&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;AttributeType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;S&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;KeySchema&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AttributeName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;id&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;KeyType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;HASH&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ProvisionedThroughput&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
        &lt;span class=&quot;na&quot;&gt;ReadCapacityUnits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;WriteCapacityUnits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;StreamSpecification&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;StreamViewType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;NEW_IMAGE&lt;/span&gt;

  &lt;span class=&quot;na&quot;&gt;DynamoDBTableCounts&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;AWS::DynamoDB::Table&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;Properties&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
      &lt;span class=&quot;na&quot;&gt;TableName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;event-counter-demo-counts-table'&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;AttributeDefinitions&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AttributeName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;id&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;AttributeType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;S&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;KeySchema&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
        &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;AttributeName&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;id&lt;/span&gt;
          &lt;span class=&quot;na&quot;&gt;KeyType&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;HASH&lt;/span&gt;
      &lt;span class=&quot;na&quot;&gt;ProvisionedThroughput&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; 
        &lt;span class=&quot;na&quot;&gt;ReadCapacityUnits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;30&lt;/span&gt;
        &lt;span class=&quot;na&quot;&gt;WriteCapacityUnits&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;30&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;code&quot;&gt;Code&lt;/h2&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# this is your lambda code handler.py
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;boto3&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'event-counter-demo-counts-table'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dbclient&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boto3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'dynamodb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbclient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# All timestamps are in milliseconds.  These constants make it easy
# to convert units for bucketing
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MILLISECONDS_IN_AN_HOUR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3600000&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;MILLISECONDS_IN_A_MINUTE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60000&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;MILLISECONDS_IN_A_SECOND&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;handler&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Records'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'eventName'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'INSERT'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;dynamodata&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;record&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'dynamodb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'NewImage'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# helps us store multiple counters in one table
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;counterKeyPrefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dynamodata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'counterKeyPrefix'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'S'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# this helps us set the ttl based on how long we want the 
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# count to remain valid.  Different counters may have different requirements
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;retention&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dynamodata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'retention'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'N'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# snap the event time to the following buckets
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;milliseconds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dynamodata&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;insertUtcTime&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'N'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;hours&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;milliseconds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MILLISECONDS_IN_AN_HOUR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;minutes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;milliseconds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MILLISECONDS_IN_A_MINUTE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;seconds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;milliseconds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MILLISECONDS_IN_A_SECOND&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;hourkey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%s_hour_%s&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counterKeyPrefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hours&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;minutekey&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%s_minute_%s&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counterKeyPrefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;minutes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        
        &lt;span class=&quot;c1&quot;&gt;# The magic of upserts.  If we have no value set it to 1 otherwise increment.
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# also given Null ttl, set that so we can expire the key automatically and avoid 
&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;# filling our table with un-used data.
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;update_args&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'Key'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;'id'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hourkey&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'UpdateExpression'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'SET #count = if_not_exists(#count, :defaultval) + :val, #ttl = if_not_exists(#ttl, :ttl)'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'ExpressionAttributeValues'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;':val'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;':defaultval'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;':ttl'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;seconds&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;retention&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'ExpressionAttributeNames'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;'#ttl'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'ttl'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;s&quot;&gt;'#count'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'count'&lt;/span&gt;
            &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'ReturnValues'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'UPDATED_NEW'&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# update the hour key
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# update the minute key
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;update_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Key'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'id'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;minutekey&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;update_args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now that you have code and infrastructure in place you can generate some event data and see the result.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# mockdata.py
&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;boto3&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;uuid&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;random&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;event_table_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'event-counter-demo-events-table'&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boto3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;profile_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'default'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;dbclient&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'dynamodb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dbclient&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event_table_name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start_hour&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;end_hour&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;37&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# in real life there is no end...
&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;TTL_FUTURE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;90000&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# 25 hours in seconds
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MILLISECONDS_IN_AN_HOUR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3600000&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# we want time in milliseconds 
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;milli&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'.'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;milli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;milli&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;milli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; 
&lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;milli&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;milli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;zfill&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;string_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;%s%s&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;second&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;milli&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start_timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# create a bunch of random data for a 37 hour time period
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_hour&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;start_hour&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;end_hour&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;times&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)):&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;simulated_insert_time&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MILLISECONDS_IN_AN_HOUR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;simulated_insert_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;# simulates 3 counters where we are counting per customer
&lt;/span&gt;        &lt;span class=&quot;n&quot;&gt;provider_id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;randint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;counterKeyPrefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;job_event_provider_id_%s&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider_id&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;example_event&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'id'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uuid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;uuid4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()),&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'insertUtcTime'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;simulated_insert_time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'counterKeyPrefix'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;counterKeyPrefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'retention'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TTL_FUTURE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'providerId'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;provider_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'attribute1'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'data'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'attribute2'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'data'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'current_hour'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;current_hour&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;put_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;example_event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;start_timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start_timestamp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MILLISECONDS_IN_AN_HOUR&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Lastly, you can query the counts database for a specific time range, aggregate the counts and do as you wish with that information.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# query.py
&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;boto3&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;FIRST_EVENT_TIME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;# milliseconds for first event
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PROVIDER_ID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;HOUR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FIRST_EVENT_TIME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3600000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;27&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;MINUTE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FIRST_EVENT_TIME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;27&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;MINUTE_MINUS_DAY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MINUTE&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1440&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;MILLISECONDS_REMAINING&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FIRST_EVENT_TIME&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3600000&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;MINUTES_INTO_HOUR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MILLISECONDS_REMAINING&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;TRAILING_MINUTES&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;60&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MINUTES_INTO_HOUR&lt;/span&gt; 

&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boto3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;profile_name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'default'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'dynamodb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# create a list of the hour keys we need
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hour_keys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;job_event_provider_id_%s_hour_%s&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PROVIDER_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;HOUR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# create a list of the minute keys we need
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;minutes_keys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;job_event_provider_id_%s_minute_%s&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;PROVIDER_ID&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MINUTE_MINUS_DAY&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TRAILING_MINUTES&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)]&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;all_keys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;hour_keys&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;minutes_keys&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# format keys for dynamodb query
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all_keys_query&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'id'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'S'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all_keys&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all_keys_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;batch_get_item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;RequestItems&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;'event-counter-demo-counts-table'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'Keys'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;all_keys_query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;s&quot;&gt;'AttributesToGet'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;'count'&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;results&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;resp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Responses&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;event-counter-demo-counts-table&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# aggregate all values client side
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counts&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;count&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;N&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;total&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;counts&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h1 id=&quot;fin&quot;&gt;Fin&lt;/h1&gt;
&lt;p&gt;That’s all you really need for a basic dynamodb based counting system.  There are many other ways you might construct a counting system and this article aims to offer but one approach.  Questions and comments are always welcome.&lt;/p&gt;

</description>
        <pubDate>Tue, 02 Apr 2019 16:00:00 +0000</pubDate>
        <link>http://www.jefferydurand.com/dynamodb/serverless/counting/2019/04/02/counting-on-dynamodb.html</link>
        <guid isPermaLink="true">http://www.jefferydurand.com/dynamodb/serverless/counting/2019/04/02/counting-on-dynamodb.html</guid>
        
        
        <category>dynamodb</category>
        
        <category>serverless</category>
        
        <category>counting</category>
        
      </item>
    
      <item>
        <title>Who are you?</title>
        <description>&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/v_3ks7-OjGc?start=164&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;The big question right?  Who are any of us?  Folks reading this will probably conclude that I’m a self absorbed yuppie for making this the subject of a blog post.  While that may be the case (feel free to judge me if you must), my motivation for writing this post is that I view myself as a mentor and as a mentor, I would like to encourage folks to think a little bit about who they are in their professional careers.&lt;/p&gt;

&lt;p&gt;This article likely applies to anyone but it’s important to remember I’m speaking through the lens of being a professional software architect and technical mentor.  If you are fond of thought exercises you’ll probably enjoy this article.  If you struggle with the hypothetical, my guess is this article won’t appeal to you.  You’ve been warned.&lt;/p&gt;

&lt;h2 id=&quot;why-is-it-important-to-think-about-who-you-are-professionally&quot;&gt;Why is it important to think about who you are professionally?&lt;/h2&gt;

&lt;p&gt;Who you think you are and who you want to become, will greatly shape your career path, and largely determine where you fit into an organization, as well as shape what types of organizations you are likely to be attracted too in the first place.  Additionally, if you put zero thought into who you are or who you want to become, your identity and career will likely be determined for you.&lt;/p&gt;

&lt;p&gt;In my industry, folks have titles like “engineer III”, “senior engineer”, “product manager” or other similarly generic professional names.  While these names might be useful for determining compensation from an HR perspective they do little to signal to the rest of the organization what type of person they are dealing with.  I would advise anyone, to do everything they can to avoid the company label trap when discussing who they are, because unintentional or intentional it is a trap that could end up controlling you by controlling your identity.  To be clear you will still be labeled by the company as “engineer III” but don’t let that define your role and responsibilities and how you interact with those around you.  Try not to fall into the trap of being “Just a &lt;strong&gt;__&lt;/strong&gt;_” fill in the blank&lt;/p&gt;

&lt;p&gt;Hopefully, I’ve convinced you that taking some time to think about who you are and what you represent and stand for professionally is time well spent.  The rest of this article will revolve around some ideas for how you might explore your professional identity.&lt;/p&gt;

&lt;h2 id=&quot;what-is-your-spirit-animal&quot;&gt;What is your spirit animal?&lt;/h2&gt;

&lt;p&gt;Wow! Really Jeff?  Yeah I know, this is silly and bizarre and who the hell cares about anyone’s spirit animal?  I had this very reaction when someone first brought this line of questioning up to me.&lt;/p&gt;

&lt;p&gt;I was working for &lt;a href=&quot;https://www.gazelle.com&quot;&gt;gazelle.com&lt;/a&gt; and we were conducting interviews for a new software developer we wanted to add to the team.  We were trying to come up with questions to ask the interviewee and one of my colleagues launched into a story about how he went in for an interview early in his career, and the founder asked him without flinching, what his spirit animal was.  We all laughed at the absurdity of it, but then something funny happened.  We all went around (half jokingly) and said what our spirit animals were. This of course forced us to think about why a particular animal personified us.  The exercise yielded some fun and interesting results.&lt;/p&gt;

&lt;p&gt;In the interest of transparency and getting the ball rolling I’ll tell you my spirit animal:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=qWTwan_G6YE&quot;&gt;&lt;em&gt;The Emperor Penguin&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The reasons the Emporer Penguin is my spirit animal are as follows:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;They are patient (they incubate eggs for up to 68 days in brutal cold weather).&lt;/li&gt;
  &lt;li&gt;They care about the common good.&lt;/li&gt;
  &lt;li&gt;They are grounded (they can’t fly, I like to think I’m grounded in my thinking).&lt;/li&gt;
  &lt;li&gt;They are a bit silly (on land) but it doesn’t seem to bother them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I encourage you to think about what your spirit animal is and why.  It can be a fun / silly way to have a moment of reflection.&lt;/p&gt;

&lt;h2 id=&quot;what-would-have-been-your-profession-200-years-ago&quot;&gt;What would have been your profession 200 years ago?&lt;/h2&gt;

&lt;p&gt;This question forces you to think about your values more than your current position.  For example, if you are a salesman you might say that you’d have been either a bare knuckle boxer or maybe a fortune teller (I’ll leave the reader to determine what I think about salemen).  Try to forget the fact that, 200 years ago most people were farmers, and try to come up with something creative.&lt;/p&gt;

&lt;p&gt;To get the ball rolling I’ll go first.&lt;/p&gt;

&lt;p&gt;I believe I would have been a bar keep or Inn Owner (truthfully this still sounds like a great option).  The idea of operating a hub for discussion and ideas is incredibly appealing to me.  Taverns and dinner tables are the original open source in my opinion.  Get a few drinks in folks and ideas tend to flow.  Taverns are where deals were struck and decisions were made in the absense of a more formal settings.&lt;/p&gt;

&lt;p&gt;Lastly, a bar keep is often a sounding board for people’s problems which keeps him in the know about not only that individual’s issue, but also would help one put their finger on the pulse of the entire community. Getting the unfiltered story from a group by talking to them individually can often lead to solutions and ideas that would otherwise prove elusive in a more organized meeting style setting.&lt;/p&gt;

&lt;h2 id=&quot;parting-thoughts&quot;&gt;Parting thoughts&lt;/h2&gt;

&lt;p&gt;There are likely a number of other questions that are even better at giving you some clue about who you are or might become professionally.&lt;/p&gt;

&lt;p&gt;In closing: My title says “Software Architect” and that is the job that I perform. However, I like to think I approach that job with penguins and bar keeps in mind.  Hopefully that approach makes me more effective in my job role and more fulfilled in my career.&lt;/p&gt;

&lt;p&gt;Please leave a comment if you have who are you question you’d like to share or if you just want to tell me about your spirit animal :)&lt;/p&gt;
</description>
        <pubDate>Sun, 10 Feb 2019 16:00:00 +0000</pubDate>
        <link>http://www.jefferydurand.com/leadership/philosophy/mentorship/2019/02/10/who-are-you.html</link>
        <guid isPermaLink="true">http://www.jefferydurand.com/leadership/philosophy/mentorship/2019/02/10/who-are-you.html</guid>
        
        
        <category>leadership</category>
        
        <category>philosophy</category>
        
        <category>mentorship</category>
        
      </item>
    
      <item>
        <title>python builder pattern</title>
        <description>&lt;p&gt;After years of ruby and javascript I’ve started doing some work with python recently after joining the team at &lt;a href=&quot;https://www.agero.com/careers&quot;&gt;Agero&lt;/a&gt;.  One of the first things I noticed was that a lot of folks in the python world pass around a lot of named arguments as a convention.  My first example of this is the boto3 library used to interact with AWS in python &lt;a href=&quot;https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#DynamoDB.Client.scan&quot;&gt;(for proof, click here)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In an effort to reduce some of this parameter passing folks might be tempted to make their own class that reduces the number of parameters one needs to pass around.  I offer a simpler functional solution taken from some patterns I’ve seen used in the functional javascript community.&lt;/p&gt;

&lt;p&gt;Enter partial function application with a builder pattern.  The following code highlights how one might create reusable functions without creating a domain specific class hierarchy.&lt;/p&gt;

&lt;p&gt;Take the boto3 library.  More specifically, take the scan function for a dyanmodb table.  The following is an example of how one could use this concept to produce a convenient way to iterate through all items in a dynamo collection.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# ./function_builder.py
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;functools&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;FunctionBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
       &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;having&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;functools&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;partial&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;**&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;# ./dynamo_iterator.py
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DynamoIterator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Given dynamodb function that returns items and a LastEvaluatedKey iterate
    
        Keyword arguments:
        func -- function that takes LastEvaluatedKey and returns { items:  [] }
        &quot;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_evaluated_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_fetch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_get_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db_result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'Items'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[])&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;_has_more_results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_evaluated_key&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__iter__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Required to implement the Iterator interface in python&quot;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__next__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Required to implement the Iterator interface in python&quot;&quot;&quot;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_fetch&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_has_more_results&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_fetch&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;func&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;LastEvaluatedKey&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_evaluated_key&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;last_evaluated_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'LastEvaluatedKey'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_get_items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;StopIteration&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;s&quot;&gt;&quot;&quot;&quot;Python 2.7 requireds next vs 3.7 that requires __next__ hence we alias the function&quot;&quot;&quot;&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__next__&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Now that we have these two simple base classes we can iterate through a collection of dynamodb objects using the following code.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# ./main.py
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;boto3&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;.dynamo_iterator&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DynamoIterator&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;.function_builder&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FunctionBuilder&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;dynamodb&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;boto3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resource&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'dynamodb'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dynamodb&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'my-table-name'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;partial_scan_all&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FunctionBuilder&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;table&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;scan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;having&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Limit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;'ALL_ATTRIBUTES'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; 

&lt;span class=&quot;c1&quot;&gt;# this will iterate through every item in a dynamodb table.  Obviously not meant for quickly returning values to a user interface.
&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DynamoIterator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;partial_scan_all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Hopefully you found this code example useful. If you have any questions please leave a comment and I’ll try to help with an answer.&lt;/p&gt;

</description>
        <pubDate>Fri, 28 Dec 2018 16:00:00 +0000</pubDate>
        <link>http://www.jefferydurand.com/dynamodb/python/pattern/software/design/2018/12/28/functional-builder-pattern-with-python.html</link>
        <guid isPermaLink="true">http://www.jefferydurand.com/dynamodb/python/pattern/software/design/2018/12/28/functional-builder-pattern-with-python.html</guid>
        
        
        <category>dynamodb</category>
        
        <category>python</category>
        
        <category>pattern</category>
        
        <category>software</category>
        
        <category>design</category>
        
      </item>
    
      <item>
        <title>Static asset headers in rails 5</title>
        <description>&lt;p&gt;First things first.  Conventional wisdom tells us we should be serving our static assets from something like nginx because it’s optimized for doing such a task.  Generally speaking I disagree with that thinking and instead offer a different solution.  One should be serving static content out of a CDN and that CDN should be caching your content at edge locations.&lt;/p&gt;

&lt;p&gt;Given we want to cache content at edge locations we will want a mechanism for specifying in our rails application how we would like our our CDN to cache the content.  As in, how long should the CDN hold onto the content before returning to our origin server to fetch it again?  There are two ways that you might accomplish this.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Configure the CDN to cache the correct thing by overwriting headers produced by your application.&lt;/li&gt;
  &lt;li&gt;When a resource is requested from your origin provide the proper headers so that the CDN knows how to handle the asset.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In my opinion option 2 is much preferred.  Allowing your application to specify headers gives you more control and keeps the logic of your application where it belongs.  Assuming I’ve convinced you to use a CDN and configure the caching mechanism to work via headers specified at at the origin.  The logical question is how do I configure the proper headers such that my CDN will cache my assets.&lt;/p&gt;

&lt;p&gt;The following code works with the fastly CDN service but probably works well with many others.&lt;/p&gt;

&lt;p&gt;This code configures the browser cache and your CDN to cache assets for 10 years.  Since we are digesting all assets, meanings we are creating new versions if anything changes, we can cache these assets forever.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;c1&quot;&gt;# in your config/application.rb file&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;public_file_server&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;headers&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;'Surrogate-Control'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'max-age=315360000'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;'Cache-Control'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'maxage=315360000, public, no-check'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;'Expires'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;year&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;from_now&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;httpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;'Date'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;days&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ago&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;httpdate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;s1&quot;&gt;'Last-Modified'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;days&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ago&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;httpdate&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The result of this config behind a proper CDN is that you assets will end up being fetched exactly once from your rails application and then never again until you clear the cache.  Even then, the browser cache will ensure that only new visitors to your site will need to request the assets.  In my opinion, Rails is perfectly well suited to serving up the occassional asset in this context and will likely have little impact on overall performance.  Furthermore keeping the caching logic in the code base has obvious utility including allowing caching headers to be tested more easily via unit tests.&lt;/p&gt;

&lt;p&gt;As always, leave a comment if you found this useful.&lt;/p&gt;
</description>
        <pubDate>Wed, 09 Aug 2017 16:00:00 +0000</pubDate>
        <link>http://www.jefferydurand.com/ruby/rails/caching/assets/headers/2017/08/09/static-asset-headers-rails-5.html</link>
        <guid isPermaLink="true">http://www.jefferydurand.com/ruby/rails/caching/assets/headers/2017/08/09/static-asset-headers-rails-5.html</guid>
        
        
        <category>ruby</category>
        
        <category>rails</category>
        
        <category>caching</category>
        
        <category>assets</category>
        
        <category>headers</category>
        
      </item>
    
      <item>
        <title>A gentle introduction to Machine Learning</title>
        <description>&lt;p&gt;I recently completed a great course on Machine Learning, the course is offered by &lt;a href=&quot;https://www.coursera.org/learn/machine-learning&quot;&gt;coursera&lt;/a&gt; so check it out if you find this article interesting.  Until now, I’ve only been a passive consumer of machine learning in my own application development and home life &lt;a href=&quot;https://www.amazon.com/gp/product/B00X4WHP5E&quot;&gt;Amazon Echo&lt;/a&gt;.  As a scientist and engineer, I always knew that some interesting algorithms were crunching data and then making complex decisions behind the scene, but I didn’t really understand the mechanics of that process.  I believe writing is a great way to share information as well as solidify one’s understanding about a particular subject and so it is with that reasoning that I write the following article.  If you don’t care for understanding how machine learning works but are more interested in why it’s important right now feel free to skip over the details.&lt;/p&gt;

&lt;h1 id=&quot;the-basic-idea-behind-machine-learning&quot;&gt;The basic idea behind machine learning.&lt;/h1&gt;

&lt;p&gt;Machine learning is concerned with looking at a source of data and figuring out (fitting) a mathematical function that characterizes the behavior captured in that data.  Let’s say we have a farm and that farm uses water and fertilizer we record that data every year and then associate it with a corn yield.  So that table of data may look something like the following.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;fertilizer(lbs)&lt;/th&gt;
      &lt;th&gt;Water (G)&lt;/th&gt;
      &lt;th&gt;Corn(lbs)&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;100&lt;/td&gt;
      &lt;td&gt;600&lt;/td&gt;
      &lt;td&gt;800&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;110&lt;/td&gt;
      &lt;td&gt;400&lt;/td&gt;
      &lt;td&gt;600&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;80&lt;/td&gt;
      &lt;td&gt;700&lt;/td&gt;
      &lt;td&gt;500&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;120&lt;/td&gt;
      &lt;td&gt;600&lt;/td&gt;
      &lt;td&gt;810&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The challenge is to find a mathematical function such that given some value of water and some other value of fertilizer, we predict the amount of corn output we should see for the season.  Anyone familiar with basic economic analysis will recognize this as a simple linear regression problem than can be solved quickly with a little linear algebra.  Typically one would never use machine learning to solve such a simple problem as deterministic methods work just fine at this scale.&lt;/p&gt;

&lt;h2 id=&quot;what-does-this-have-to-do-with-machine-learning&quot;&gt;What does this have to do with machine learning?&lt;/h2&gt;

&lt;p&gt;As the number of data points gets larger and you have more and more components contributing to the “Corn” yield, the calculations required to solve the problem via deterministic methods become more and more computationally expensive and complex.  At a large enough scale you could have a problem that would take an entire lifetime to solve.  Thus we have to use more involved mathematical techniques that estimate the solution rather than solving exactly.&lt;/p&gt;

&lt;p&gt;In addition to complex linear problems, we might not be trying to find linear functions at all.  This leads us to explore techniques for finding more complex functions that can make better predictions given a new set of inputs (features).  It turns out that machine learning techniques can fit a wide variety of functions and thus have broad appeal across domain models.&lt;/p&gt;

&lt;h1 id=&quot;least-squared-errors-linear-models&quot;&gt;Least Squared Errors (linear models)&lt;/h1&gt;

&lt;p&gt;Let’s say I have some data and I’m looking for a function to characterize that data.  A measure of how well my function does at classifying the data could be how far away from a line each data point was.  This is the error between my function and the data it represents.  In order to account for points below and above the line and count them equally, one might square the error.  If you add up all the squared-error values you get for a particular function, you have a way to evaluate how good your function “fits” the data you have.&lt;/p&gt;

&lt;p&gt;It’s worth noting that different problems involve different functions that measure the amount of error, but the premise remains the same.  The error produced is often referred to as the cost of the model and thus the error measuring function is referred to as a Cost function.&lt;/p&gt;

&lt;p&gt;If you have a way of measuring how much error your function has then you have an objective way of deciding if one function is better than another.  Machine learning techniques offer us a way to discover the function that works the best without looking at an infinite number of possible solutions.&lt;/p&gt;

&lt;h1 id=&quot;gradient-descent&quot;&gt;Gradient Descent&lt;/h1&gt;

&lt;p&gt;The next logical question is how do I quickly find the best function given an infinite number of possible functions.  The answer lies in differential calculus.  Calculus tells us that if we take the derivative of function and set it equal to 0 then solve the equation we will get the optimal values.  Now since we don’t know the function in advance, things aren’t so simple, but we can exploit this truth. Mainly we can get a sense of the direction where the minima lies by calculating the partial derivatives given our current best function.  We can then use this information to take a small step in a direction towards a better function.  We do this until a specific number of iterations or until we are no longer making much progress at which point we stop and assume our solution is good enough.  Here is a video illustrating the concept clearly.&lt;/p&gt;

&lt;iframe width=&quot;100%&quot; height=&quot;400&quot; src=&quot;https://www.youtube.com/embed/eikJboPQDT0&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;This technique forms the foundation for most machine learning problems. Solving different problems is simply a matter of formulating them so that they can be solved using this technique.&lt;/p&gt;

&lt;h1 id=&quot;why-is-machine-learning-so-exciting&quot;&gt;Why is machine learning so exciting?&lt;/h1&gt;

&lt;p&gt;The reason machine learning is exciting is because a huge number of problems can be re-formulated as probabilistic models.  Meaning sometimes we only care that an answer is close enough to the correct answer and we’d be willing to trade compute time human or computer to get an answer that’s 99.999% correct. Furthermore, some problems only make sense in terms of probabilities.  For instance, if you are predicting if it’s going to rain 5 days from now it would not make sense to say there is 100% chance of anything happening in the future.&lt;/p&gt;

&lt;h1 id=&quot;why-is-machine-learning-so-much-more-exciting-lately&quot;&gt;why is machine learning so much more exciting lately?&lt;/h1&gt;

&lt;p&gt;The reason is pretty simple.  We now have access to massive amounts of data, the likes of which the human race has previously never recorded (the amount of data gathered per day is accelerating).  Secondly, the semiconductor industry has managed to make silicon that is particularly well suited for Linear Algebra calculations (even if they didn’t, the fact that many linear algebra operations can be parallelized over multiple machines means we could just use more off the shelf computers to do our calculations anyway).  Thus, we now have the compute infrastructure to make use of this ever increasing data set.&lt;/p&gt;

&lt;h2 id=&quot;so-what-how-does-more-data-improve-the-solutions-to-our-machine-learning-problems&quot;&gt;So what? how does more data improve the solutions to our machine learning problems?&lt;/h2&gt;

&lt;p&gt;It turns out that there are a huge number of problems that perform better, the more data one has.  Think about what we are trying to do. We are making mathematical models that estimate the real world, many times the data we have describes the real world, the better our description of the real world the better our understanding could potential be and thus the better our prediction about what might happen in that world might be.  Having more data and compute power allows computers to understand the world through data in ways that have never been possible.&lt;/p&gt;

&lt;h1 id=&quot;how-does-all-this-relate-to-artificial-intelligence&quot;&gt;How does all this relate to Artificial Intelligence?&lt;/h1&gt;

&lt;p&gt;Think of artificial intelligence as systems of machine learning models adapted for human consumption.  The idea is that you want to make a system that can take some input, run it through 1..n different machine learning algorithms and other filtering processes and then spit out a useful result for a human.  So machine learning algorithms are just one of a number of sub-systems that make up an artificially intelligent system.  It could be that a system like Amazon’s Alexa uses hundreds of different machine learning algorithms to present the user with one fluid experience. The average consumer will never interact directly with a machine learning model but almost everyone will interact with an artificial intelligence system.&lt;/p&gt;

&lt;h1 id=&quot;conclusion-and-thoughts&quot;&gt;Conclusion and thoughts&lt;/h1&gt;

&lt;p&gt;Learning these concepts has been an eye-opening experience and I believe there are a lot of interesting applications that will be created as things progress in this space.  Clearly, this article is a Math lite summary about machine learning but I encourage you to learn more by taking the coursera course (amazingly it’s totally free).  I plan to use machine learning models to make more intelligent systems for hobby work (drones to chase the squirrels from my garden anyone?) or for profit (fraud detection, customer sentiment,product recommendations, and BI based decision making). I Can’t wait to get started.&lt;/p&gt;
</description>
        <pubDate>Thu, 01 Dec 2016 16:00:00 +0000</pubDate>
        <link>http://www.jefferydurand.com/machine/learning/2016/12/01/intro-to-machine-learning.html</link>
        <guid isPermaLink="true">http://www.jefferydurand.com/machine/learning/2016/12/01/intro-to-machine-learning.html</guid>
        
        
        <category>machine</category>
        
        <category>learning</category>
        
      </item>
    
  </channel>
</rss>
