<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Alaina Kafkes</title>
    <description>The latest articles on Forem by Alaina Kafkes (@alainakafkes).</description>
    <link>https://forem.com/alainakafkes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F18021%2Fc0eabd1e-8c20-411b-ae42-6f99bc70c587.jpg</url>
      <title>Forem: Alaina Kafkes</title>
      <link>https://forem.com/alainakafkes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/alainakafkes"/>
    <language>en</language>
    <item>
      <title>Exploring the Linguistics Behind Regular Expressions</title>
      <dc:creator>Alaina Kafkes</dc:creator>
      <pubDate>Mon, 20 Nov 2017 15:39:36 +0000</pubDate>
      <link>https://forem.com/alainakafkes/exploring-the-linguistics-behind-regular-expressions-bb4</link>
      <guid>https://forem.com/alainakafkes/exploring-the-linguistics-behind-regular-expressions-bb4</guid>
      <description>&lt;p&gt;Regular expressions inspire fear in new and experienced programmers alike. When I first saw a regular expression — often abbreviated as “regex” — I remember feeling dizzy from looking at the litany of parentheses, asterisks, letters, and numbers. Regular expressions seemed nonsensical, impenetrable.&lt;/p&gt;

&lt;p&gt;I expected regular expressions to crop up again in my upper-level computer science coursework — maybe by then I’d finally feel ready to tackle them — but I encountered them in an introductory class that I had put off until my senior year. The purpose of this course was to draw students who had never written a line of code into CS by introducing them to concepts like cryptography, human-computer interaction, machine learning — you know, only the latest and greatest of tech buzzwords.&lt;/p&gt;

&lt;p&gt;I didn’t attend more than a handful of lectures, but one of the assignments stuck with me. I had to write an essay about a famous computer scientist or academic whose work impacted computer science. I chose Noam Chomsky.&lt;/p&gt;

&lt;p&gt;Little did I know that learning about Chomsky would drag me down a rabbit hole back to regular expressions, and then magically cast regular expressions into something that fascinated me. What enchanted me about regular expressions was the homonymous linguistic concept that powered them.&lt;/p&gt;

&lt;p&gt;I hope to spellbind you, too, with the linguistics behind regular expressions, a a backstory unknown to most programmers. Though I won’t teach you how to use regular expressions in any particular programming language, I hope that my linguistic introduction will inspire you to dive deeper into how regular expressions work in your programming language of choice.&lt;/p&gt;

&lt;p&gt;To begin, let’s return to Chomsky: what does he have to do with regular expressions? Hell, what does he even have to do with computer science?&lt;/p&gt;

&lt;h3&gt;
  
  
  A Computer Scientist By Accident
&lt;/h3&gt;

&lt;p&gt;Wikipedia christens &lt;a href="https://en.wikipedia.org/wiki/Noam_Chomsky" rel="noopener noreferrer"&gt;Noam Chomsky&lt;/a&gt; as a linguist, philosopher, cognitive scientist, historian, social critic, and political activist, but not as a computer scientist. Because he is so highly regarded in all of these fields, his indirect contributions to the field of computer science often fall by the wayside.&lt;/p&gt;

&lt;p&gt;The more I researched Chomsky’s academic work, the more accidental Chomsky’s foray into computing seemed. This affirmed my belief that all fields — even those that appear disparate from computer science — have something to offer to computing and the tech industry.&lt;/p&gt;

&lt;p&gt;His contributions to the field of linguistics in particular exemplify the impact of interdisciplinary research on computer science. The Chomsky hierarchy transformed the code that computer scientists, software engineers, and hobbyists write today.&lt;/p&gt;

&lt;p&gt;Yes, it was this hierarchy that brought regular expressions to computer science. But, before we can understand the jump from Chomsky to regular expressions, I’ll outline the Chomsky hierarchy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Linguistic Law &amp;amp; Order
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Chomsky_hierarchy" rel="noopener noreferrer"&gt;&lt;strong&gt;Chomsky hierarchy&lt;/strong&gt;&lt;/a&gt; is an ordering of &lt;a href="https://en.wikipedia.org/wiki/Formal_grammar" rel="noopener noreferrer"&gt;&lt;strong&gt;formal grammars&lt;/strong&gt;&lt;/a&gt; — think syntactic rules for &lt;a href="http://interactivepython.org/courselib/static/thinkcspy/GeneralIntro/FormalandNaturalLanguages.html" rel="noopener noreferrer"&gt;&lt;strong&gt;formal languages&lt;/strong&gt;&lt;/a&gt; — such that each grammar exists as a &lt;a href="http://mathworld.wolfram.com/ProperSubset.html" rel="noopener noreferrer"&gt;proper subset&lt;/a&gt; of the grammars above it in the hierarchy. Some formal languages have stricter grammars than others, so Chomsky sought to organize formal grammars into his eponymous hierarchy.&lt;/p&gt;

&lt;p&gt;I briefly mentioned that formal grammars are syntactic rules: rules that give all possible valid phrases for a given formal language.  Grammars provide the rules that build languages. In linguist-speak, a language’s formal grammar provides a framework with which &lt;strong&gt;nonterminals&lt;/strong&gt; (input or intermediate string values) can be converted into &lt;strong&gt;terminals&lt;/strong&gt; (output string values).&lt;/p&gt;

&lt;p&gt;To elucidate this new vocabulary, I’ll walk through an example of converting a set of nonterminals into terminals using a made-up formal grammar. Let’s say that our pretend formal language, &lt;a href="http://harrypotter.wikia.com/wiki/Parseltongue" rel="noopener noreferrer"&gt;Parseltongue&lt;/a&gt;, has the following formal grammar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terminals: {s, sh, ss}&lt;/li&gt;
&lt;li&gt;Nonterminals: {snake, I, am}&lt;/li&gt;
&lt;li&gt;Production rules: {I → sh, am → s, snake → ss}&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using the production rules, I can convert the input sentence “I am snake” into “sh s ss.” This conversion happens piece by piece: “I am snake” → “sh am snake” → “sh s snake” → “sh s ss.”&lt;/p&gt;

&lt;p&gt;As my Parseltongue example illustrates, formal grammars parse strings of nonterminals into terminal-only strings — grammatically correct phrases. But formal grammars act not only as generators of a language, but also recognizers of whether a string fits the formal grammar. Whereas the example string “I am a snake” can be fully converted into terminals, the string “I am not a snake” cannot be written in Parseltongue because the nonterminal “not” cannot be translated into a Parseltongue terminal.&lt;/p&gt;

&lt;p&gt;To re-emphasize something I stated earlier: formal grammars generate formal languages. That means that, by creating a hierarchy of formal grammars, Chomsky also categorized languages themselves.&lt;/p&gt;

&lt;p&gt;With that sobering introduction, let’s look at the four formal grammars in Chomsky’s hierarchy. From most to least strict, they are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Regular grammars&lt;/strong&gt;, which retain no past state knowledge from input string to output string&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context-free grammars&lt;/strong&gt;, which retain only recent state knowledge from input string to output string&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context-sensitive grammars&lt;/strong&gt;, which keep all past state knowledge from input string to output string&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unrestricted&lt;/strong&gt; (or &lt;strong&gt;recursively enumerable&lt;/strong&gt;) &lt;strong&gt;grammars&lt;/strong&gt;, which have all state knowledge and thus can create every output string imaginable from a given input string&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What is this “state knowledge” that I speak of? Think of knowledge in terms of &lt;a href="https://en.wikipedia.org/wiki/Scope_(computer_science)" rel="noopener noreferrer"&gt;scope&lt;/a&gt;. Regular grammars, for example, have no knowledge of the string’s past states in their "scope" in the process of converting an input string into an output string. This suggests that once the grammar makes an individual conversion of nonterminal  to terminal (plus a series of zero or more nonterminals), the grammar “forgets” the previous state of the string.&lt;/p&gt;

&lt;p&gt;On the other hand, unrestricted grammars hold onto every possible state of the string-in-translation. Context-free and context-sensitive grammars fall somewhere in the middle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F9%2F9a%2FChomsky-hierarchy.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fupload.wikimedia.org%2Fwikipedia%2Fcommons%2F9%2F9a%2FChomsky-hierarchy.svg" alt="Chomsky Hierarchy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re looking for a more detailed explanation of the grammars in the Chomsky hierarchy, you’ll have to take a peek at &lt;a href="https://en.wikipedia.org/wiki/Automata_theory" rel="noopener noreferrer"&gt;automata theory&lt;/a&gt;. I’ll focus on the grammar that brings us back to regular expressions, fittingly called the regular grammar.&lt;/p&gt;

&lt;h3&gt;
  
  
  On the Regular Expressions
&lt;/h3&gt;

&lt;p&gt;Regular expressions and regular grammars are equivalent.  They communicate the same set of syntactic rules, albeit using different formalisms, and both produce the same regular languages.&lt;/p&gt;

&lt;p&gt;In linguistics, a &lt;strong&gt;regular expression&lt;/strong&gt; is recursively defined as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The empty set is a regular expression.&lt;/li&gt;
&lt;li&gt;The empty string is a regular expression.&lt;/li&gt;
&lt;li&gt;For any character x in the input alphabet, x is a regular expression that produces the regular language {x}.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alternation&lt;/strong&gt;: If x and y are regular expressions, then x | y is a regular expression. For example, the regular expression &lt;code&gt;0|1&lt;/code&gt; produces the regular language &lt;code&gt;{0,1}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concatenation&lt;/strong&gt;: If x and y are regular expressions, then x • y is a regular expression. For example, the regular expression &lt;code&gt;0•1&lt;/code&gt; produces the regular language &lt;code&gt;{01}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repetition&lt;/strong&gt; (also known as &lt;strong&gt;Kleene star&lt;/strong&gt;): If x and y are regular expressions, then x*  is a regular expression. For example, the regular language &lt;code&gt;0•1*&lt;/code&gt; produces the regular language &lt;code&gt;{0, 01, 011, 0111, ...}&lt;/code&gt;, ad infinitum.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A regular grammar is composed of rules like those of Parseltongue. Just as a regular grammar can be utilized to parse an input string into an output string, a regular expression converts strings quite similarly. You can see examples of this parsing for the alternation, concatenation, and repetition operations — or, to use my prior analogy, rules — that regular expressions adopt.&lt;/p&gt;

&lt;p&gt;Let’s return to our friend Noam Chomsky for a moment. According to his hierarchy of grammars, regular grammars retain no information about intermediate steps in converting from an input string to an output string. What does this tell us about regular expressions?&lt;/p&gt;

&lt;p&gt;The “forgetfulness” of regular grammars implies that translations in one part of the string do not impact how the other nonterminals in the string are translated in future steps. There is no coordination between different parts of the string in the creation of the output string.&lt;/p&gt;

&lt;p&gt;Looking at the linguistics behind regular grammars gives us insight into why programmers first brought regular expressions into code. Although I’ve only discussed formal grammars as generators and recognizers of language, the fact that regular grammars convert input string to output string piece by piece makes them &lt;em&gt;pattern-matchers&lt;/em&gt;. In programming, regular expressions use production rules to convert an input string — a pattern — into a regular language — a set of strings that match that pattern.&lt;/p&gt;

&lt;p&gt;But I would have never written this blog post if programming language creators implemented regular expressions exactly as they are defined in the field of linguistics. Computational regular expressions are a far cry from their linguistic precursor, but the linguistic regular expressions that I covered provide a useful framework for understanding regular expressions in code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Two Regular Expressions, Both Alike in Dignity
&lt;/h3&gt;

&lt;p&gt;Hereafter, I will use the term &lt;strong&gt;regular expression&lt;/strong&gt; to mean a linguistic regular expression and the term &lt;strong&gt;regex&lt;/strong&gt; to signify a programmatic regular expression. In the wild, both linguistic and programmatic regular expressions are referred to as “regular expressions” even though they are quite different from one another — how confusing!&lt;/p&gt;

&lt;p&gt;The difference between regular expressions and regexes stems from how they are used. Regular expressions — or regular grammars — are part of &lt;a href="https://en.wikipedia.org/wiki/Formal_language" rel="noopener noreferrer"&gt;formal language &lt;em&gt;theory&lt;/em&gt;&lt;/a&gt;, which exists to &lt;em&gt;describe&lt;/em&gt; shared elements of &lt;strong&gt;natural languages&lt;/strong&gt; — languages that evolved over time without human premeditation. Linguists use regular expressions for theoretical purposes, like the categorization of formal grammars in the Chomsky hierarchy. Regular expressions help linguists understand the languages that humans speak.&lt;/p&gt;

&lt;p&gt;Regexes, on the other hand, are utilized by &lt;em&gt;everyday programmers&lt;/em&gt; who want to &lt;em&gt;search&lt;/em&gt; for strings that &lt;em&gt;match&lt;/em&gt; a given pattern. While regular expressions are theoretical, regexes are pragmatic. Programming languages are &lt;strong&gt;formal languages&lt;/strong&gt;: languages designed by people (here, programmers) for specific purposes. As you might imagine, programming language creators augmented the functionality of regexes in code. Let’s examine these enhancements.&lt;/p&gt;

&lt;p&gt;Remember that regular expressions have three operations: alternation, concatenation, and repetition. I’m no regex expert — regexpert? — but all it takes is a peek at the &lt;a href="https://en.wikipedia.org/wiki/Regular_expression" rel="noopener noreferrer"&gt;regular expression Wikipedia page&lt;/a&gt; to notice that regexes implement more than just three operations.&lt;/p&gt;

&lt;p&gt;For example, using &lt;a href="https://www.regular-expressions.info/posix.html" rel="noopener noreferrer"&gt;POSIX regex syntax&lt;/a&gt;, the pattern &lt;code&gt;.ork&lt;/code&gt; matches all four-character strings that end with the three characters “ork.” That period is more powerful than simple alternation, concatenation, and repetition, right?&lt;/p&gt;

&lt;p&gt;Nope. Truth be told, even the fanciest of regex &lt;a href="https://en.wikipedia.org/wiki/Metacharacter" rel="noopener noreferrer"&gt;&lt;strong&gt;metacharacters&lt;/strong&gt;&lt;/a&gt; — characters that invoke a regex operation — derive from regular expression operations. Assuming that the twenty-six lowercase letters of the alphabet are the only characters in the regular grammar, the regex pattern &lt;code&gt;.ork&lt;/code&gt; could be written using only regular expression operations as &lt;code&gt;[a|b|c|...|z]ork&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Though the sheer volume of metacharacters suggests that regex has a more powerful set of operations than regular expressions themselves, metacharacters are merely shortcuts for various permutations of the operations that define regular expressions. Regex metacharacters provide a programmer-friendly abstraction for common combinations of alternation, concatenation, and repetition.&lt;/p&gt;

&lt;p&gt;So far, I’ve portrayed regexes as regular expressions with amazing shortcuts and clear-cut use cases. However, as you may recall from Chomsky’s hierarchy, regular grammars have the strictest rules and no scope. Luckily, regexes have a little more leeway than their linguistic precursor, thereby bestowing  them with more practical power.&lt;/p&gt;

&lt;h3&gt;
  
  
  Breaking the Regular Grammar Rules
&lt;/h3&gt;

&lt;p&gt;Recall that, according to the the Chomsky hierarchy, regular grammars retain no knowledge in converting an input string to an output string. Since regular expressions are equivalent to regular grammars, this means that regular expressions also have no memory of the intermediate states of a string as it changed from input to output. It also means that translating a nonterminal in one part of a regular expressions has no bearing on the translation of a nonterminal in another part of the expression.&lt;/p&gt;

&lt;p&gt;For regexes, it’s a  different story. Regexes  violate this key regular grammar characteristic by supporting the ability to backreference. &lt;strong&gt;Backreferencing&lt;/strong&gt; allows the programmer to parenthetically separate a subsection of a regular expression and refer to it using a metacharacter. To give an example, the pattern &lt;code&gt;(la)\1&lt;/code&gt; matches “lala” by employing the &lt;code&gt;\1&lt;/code&gt; metacharacter to repeat the search for “la.”&lt;/p&gt;

&lt;p&gt;Because different parts of the string cannot influence one another in regular expressions, backreferencing gives regexes a lot more power than their predecessor. More importantly, backreferencing facilitates practical uses of regex such as searching for typos in which the same word was accidentally typed twice in a row. Pragmatism gives insight into why regular expressions were tweaked to create regexes in programming.&lt;/p&gt;

&lt;p&gt;Another feature that increases the functionality of regex is the ability to alter the greediness of the matching. Different &lt;strong&gt;quantifiers&lt;/strong&gt; — categories of regex patterns — can look similar but match drastically different parts of a string. A &lt;strong&gt;greedy quantifier&lt;/strong&gt; (*) will attempt to match as much of the string as possible, whereas a &lt;strong&gt;reluctant quantifier&lt;/strong&gt; (?) will try to match the minimum amount of characters in the string. Given the string “abcorgi,” the pattern &lt;code&gt;.*corgi&lt;/code&gt; would match the entire string but the pattern &lt;code&gt;.?corgi&lt;/code&gt; would only match “bcorgi.”&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;possessive quantifier&lt;/strong&gt; (+) also attempts to match as much of the string as possible, but, unlike the greedy quantifier, it will not backtrack to previous characters in the string in order to find the largest possible match. Given the string “abcorgi,” the patterns &lt;code&gt;.*corgi&lt;/code&gt; and &lt;code&gt;.+corgi&lt;/code&gt; would match the entire string. Though possessive and greedy qualifiers will often produce the same result, possessive qualifiers tend to be more efficient because they avoid backtracking.&lt;/p&gt;

&lt;p&gt;Because quantifiers are metacharacters, they can technically be built from alternation, concatenation, and repetition: the three operations of regular expressions. However, quantifiers create a simple abstraction that allows programmers to quickly specify what type of match they would like.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion &amp;amp; Further Reading
&lt;/h3&gt;

&lt;p&gt;What a journey we’ve undertaken! We learned about Chomsky and his eponymous hierarchy, then dove deeper into regular grammars. From regular grammars, we explored the linguistic definition of a regular expression. Finally, we used the differences between regular expressions and regexes to motivate how programmers use regex today.&lt;/p&gt;

&lt;p&gt;Although I trace the history of regular expressions from Chomsky to modern programming languages, this blog post is not the end of the regex story. If you’d like to learn more about linguistic and computational regular expressions, I have some motivating questions for you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What is automata theory and how does it relate to the Chomsky hierarchy?&lt;/li&gt;
&lt;li&gt;How is regex implemented? What are the tradeoffs of various regex algorithms?&lt;/li&gt;
&lt;li&gt;When is it appropriate to use regexes instead of built-in string match and manipulation libraries?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also have a list of resources that I used to study up on the linguistic and computational elements of regular expressions. Happy regex-ing!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.regular-expressions.info/" rel="noopener noreferrer"&gt;Regular-Expressions.info&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Regular_expression" rel="noopener noreferrer"&gt;Wikipedia: Regular Expressions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/8398030/chomsky-hierarchy-in-plain-english" rel="noopener noreferrer"&gt;StackOverflow: Chomsky Hierarchy in plain English&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.amazon.com/Introduction-Automata-Theory-Languages-Computation/dp/0321455363" rel="noopener noreferrer"&gt;&lt;em&gt;Introduction to Automata Theory, Languages, and Computation&lt;/em&gt;&lt;/a&gt; by Hopcroft et al.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cs.stackexchange.com/questions/45755/difference-between-regular-expression-and-grammar-in-automata" rel="noopener noreferrer"&gt;StackOverflow: Difference between regular expression and grammar in automata&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://interactivepython.org/courselib/static/thinkcspy/GeneralIntro/FormalandNaturalLanguages.html" rel="noopener noreferrer"&gt;How to Think like a Computer Scientist: Formal and Natural Languages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.oracle.com/javase/tutorial/essential/regex/quant.html" rel="noopener noreferrer"&gt;Oracle’s Java Tutorials: Quantifiers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cs.stackexchange.com/questions/53397/compare-regex-in-programming-languages-with-regular-expression-from-automata-for?rq=1" rel="noopener noreferrer"&gt;StackOverflow: Compare regex in programming languages with regular expression from automata/formal language&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.quora.com/How-are-regular-expressions-implemented" rel="noopener noreferrer"&gt;Quora: How are regular expressions implemented?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Enjoy what you read? Spread the love by liking and sharing this piece. Have thoughts or questions? Reach out to me on &lt;a href="https://twitter.com/alainakafkes" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or in the comments below. This post was originally published on &lt;a href="https://medium.com/@alainakafkes/exploring-the-linguistics-behind-regular-expressions-596fab41146" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;. Cover image credits belong to &lt;a href="https://xkcd.com/" rel="noopener noreferrer"&gt;xkcd&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>regularexpressions</category>
      <category>regex</category>
      <category>linguistics</category>
    </item>
    <item>
      <title>Rejection &amp; Revision: On Improving Conference Proposals</title>
      <dc:creator>Alaina Kafkes</dc:creator>
      <pubDate>Mon, 23 Oct 2017 15:07:49 +0000</pubDate>
      <link>https://forem.com/alainakafkes/rejection--revision-on-improving-conference-proposals-3fk</link>
      <guid>https://forem.com/alainakafkes/rejection--revision-on-improving-conference-proposals-3fk</guid>
      <description>&lt;p&gt;One of my goals for 2018 is to speak at more tech conferences.&lt;/p&gt;

&lt;p&gt;Although I gave talks at &lt;a href="http://selfconference.org/sessions#speaker_298"&gt;two&lt;/a&gt; &lt;a href="https://werise.tech/sessions/2017/5/30/build-her-up-on-supporting-university-women-in-tech"&gt;conferences&lt;/a&gt; in 2017, the call for proposals (CFP) process still feels opaque to me. Submitting a talk proposal to CFP reviewers is like dropping a letter into the void: usually, the only contact I receive from the reviewers or organizers about my proposal is an email stating whether they accepted or rejected my talk.&lt;/p&gt;

&lt;p&gt;Newcomers to the technical speaking circuit haven't mastered the CFP process yet. Receiving so little feedback – an auto-generated rejection most of the time – leaves them without a clear path to improve their proposal for the next CFP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For an industry that prides itself on iterative development, the black box nature of CFPs feels particularly egregious to me&lt;/strong&gt;. If a conference wishes to invest in scouting fresh technical speaking talent, feedback besides (and even before!) acceptance or rejection should be high priority.&lt;/p&gt;

&lt;p&gt;Despite the flaws of the CFP process, I have developed strategies to turn the rejections I receive into better proposals. I'll share the steps I took to turn my most recent rejections into revisions in the hopes that it will assuage the discouragement of speaker-hopefuls (like you!) and provide them with actions that they can take to improve their talk proposals.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ask for Feedback
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cQoAJbR7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/7_playing_cards.jpg/1200px-7_playing_cards.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cQoAJbR7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://upload.wikimedia.org/wikipedia/commons/thumb/8/80/7_playing_cards.jpg/1200px-7_playing_cards.jpg" alt="Card Suits Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I emailed two conference organizers asking for their reviewers' feedback on my proposal. Both organizers were quite willing to share this information with me.&lt;/p&gt;

&lt;p&gt;Before I provide excerpts of this feedback, I'd like to introduce a framework that I use to sift the useful feedback and discard the fluff: &lt;a href="https://twitter.com/lara_hogan"&gt;Lara Hogan&lt;/a&gt;'s card suits. In her book &lt;em&gt;Demystifying Public Speaking&lt;/em&gt;, she elaborates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hearts: "feedback that is positive, but not specific"&lt;/li&gt;
&lt;li&gt;Diamonds: "feedback that is positive, and specific or actionable"&lt;/li&gt;
&lt;li&gt;Clubs: "feedback that is negative, but not specific or constructive"&lt;/li&gt;
&lt;li&gt;Spades: "feedback that is negative, but gives specific suggestions"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;After receiving a rejection, speakers and speakers-to-be should seek diamonds and spades from the organizers&lt;/strong&gt;. I'll categorize some of the feedback I received for my rejected talk proposal:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"The audience participation section feels like something that would work great in a small meetup group or a tutorial session at a larger conference, but probably won't go quite as well in a 400-person single-track event."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I consider this feedback a diamond. As someone who has never spoken in front of a 400-person audience, it would be impossible for me to ascertain this fact without the wisdom of a seasoned conference speaker or organizer. This ill-placed audience participation could mark me as unprepared to give this talk, which is untrue. I have since removed this section from my talk proposal.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"[Topic] was not a topic that we saw other people submitting talks about. That is also a great feather in your cap for the uniqueness factor."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I consider this feedback a heart. When speaking with this organizer, it was clear that the reviewers liked my proposal, but not enough to select it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I think restructuring the talk/proposal to show evidence of why... [would make] you and your material sound more authoritative than anecdotal."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I consider this feedback a diamond or spade, depending on how negatively the organizer connotes the word "anecdotal." I have since cut out any weak language in my talk proposal and put more emphasis on explaining the why.&lt;/p&gt;

&lt;p&gt;I'm grateful to these conference organizers for providing me with mostly diamonds – feedback that I can use. When asking for feedback on a rejected talk proposal, push for those diamonds and spades.&lt;/p&gt;

&lt;h3&gt;
  
  
  Look to the Abstract
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bIsle1n6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blogwillis-zippykid.netdna-ssl.com/wp-content/uploads/2016/01/dark-canary_645x400.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bIsle1n6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://blogwillis-zippykid.netdna-ssl.com/wp-content/uploads/2016/01/dark-canary_645x400.jpg" alt="Canary in the Coal Mine Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Of all the sections of a talk proposal, the abstract is the canary in the coal mine&lt;/strong&gt;. If the abstract is bad – meaning unclear, illogical, or underdeveloped – then the rest of the proposal probably follows suit.&lt;/p&gt;

&lt;p&gt;Before I continue talking about the abstract, I'd like to outline the anatomy of a talk proposal. Typically, it has these three parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Abstract&lt;/strong&gt;: short, elevator pitch that motivates the talk and summarizes the desired outcome&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Description&lt;/strong&gt;: talk outline&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Notes&lt;/strong&gt;: any additional information, especially why &lt;em&gt;this&lt;/em&gt; speaker should give &lt;em&gt;this&lt;/em&gt; talk at &lt;em&gt;this&lt;/em&gt; conference&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since the abstract is often the first thing an organizer or reviewer sees on a talk proposal, it sets up their perception of a given talk. Even though abstracts tend to be looked at first, they should be written last: as in &lt;a href="https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3136027/"&gt;scientific research papers&lt;/a&gt;, abstracts act as introduction, content summary, and conclusion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The best way for a speaker-hopeful to vet and improve their abstract is, paradoxically, to elucidate their talk description&lt;/strong&gt;. Writing a razor-sharp description can help the speaker determine the biggest selling points of their talk, which is exactly what they should share in the abstract. I'll discuss honing the talk description more in the next section.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Speakers can also ameliorate their abstract by tailoring it to the unique traits of a conference&lt;/strong&gt;. If a speaker-hopeful wishes to present at a Ruby conference, then their abstract should include how their talk benefits Ruby developers. Even language-agnostic conferences have traits that a speaker can reflect in their abstract – all it takes is a little research to find their quirks! Tailoring an abstract to a conference can convince reviewers of the talk's value-add for their audience.&lt;/p&gt;

&lt;p&gt;The abstract that I submitted to conferences in September and October was, admittedly, not a good reflection of my talk description. It also was not tailored to the special characteristics of each conference. My abstract – and overall proposal – feels much more polished now that I have addressed these flaws. But, in order for me to strengthen my abstract, I first had to edit my description.&lt;/p&gt;

&lt;h3&gt;
  
  
  Whittle Down to the Essentials
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CfY2sSVY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://d2v9y0dukr6mq2.cloudfront.net/video/thumbnail/bright-north-star_byf-sv-zs__F0000.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CfY2sSVY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://d2v9y0dukr6mq2.cloudfront.net/video/thumbnail/bright-north-star_byf-sv-zs__F0000.png" alt="North Star Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I mentioned earlier, the talk description influences the direction an abstract should take. &lt;strong&gt;An airtight talk description leads to an airtight abstract&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The description section of my rejected proposal was thorough but lacked direction. I was trying to sell the audience on a thesis muddled by too many arguments.&lt;/p&gt;

&lt;p&gt;When I put together slides to give this talk at a meetup, I leaned on one of these arguments more heavily than others. This made me realize that my talk description didn't accurately reflect what I wanted to talk talk about. Instead of including every possible argument for my thesis, I rewrote my description with a focus on one.&lt;/p&gt;

&lt;p&gt;My choice to narrow the scope of this talk was validated at the meetup: attendees loved the clear cause-and-effect relationship that I presented. Confidently settling on a focal point for my talk also helped me eliminate wishy-washy language in my description, and as you can imagine, whittling down my talk description helped me write a stronger abstract for the next round of CFPs.&lt;/p&gt;

&lt;p&gt;But not every speaker-hopeful has taken the opportunity to practice their talk in front of an audience before applying to speak at conferences. &lt;strong&gt;Another technique that speakers use to find the linchpin of their proposed talk is the conclusion framework&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I learned about the conclusion framework from &lt;a href="https://twitter.com/goinggodotnet"&gt;Bill Kennedy&lt;/a&gt;. He shared it with me as an exercise for succinctly expressing the purpose of my talk.&lt;/p&gt;

&lt;p&gt;Here's my take on the conclusion framework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I believe... [thesis]&lt;/li&gt;
&lt;li&gt;I have shown you... [argument(s) that support thesis]&lt;/li&gt;
&lt;li&gt;We now both agree on... [reworded thesis]&lt;/li&gt;
&lt;li&gt;Go forth and... [call to action]&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I recommend that every speaker-hopeful fill in these four sentences at least once per talk proposal. The conclusion framework acts as the speaker-hopeful's North Star: it will keep them on track as they revise their abstract and description.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try a Meetup
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oVHOfxMm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3a87pfvokvbkpmgl3xp5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oVHOfxMm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/3a87pfvokvbkpmgl3xp5.jpg" alt="Meetup Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although talk proposals can be revised alone, &lt;strong&gt;there's nothing like presenting a talk in front of an audience to discover where a talk shines and where it flops&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When speaking at a meetup – especially a more intimate one – I preface my talk with instructions for my audience: feedback, please! I share my Twitter handle on every slide in my deck to encourage audience members to reach out. In the parlance of card suits, those who tweet at me usually share hearts.&lt;/p&gt;

&lt;p&gt;To dig for more diamonds and spades, after the conclusion and Q&amp;amp;A I like to ask the audience some questions of my own beyond "Any thoughts?":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Do you have any criticisms of my talk?"&lt;/li&gt;
&lt;li&gt;"How did you feel about [slide]"?&lt;/li&gt;
&lt;li&gt;"Was there anything I said that didn't feel clear?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One diamond I received at a meetup was that I sometimes use uncommon words – English words, not jargon – on my slides. Given that my talk deals with words more than code, utilizing language that will keep the audience engaged matters deeply to me. I never would have received this feedback without presenting in front of audience members who don't speak the way I do.&lt;/p&gt;

&lt;p&gt;Finding a meetup to speak at seems like it would be just as daunting as submitting talk proposals to conferences, but it's been so much more relaxed in my experience. I search &lt;a href="https://www.meetup.com/"&gt;Meetup&lt;/a&gt; for meetups in my area that interest me and email the organizer(s) to see if they have any open speaking opportunities.&lt;/p&gt;

&lt;h3&gt;
  
  
  Keep Submitting
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1YjwfzuT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ienl4m4dmb8oqa93rp24.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1YjwfzuT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ienl4m4dmb8oqa93rp24.jpg" alt="Inspiration Image"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the end of the day, there's only one way for a newcomer to speak at a conference: &lt;strong&gt;by continuing to submit talk proposals to conferences&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I hope that, by sharing with you – the speaker-hopeful – how I bounced back from my recent talk proposal rejections, you feel empowered to improve your talk proposals and own the next round of CFPs. Anytime you feel discouraged by rejection, remember that you can channel that negativity into actions that make your talk even better.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://abookapart.com/products/demystifying-public-speaking"&gt;Demystifying Public Speaking&lt;/a&gt; by &lt;a href="https://twitter.com/lara_hogan"&gt;Lara Hogan&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="http://helpmeabstract.com/"&gt;HelpMeAbstract&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://code.likeagirl.io/on-conference-proposal-rejections-205f7fead68"&gt;On conference proposal rejections&lt;/a&gt; by &lt;a href="https://twitter.com/limedaring"&gt;Tracy Osborn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://www.sarahmei.com/blog/2014/04/07/what-your-conference-proposal-is-missing/"&gt;What Your Conference Proposal Is Missing&lt;/a&gt; by &lt;a href="https://twitter.com/sarahmei"&gt;Sarah Mei&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Acknowledgments
&lt;/h3&gt;

&lt;p&gt;Thank you to everyoneÂ – and I am fortunate there are so many of you! –Â who has helped me navigate the labyrinthine world of technical speaking. This blog post wouldn't have happened without your support.&lt;/p&gt;

&lt;p&gt;Photo credits to &lt;a href="https://unsplash.com/"&gt;Unsplash&lt;/a&gt; and &lt;a href="http://www.wocintechchat.com/"&gt;#WoCInTechChat&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Enjoy what you read? Spread the love by sharing this piece. Have thoughts or questions? Reach out to me on &lt;a href="https://twitter.com/alainakafkes"&gt;Twitter&lt;/a&gt; or in the comments below.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This blog post was originally published in &lt;a href="https://alainakafkes.github.io/chronicles/"&gt;Chronicles of a Junior Dev&lt;/a&gt;, where I've been blogging about my first year as a software engineer. You can keep up-to-date with Chronicles by following me on &lt;a href="https://twitter.com/alainakafkes"&gt;Twitter&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>talks</category>
      <category>speaking</category>
      <category>conferences</category>
      <category>cfp</category>
    </item>
    <item>
      <title>What do you talk about during your one-on-one meetings with your engineering (or other) manager?</title>
      <dc:creator>Alaina Kafkes</dc:creator>
      <pubDate>Tue, 10 Oct 2017 21:52:47 +0000</pubDate>
      <link>https://forem.com/alainakafkes/what-do-you-talk-about-during-your-one-on-one-meetings-with-your-engineering-or-other-manager-e7d</link>
      <guid>https://forem.com/alainakafkes/what-do-you-talk-about-during-your-one-on-one-meetings-with-your-engineering-or-other-manager-e7d</guid>
      <description>&lt;p&gt;As much as I love chatting with my manager, sometimes I worry that I walk into our weekly one-on-ones woefully underprepared. My question in the discussion title says it all, but here are some related questions that I have about one-on-ones:&lt;/p&gt;

&lt;p&gt;What subjects are typically discussed during one-on-ones?&lt;/p&gt;

&lt;p&gt;What are your goals going into a one-on-one meeting?&lt;/p&gt;

&lt;p&gt;What can I – a new full-time software engineer – do to get the most out of my one-on-ones?&lt;/p&gt;

&lt;p&gt;I'd love to hear about any and all of your experiences. Thank you!&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Tackling Technical Writing</title>
      <dc:creator>Alaina Kafkes</dc:creator>
      <pubDate>Fri, 18 Aug 2017 14:23:39 +0000</pubDate>
      <link>https://forem.com/alainakafkes/tackling-technical-writing</link>
      <guid>https://forem.com/alainakafkes/tackling-technical-writing</guid>
      <description>

&lt;p&gt;I recently received a request from a reader for technical writing advice. As I sat down to respond, I thought back to something I heard &lt;a href="https://www.hanselman.com/"&gt;Scott Hanselman&lt;/a&gt; say at &lt;a href="https://werise.tech/"&gt;We Rise Conf&lt;/a&gt; in June: we only have &lt;a href="http://keysleft.com/"&gt;so many keystrokes left&lt;/a&gt; to type in our lives, so we may as well use them effectively. Why not share what I’ve learned from technical writing to a greater audience?&lt;/p&gt;

&lt;p&gt;If you’ve never read &lt;a href="http://alainakafk.es/#/words"&gt;my writing&lt;/a&gt; before, here’s a quick backstory.&lt;/p&gt;

&lt;p&gt;Though I’ve been an avid writer since the days of elementary school, I got into technical writing when I found out about Medium last year. The first pieces I read weren’t about code per se, but they covered subjects like &lt;a href="https://medium.com/@lamthuyvo/you-and-me-as-data-points-958ed4723f51"&gt;data as storytelling&lt;/a&gt;. I was hooked, and hastened to write my &lt;a href="https://medium.com/ladies-storm-hackathons/the-gender-gap-as-told-by-data-71dfce420519"&gt;first article&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Upon becoming a &lt;a href="http://blog.clarifai.com/clarifai-champions-round-2-meet-our-newest-future-developer-evangelists/#.WZb2FxJSyRt"&gt;Clarifai Champion&lt;/a&gt; in the fall of 2016, I challenged myself to write more about code. I created a well-received &lt;a href="https://medium.com/clarifai-champions/99-pr-oblems-a-beginners-guide-to-open-source-abc1b867385a"&gt;beginner’s guide to open source&lt;/a&gt;, and followed up with my &lt;a href="https://medium.freecodecamp.org/demystifying-dynamic-programming-3efafb8d4296"&gt;step-by-step dynamic programming tutorial&lt;/a&gt; this summer.&lt;/p&gt;

&lt;p&gt;Since August 2016, I’ve written a total of 10 technical writing pieces, and recently announced a &lt;a href="https://alainakafkes.github.io/chronicles/about/"&gt;new blogging initiative&lt;/a&gt;. I’d like to share five takeaways that I’ve learned from my year of technical writing.&lt;/p&gt;

&lt;h3&gt;
  
  
  (1) Break things into steps or sections.
&lt;/h3&gt;

&lt;p&gt;Every technical blog post I’ve written is broken down into smaller pieces to make it more digestible to an audience with a short attention span (aka most of us on the Internet). This way someone can skim through your blog post and determine if it’s in their best interest to read it. Similarly, section headers can help a reader easily bookmark where they left off in your piece.&lt;/p&gt;

&lt;h3&gt;
  
  
  (2) Write about how YOU solved a technical problem.
&lt;/h3&gt;

&lt;p&gt;I couldn’t find a comprehensive resource for getting into open source, so I created &lt;a href="https://medium.com/clarifai-champions/99-pr-oblems-a-beginners-guide-to-open-source-abc1b867385a"&gt;my own&lt;/a&gt;. I studied dynamic programming quite aggressively to succeed in my algorithms class, so I wrote about &lt;a href="https://medium.freecodecamp.org/demystifying-dynamic-programming-3efafb8d4296"&gt;how I solve problems that require dynamic programming&lt;/a&gt;. Writing about what you learned while solving a technical problem is &lt;em&gt;never&lt;/em&gt; a waste of words because it documents your growth, confirms your understanding, and provides a resource for future learners. Your technical writing will be more detailed and empathetic if you’ve been in the shoes of your audience!&lt;/p&gt;

&lt;h3&gt;
  
  
  (3) Don’t feel pressured to write only aboutÂ code.
&lt;/h3&gt;

&lt;p&gt;I’ve written about “softer topics like &lt;a href="https://medium.com/@alainakafkes/on-literature-linked-lists-6730308a0d81"&gt;how reading books can benefit engineers&lt;/a&gt;, which is a contender for my favorite technical blog post that I’ve ever written. You could write about a subject that matters to people in technologyâ€Š–â€Šsuch as takeaways from a conference, lessons learned in a first job, or thoughts on technical speakingâ€Š–â€Šand it would still be worthwhile technical writing. Remember, not all of us are software engineers.&lt;/p&gt;

&lt;h3&gt;
  
  
  (4) To gain an audience, leverage popular technical writing platforms.
&lt;/h3&gt;

&lt;p&gt;I got a lot of traction as a technical writer by getting pieces accepted in &lt;a href="https://medium.freecodecamp.org/"&gt;freeCodeCamp&lt;/a&gt;, which is one of the biggest technical writing publications on Medium with ~300,000 followers. I sometimes publish my writing on &lt;a href="https://dev.to/"&gt;dev.to&lt;/a&gt;, a technical writing platform that will tweet out your piece if they find it interesting, thereby reaching an audience of ~125,000 followers. By publishing in these and other well-known places, I gained more readership than I would have in my own network.&lt;/p&gt;

&lt;h3&gt;
  
  
  (5) Ask friends to look over yourÂ work!
&lt;/h3&gt;

&lt;p&gt;Behind every great technical writer is a team of proofreaders. Before you hit publish, it’s helpful to see how a few friends respond to what you’ve written. Does it make sense? Is it too wordy? Are there grammatical errors? Although any good friend will be inclined to think highly of your work, probe them for any mistakes, weak arguments, or other problems in your blog post. Criticism is more easily taken from a friend than a stranger on the Internet, and more easily resolved before you publish.&lt;/p&gt;

&lt;p&gt;These five tips have helped me write some pretty cool stuff over the past year, and I’m looking forward to my future technical writing endeavors. Now it’s your turn to try out technical writing. So get out there and whip up that blog post idea that’s been on your mind!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Enjoy what you read? Spread the love by sharing this piece. Have thoughts or questions? Reach out to me on &lt;a href="https://twitter.com/alainakafkes"&gt;Twitter&lt;/a&gt; or in the comments below.&lt;/em&gt;&lt;/p&gt;


</description>
      <category>technicalwriting</category>
      <category>writing</category>
      <category>developerevangelism</category>
      <category>advice</category>
    </item>
    <item>
      <title>The Goldilocks Contribution: How can someone find open source issues that challenge them just the right amount?</title>
      <dc:creator>Alaina Kafkes</dc:creator>
      <pubDate>Wed, 05 Jul 2017 13:16:54 +0000</pubDate>
      <link>https://forem.com/alainakafkes/the-goldilocks-contribution-how-can-someone-find-open-source-issues-that-challenge-them-just-the-right-amount</link>
      <guid>https://forem.com/alainakafkes/the-goldilocks-contribution-how-can-someone-find-open-source-issues-that-challenge-them-just-the-right-amount</guid>
      <description>&lt;p&gt;In open source, how can someone find their Goldilocks issue  —  one that stretches their skillset just right  —  in the chasm between contributions that are too easy and too hard?&lt;/p&gt;

&lt;p&gt;I'll use myself as an example. As someone who has already had a few pull requests merged, I find that issues labeled “beginner don't seem challenging enough to help me grow. However, some issues labeled "bug" or "feature" can be over my head. My goal in contributing to open source is to give back to programming communities I care about, but also to advance my technical skills.&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>Demystifying Dynamic Programming</title>
      <dc:creator>Alaina Kafkes</dc:creator>
      <pubDate>Wed, 28 Jun 2017 13:17:05 +0000</pubDate>
      <link>https://forem.com/alainakafkes/demystifying-dynamic-programming</link>
      <guid>https://forem.com/alainakafkes/demystifying-dynamic-programming</guid>
      <description>&lt;p&gt;Maybe you’ve heard about it in preparing for coding interviews. Maybe you’ve struggled through it in an algorithms course. Maybe you’re trying to learn how to code on your own, and were told somewhere along the way that it’s important to understand dynamic programming. Using dynamic programming (DP) to write algorithms is as essential as it is feared.&lt;/p&gt;

&lt;p&gt;And who can blame those who shrink away from it? Dynamic programming seems intimidating because it is ill-taught. Many tutorials focus on the outcomeâ€Š–â€Š&lt;em&gt;explaining&lt;/em&gt; the algorithmâ€Š–â€Šinstead of the processâ€Š–â€Š&lt;em&gt;finding&lt;/em&gt; the algorithmâ€Š–â€Šwhich encourages memorization, not understanding.&lt;/p&gt;

&lt;p&gt;Not this one.&lt;/p&gt;

&lt;p&gt;During my algorithms class this year, I pieced together my own process for solving problems that require dynamic programming. Parts of it come from &lt;a href="https://sites.northwestern.edu/hartline/" rel="noopener noreferrer"&gt;my algorithms professor&lt;/a&gt; (to whom much credit is due!), and parts from my own dissection of dynamic programming algorithms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In this post, I'll teach you my process for creating dynamic programming algorithms.&lt;/strong&gt; But before I share this process, let’s start with the basics. What is dynamic programming, anyway?&lt;/p&gt;

&lt;h2&gt;
  
  
  DP Defined
&lt;/h2&gt;

&lt;p&gt;Dynamic programming amounts to &lt;strong&gt;breaking down an optimization problem&lt;/strong&gt; into simpler sub-problems, and &lt;strong&gt;storing the solution to each sub-problem&lt;/strong&gt; so that each sub-problem is only solved once.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-868089738669899776-411" src="https://platform.twitter.com/embed/Tweet.html?id=868089738669899776"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-868089738669899776-411');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=868089738669899776&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;To be honest, this definition may not make total sense until you see an example of a sub-problem. That’s okayâ€Š–â€Šit’s coming up in the next section.&lt;/p&gt;

&lt;p&gt;What I hope to convey is that DP is a useful technique because it looks through all possible sub-problems and never recomputes the solution to any sub-problem. This &lt;strong&gt;guarantees correctness and efficiency&lt;/strong&gt;, which cannot be said of most techniques used to solve or approximate algorithms. This alone makes DP special.&lt;/p&gt;

&lt;p&gt;In the next two sections, I’ll explain what a &lt;strong&gt;sub-problem&lt;/strong&gt; is, and then motivate why storing solutionsâ€Š–â€Ša technique known as &lt;strong&gt;memoization&lt;/strong&gt;â€Š–â€Šmatters in dynamic programming.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sub-problems on Sub-problems on Sub-problems
&lt;/h2&gt;

&lt;p&gt;Sub-problems are simply &lt;strong&gt;smaller versions of the original problem&lt;/strong&gt;. In fact, sub-problems often look like a reworded version of the original problem. If formulated correctly, sub-problems build on each another in order to obtain the solution to the original problem.&lt;/p&gt;

&lt;p&gt;To give you a better idea of how this works, let’s find the sub-problem in an example dynamic programming problem.&lt;/p&gt;

&lt;p&gt;Pretend you’re back in the 1950s working on an IBM-650 computer. You know what this meansâ€Š–â€Špunchcards! Your job is to manâ€Š–â€Šor womanâ€Š–â€Šthe IBM-650 for a day. You’re given a natural number &lt;em&gt;n&lt;/em&gt; punchcards to run. Each punchcard &lt;em&gt;i&lt;/em&gt; must be run at some predetermined start time &lt;em&gt;si&lt;/em&gt; and stop running at some predetermined finish time &lt;em&gt;fi&lt;/em&gt;. Only one punchcard can run on the IBM-650 at once. Each punchcard also has an associated value &lt;em&gt;vi&lt;/em&gt; based on how important it is to your company.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpkk6rlyqz1mx1vtww3wk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpkk6rlyqz1mx1vtww3wk.jpg" alt="Punchcard Problem"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: As the person in charge of the IBM-650, you must determine the optimal schedule of punchcards that maximizes the total value of all punchcards run.&lt;/p&gt;

&lt;p&gt;Because I’ll go through this example in great detail throughout this article, I’ll only tease you with its sub-problem for now:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sub-problem&lt;/strong&gt;: The maximum value schedule for punchcards &lt;em&gt;i&lt;/em&gt; through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time.&lt;/p&gt;

&lt;p&gt;Notice how the sub-problem breaks down the original problem into components that build up the solution. With the sub-problem, you can find the maximum value schedule for punchcards &lt;em&gt;n&lt;/em&gt;-1 through &lt;em&gt;n&lt;/em&gt;, and then for punchcards &lt;em&gt;n&lt;/em&gt;-2 through &lt;em&gt;n&lt;/em&gt;, et cetera. By finding the solutions for every single sub-problem, you can then tackle the original problem itself: the maximum value schedule for punchcards 1 through &lt;em&gt;n&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Otherwise put, since the sub-problem looks like the original problem, sub-problems can be used to solve the original problem.&lt;/p&gt;

&lt;p&gt;In dynamic programming, after you solve each sub-problem, you must memoizeâ€Š–â€Šor storeâ€Š–â€Šit. Let’s find out why in the following section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivating Memoization with Fibonacci Numbers
&lt;/h2&gt;

&lt;p&gt;When told to implement an algorithm that calculates the &lt;a href="https://www.mathsisfun.com/numbers/fibonacci-sequence.html" rel="noopener noreferrer"&gt;Fibonacci value&lt;/a&gt; for any given number, what would you do? Most people I know would opt for a &lt;a href="https://softwareengineering.stackexchange.com/questions/25052/in-plain-english-what-is-recursion" rel="noopener noreferrer"&gt;recursive algorithm&lt;/a&gt; that looks something like this in Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fibonacciVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;fibonacciVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;fibonacciVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This algorithm accomplishes its purpose, but at a &lt;em&gt;huge&lt;/em&gt; cost. For example, let’s look at what this algorithm must calculate in order to solve for n=5 (abbreviated as F(5)):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                      F(5)  
                    /      \                  
                   /        \
                  /          \
               F(4)          F(3)
            /       \        /   \
          F(3)     F(2)     F(2)  F(1)
         /   \     /  \     /   \
       F(2) F(1) F(1) F(0) F(1) F(0)
       /  \
     F(1) F(0)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tree above represents each computation that must be made in order to find the Fibonacci value for n=5. Notice how the sub-problem for n=2 is solved &lt;em&gt;thrice&lt;/em&gt;. For a relatively small example (n=5), that’s a lot of repeatedâ€Š–â€Šand wastedâ€Š–â€Šcomputation!&lt;/p&gt;

&lt;p&gt;What if, instead of calculating the Fibonacci value for n = 2 three times, we created an algorithm that calculates it once, stores its value, and accesses the stored Fibonacci value for every subsequent occurrence of n=2? That’s &lt;em&gt;exactly&lt;/em&gt; what memoization does.&lt;/p&gt;

&lt;p&gt;With this idea in mind, I’ve written a dynamic programming solution to the Fibonacci value problem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fibonacciVal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;memo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how the solutionâ€Š–â€Šthe return valueâ€Š–â€Šcomes from the memoization array memo[], which is iteratively filled in by the for loop. By “iteratively,” I mean that memo[2] is calculated and stored before memo[3], memo[4],Â …, and memo[&lt;em&gt;n&lt;/em&gt;]. Because memo[] is filled in this order, &lt;strong&gt;the solution for each sub-problem&lt;/strong&gt; (e.g., n=3) &lt;strong&gt;can be found using the solutions to its preceding sub-problems&lt;/strong&gt; (e.g., n=2 and n=1) because these values were already stored in memo[] at an earlier time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memoization means no re-computation&lt;/strong&gt;, which makes for a more efficient algorithm. Thus, memoization ensures that dynamic programming is efficient, but it is choosing the right sub-problem that guarantees overall correctness.&lt;/p&gt;

&lt;p&gt;Now that we’ve addressed memoization and sub-problems, it’s time to learn the dynamic programming process. Buckle in.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Dynamic Programming Process
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Step 1: Identify the sub-problem inÂ words.
&lt;/h3&gt;

&lt;p&gt;Too often, programmers will turn to writing code &lt;em&gt;before&lt;/em&gt; thinking critically about the problem at hand. Not good. One strategy for firing up your brain before you touch the keyboard is using wordsâ€Š–â€ŠEnglish or otherwiseâ€Š–â€Što describe the sub-problem that you have identified within the original problem.&lt;/p&gt;

&lt;p&gt;If you’re solving a problem that requires dynamic programming, grab a piece of paper and think about the information that you need in order to solve this problem. Write out the sub-problem with this in mind.&lt;/p&gt;

&lt;p&gt;For example, in the aforementioned punchcard problem, I stated that the sub-problem can be written as “the maximum value schedule for punchcards &lt;em&gt;i&lt;/em&gt; through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time.” I found this sub-problem by realizing that, in order to determine the maximum value schedule for punchcards 1 through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time, I would need to find the answer to the following sub-problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The maximum value schedule for punchcards &lt;em&gt;n&lt;/em&gt;-1 through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time&lt;/li&gt;
&lt;li&gt;The maximum value schedule for punchcards &lt;em&gt;n&lt;/em&gt;-2 through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time&lt;/li&gt;
&lt;li&gt;The maximum value schedule for punchcards &lt;em&gt;n&lt;/em&gt;-3 through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time&lt;/li&gt;
&lt;li&gt;(Et cetera)&lt;/li&gt;
&lt;li&gt;The maximum value schedule for punchcards 2 through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you can &lt;strong&gt;identify a sub-problem that builds upon previous sub-problems&lt;/strong&gt; in order to solve the problem at hand, then you’re on the right track.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Write out the sub-problem as a recurring mathematical decision.
&lt;/h3&gt;

&lt;p&gt;Once you’ve identified a sub-problem in words, it’s time to write it out mathematically. Why must this be done? Well, the mathematical &lt;strong&gt;recurrence&lt;/strong&gt;â€Š–â€Šor repeated decisionâ€Š–â€Šthat you find will eventually be what you put into your code. Besides, writing out the sub-problem mathematically vets your sub-problem in words from Step 1. If it is difficult to encode your sub-problem from Step 1 in  math, then it may be the wrong sub-problem!&lt;/p&gt;

&lt;p&gt;There are two questions that I ask myself every time I try to find a recurrence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What decision do I make at every step?&lt;/li&gt;
&lt;li&gt;If my algorithm is at step &lt;em&gt;i&lt;/em&gt;, what information would it need to decide what to do in step &lt;em&gt;i&lt;/em&gt;+1? (And sometimes: If my algorithm is at step &lt;em&gt;i&lt;/em&gt;, what information did it need to decide what to do in step &lt;em&gt;i&lt;/em&gt;-1?)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s return to the punchcard problem and ask these questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What decision do I make at every step?&lt;/strong&gt; Assume that the punchcards are sorted by start time, as mentioned previously. For each punchcard that is compatible with the schedule so far (i.e., its start time is after the finish time of the punchcard that is currently running), the algorithm must choose between two options: to run, or not to run, the punchcard.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftt9sj76gezeqrvu4vqeg.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftt9sj76gezeqrvu4vqeg.jpg" alt="Hamlet &amp;amp; Dynamic Programming"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If my algorithm is at step &lt;em&gt;i&lt;/em&gt;, what information would it need to decide what to do in step &lt;em&gt;i&lt;/em&gt;+1?&lt;/strong&gt; In order to decide between the two options, the algorithm needs to know the next compatible punchcard in the order.Â &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If my algorithm is at step &lt;em&gt;i&lt;/em&gt;, what information did it need to decide what to do in step &lt;em&gt;i&lt;/em&gt;-1?&lt;/strong&gt; The algorithm needs to know about future decisions: the ones made for punchcards &lt;em&gt;i&lt;/em&gt; through &lt;em&gt;n&lt;/em&gt; in order to decide to run or not to run punchcard &lt;em&gt;i&lt;/em&gt;-1.&lt;/p&gt;

&lt;p&gt;Now that we’ve answered these questions, perhaps you’ve started to form a recurring mathematical decision in your mind. If not, that’s also okayâ€Š–â€Šit becomes easier and easier to write recurrences as you get exposed to more dynamic programming problems.&lt;/p&gt;

&lt;p&gt;Without further ado, here’s our recurrence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OPT(&lt;em&gt;i&lt;/em&gt;) = &lt;strong&gt;max&lt;/strong&gt;(&lt;em&gt;vi&lt;/em&gt; + OPT(next[&lt;em&gt;i&lt;/em&gt;]), OPT(&lt;em&gt;i&lt;/em&gt;+1))&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This mathematical recurrence requires some explaining, especially for those who haven’t written one before. Here, I use OPT(&lt;em&gt;i&lt;/em&gt;) to represent the maximum value schedule for punchcards &lt;em&gt;i&lt;/em&gt; through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time. Sounds familiar, right? OPT(â€¢) is our sub-problem from Step 1.&lt;/p&gt;

&lt;p&gt;In order to determine the value of OPT(&lt;em&gt;i&lt;/em&gt;), we consider two options, and we want to take the &lt;em&gt;maximum&lt;/em&gt; of these options in order to meet our goal: the &lt;em&gt;maximum&lt;/em&gt; value schedule for all punchcards. Once we choose the option that gives the maximum result at step &lt;em&gt;i&lt;/em&gt;, we memoize its value as OPT(&lt;em&gt;i&lt;/em&gt;).&lt;/p&gt;

&lt;p&gt;The two optionsâ€Š–â€Što run or not to run punchcard &lt;em&gt;i&lt;/em&gt;â€Š–â€Šare represented mathematically as follows:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;vi&lt;/em&gt; + OPT(next[&lt;em&gt;i&lt;/em&gt;])&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This clause represents the decision to run punchcard &lt;em&gt;i&lt;/em&gt;. It adds the value gained from running punchcard &lt;em&gt;i&lt;/em&gt; to OPT(next[&lt;em&gt;i&lt;/em&gt;]), where next[&lt;em&gt;i&lt;/em&gt;] represents the next compatible punchcard following punchcard &lt;em&gt;i&lt;/em&gt;. OPT(next[&lt;em&gt;i&lt;/em&gt;]) gives the maximum value schedule for punchcards next[&lt;em&gt;i&lt;/em&gt;] through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time. Adding these two values together produces maximum value schedule for punchcards &lt;em&gt;i&lt;/em&gt; through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time &lt;em&gt;if punchcard i is run&lt;/em&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OPT(&lt;em&gt;i&lt;/em&gt;+1)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Conversely, this clause represents the decision to not run punchcard &lt;em&gt;i&lt;/em&gt;. If punchcard &lt;em&gt;i&lt;/em&gt; is not run, its value is not gained. OPT(&lt;em&gt;i&lt;/em&gt;+1) gives the maximum value schedule for punchcards &lt;em&gt;i&lt;/em&gt;+1 through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time. Otherwise stated, OPT(&lt;em&gt;i&lt;/em&gt;+1) gives the maximum value schedule for punchcards &lt;em&gt;i&lt;/em&gt; through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time &lt;em&gt;if punchcard i is not run&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In this way, the decision made at each step of the punchcard problems is encoded mathematically to reflect the sub-problem in Step 1.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Solve the original problem using Steps 1 andÂ 2.
&lt;/h3&gt;

&lt;p&gt;In Step 1, we wrote down the sub-problem for the punchcard problem in words. In Step 2, we wrote down a recurring mathematical decision that corresponds to these sub-problems. How can we solve the original problem with this information?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OPT(1)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It’s that simple. Since the sub-problem we found in Step 1 is the maximum value schedule for punchcards &lt;em&gt;i&lt;/em&gt; through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time, we can write out the solution to the original problem as the maximum value schedule for punchcards 1 through &lt;em&gt;n&lt;/em&gt; such that the punchcards are sorted by start time. Since Steps 1 and 2 go hand in hand, the original problem can also be written as OPT(1).&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Determine the dimensions of the memoization array and the direction in which it should beÂ filled.
&lt;/h3&gt;

&lt;p&gt;Did you find Step 3 deceptively simple? It sure seems that way. You may be thinking, how can OPT(1) be the solution to our dynamic program if it relies on OPT(2), OPT(next[1]), et cetera?&lt;/p&gt;

&lt;p&gt;You’re correct to notice that OPT(1) relies on the solution to OPT(2). This follows directly from Step 2:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OPT(1) = &lt;strong&gt;max&lt;/strong&gt;(&lt;em&gt;v1&lt;/em&gt; + OPT(next[1]), OPT(2))&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But this is not a crushing issue. Think back to Fibonacci memoization example. In order to find the Fibonacci value for n=5, the algorithm relied on the fact that the Fibonacci values for n=4, n=3, n=2, n=1, and n=0 had already been memoized. If we fill in our memoization table in the correct order, the reliance of OPT(1) on other sub-problems is no big deal.&lt;/p&gt;

&lt;p&gt;How can we identify the correct direction to fill the memoization table? Well, in the punchcard problem, since we know OPT(1) relies on the solutions to OPT(2) and OPT(next[1]), and that punchcards 2 and next[1] have start times after punchcard 1 due to sorting, we can infer that we need to fill our memoization table from OPT(&lt;em&gt;n&lt;/em&gt;) to OPT(1).&lt;/p&gt;

&lt;p&gt;How do we determine the dimensions of this memoization array? Here’s a trick: &lt;strong&gt;the dimensions of the array are equivalent to the &lt;em&gt;number&lt;/em&gt; and &lt;em&gt;size&lt;/em&gt; of the variables on which OPT(â€¢) relies&lt;/strong&gt;. In the punchcard problem, we have OPT(i), which means that OPT(â€¢) only relies on variable &lt;em&gt;i&lt;/em&gt;, which represents the punchcard number. This suggest that our memoization array will be one-dimensional and that its size will be &lt;em&gt;n&lt;/em&gt; since there are &lt;em&gt;n&lt;/em&gt; total punchcards.&lt;/p&gt;

&lt;p&gt;If we know that n=5, then our memoization array might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;memo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;OPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;OPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;OPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;OPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;OPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, because many programming languages &lt;a href="https://en.wikipedia.org/wiki/Zero-based_numbering" rel="noopener noreferrer"&gt;start indexing arrays at 0&lt;/a&gt;, it may be more convenient to create this memoization array so that its indices align with punchcard numbers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;memo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;OPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;OPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;OPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nc"&gt;OPT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 5: CodeÂ it!
&lt;/h3&gt;

&lt;p&gt;To code our dynamic program, we put together Steps 2–4. The only new piece of information that you’ll need to write a dynamic program is a base case, which you can find as you tinker with your algorithm.&lt;/p&gt;

&lt;p&gt;A dynamic program for the punchcard problem will look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;punchcardSchedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="s"&gt;Where values stores the punchcard values (ordered by punchcard start time)
        &amp;amp; next[i] stores the next punchcard that can run after punchcard i&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;
 &lt;span class="c1"&gt;# Initialize memoization array - Step 4
&lt;/span&gt;  &lt;span class="n"&gt;memo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="c1"&gt;# Set base case
&lt;/span&gt;  &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

 &lt;span class="c1"&gt;# Build memoization table from n to 1 - Step 2
&lt;/span&gt;  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

 &lt;span class="c1"&gt;# Return solution to original problem OPT(1) - Step 3
&lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congrats on writing your first dynamic program! Now that you’ve wet your feet, I’ll walk you through a different type of dynamic program.&lt;/p&gt;

&lt;h2&gt;
  
  
  Paradox of Choice: Multiple Options DPÂ Example
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00ihu54mqus939ycj36t.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F00ihu54mqus939ycj36t.jpg" alt="Grizelda &amp;amp; Dynamic Programming"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Although the previous dynamic programming example had a two-option decisionâ€Š–â€Što run or not to run a punchcardâ€Š–â€Šsome problems require that &lt;strong&gt;multiple options be considered before a decision can be made&lt;/strong&gt; at each step.&lt;/p&gt;

&lt;p&gt;Time for a new example.&lt;/p&gt;

&lt;p&gt;Pretend you’re selling the friendship bracelets to &lt;em&gt;n&lt;/em&gt; customers, and the value of that product increases monotonically. This means that the product has prices {&lt;em&gt;p1&lt;/em&gt;,Â …, &lt;em&gt;pn&lt;/em&gt;} such that &lt;em&gt;pi&lt;/em&gt; â‰¤ &lt;em&gt;pj&lt;/em&gt; if customer &lt;em&gt;j&lt;/em&gt; comes after customer &lt;em&gt;i&lt;/em&gt;. These &lt;em&gt;n&lt;/em&gt; customers have values {&lt;em&gt;v1&lt;/em&gt;,Â …, &lt;em&gt;vn&lt;/em&gt;}. A given customer &lt;em&gt;i&lt;/em&gt; will buy a friendship bracelet at price &lt;em&gt;pi&lt;/em&gt; if and only if &lt;em&gt;pi&lt;/em&gt; â‰¤ &lt;em&gt;vi&lt;/em&gt;; otherwise the revenue obtained from that customer is 0. Assume prices are natural numbers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: You must find the set of prices that ensure you the maximum possible revenue from selling your friendship bracelets.&lt;/p&gt;

&lt;p&gt;Take a second to think about how you might address this problem before looking at my solutions to Steps 1 and 2.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Identify the sub-problem inÂ words.
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sub-problem&lt;/strong&gt;: The maximum revenue obtained from customers &lt;em&gt;i&lt;/em&gt; through &lt;em&gt;n&lt;/em&gt; such that the price for customer &lt;em&gt;i&lt;/em&gt;-1 was set at &lt;em&gt;q&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I found this sub-problem by realizing that, in order to determine the maximum revenue for customers 1 through &lt;em&gt;n&lt;/em&gt;, I would need to find the answer to the following sub-problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The maximum revenue obtained from customers &lt;em&gt;n&lt;/em&gt;-1 through &lt;em&gt;n&lt;/em&gt; such that the price for customer &lt;em&gt;n&lt;/em&gt;-2 was set at &lt;em&gt;q&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;The maximum revenue obtained from customers &lt;em&gt;n&lt;/em&gt;-2 through &lt;em&gt;n&lt;/em&gt; such that the price for customer &lt;em&gt;n&lt;/em&gt;-3 was set at &lt;em&gt;q&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;(Et cetera)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice that I introduced a second variable &lt;em&gt;q&lt;/em&gt; into the sub-problem. I did this because, in order to solve each sub-problem, I need to know the price I set for the customer &lt;em&gt;before&lt;/em&gt; that sub-problem. Variable &lt;em&gt;q&lt;/em&gt; ensures the monotonic nature of the set of prices, and variable &lt;em&gt;i&lt;/em&gt; keeps track of the current customer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Write out the sub-problem as a recurring mathematical decision.
&lt;/h3&gt;

&lt;p&gt;There are two questions that I ask myself every time I try to find a recurrence:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What decision do I make at every step?&lt;/li&gt;
&lt;li&gt;If my algorithm is at step &lt;em&gt;i&lt;/em&gt;, what information would it need to decide what to do in step &lt;em&gt;i&lt;/em&gt;+1? (And sometimes: If my algorithm is at step &lt;em&gt;i&lt;/em&gt;, what information did it need to decide what to do in step &lt;em&gt;i&lt;/em&gt;-1?)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s return to the friendship bracelet problem and ask these questions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What decision do I make at every step?&lt;/strong&gt; I decide at which price to sell my friendship bracelet to the current customer. Since prices must be natural numbers, I know that I should set my price for customer &lt;em&gt;i&lt;/em&gt; in the range from &lt;em&gt;q&lt;/em&gt;â€Š–â€Šthe price set for customer &lt;em&gt;i&lt;/em&gt;-1â€Š–â€Što &lt;em&gt;viâ€Š&lt;/em&gt;–â€Šthe maximum price at which customer &lt;em&gt;i&lt;/em&gt; will buy a friendship bracelet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If my algorithm is at step &lt;em&gt;i&lt;/em&gt;, what information would it need to decide what to do in step &lt;em&gt;i&lt;/em&gt;+1?&lt;/strong&gt; My algorithm needs to know the price set for customer &lt;em&gt;i&lt;/em&gt; and the value of customer &lt;em&gt;i&lt;/em&gt;+1 in order to decide at what natural number to set the price for customer &lt;em&gt;i&lt;/em&gt;+1.&lt;/p&gt;

&lt;p&gt;With this knowledge, I can mathematically write out the recurrence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OPT(&lt;em&gt;i&lt;/em&gt;, &lt;em&gt;q&lt;/em&gt;) = &lt;strong&gt;max~&lt;/strong&gt;([Revenue(&lt;em&gt;vi&lt;/em&gt;, &lt;em&gt;a&lt;/em&gt;) + OPT(&lt;em&gt;i&lt;/em&gt;+1, &lt;em&gt;a&lt;/em&gt;)])&lt;br&gt;
such that &lt;strong&gt;max~&lt;/strong&gt; finds the maximum over all &lt;em&gt;a&lt;/em&gt; in the range &lt;em&gt;q&lt;/em&gt; â‰¤ &lt;em&gt;a&lt;/em&gt; â‰¤ &lt;em&gt;vi&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once again, this mathematical recurrence requires some explaining. Since the price for customer &lt;em&gt;i&lt;/em&gt;-1 is &lt;em&gt;q&lt;/em&gt;, for customer &lt;em&gt;i&lt;/em&gt;, the price a either stays at integer &lt;em&gt;q&lt;/em&gt; or it changes to be some integer between &lt;em&gt;q&lt;/em&gt;+1 and &lt;em&gt;vi&lt;/em&gt;. To find the total revenue, we add the revenue from customer &lt;em&gt;i&lt;/em&gt; to the maximum revenue obtained from customers &lt;em&gt;i&lt;/em&gt;+1 through &lt;em&gt;n&lt;/em&gt; such that the price for customer &lt;em&gt;i&lt;/em&gt; was set at &lt;em&gt;a&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;In other words, to maximize the total revenue, the algorithm must find the optimal price for customer &lt;em&gt;i&lt;/em&gt; by checking all possible prices between &lt;em&gt;q&lt;/em&gt; and &lt;em&gt;vi&lt;/em&gt;. If &lt;em&gt;vi&lt;/em&gt; â‰¤ &lt;em&gt;q&lt;/em&gt;, then the price a must remain at &lt;em&gt;q&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about the otherÂ steps?
&lt;/h3&gt;

&lt;p&gt;Working through Steps 1 and 2 is the most difficult part of dynamic programming. As an exercise, I suggest you work through Steps 3, 4, and 5 on your own to check your understanding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime Analysis of DynamicÂ Programs
&lt;/h2&gt;

&lt;p&gt;Now for the fun part of writing algorithms: &lt;strong&gt;runtime analysis&lt;/strong&gt;. I’ll be using big-O notation throughout this discussionâ€Š–â€Šif you’re not yet familiar with big-O, I suggest you read up on it &lt;a href="https://www.interviewcake.com/article/java/big-o-notation-time-and-space-complexity" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Generally, a dynamic program’s runtime is composed of the following features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pre-processing&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How many times the for loop runs&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How much time it takes the recurrence to run in one for loop iteration&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Post-processing&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall runtime takes the following form:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Overall runtime = Pre-processing + Loop * Recurrence + Post-processing&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s perform a runtime analysis of the punchcard problem in order to get familiar with big-O for dynamic programs. Here is the punchcard problem dynamic program:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;punchcardSchedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
 &lt;span class="sh"&gt;'''&lt;/span&gt;&lt;span class="s"&gt;Where values stores the punchcard values (ordered by punchcard start time)
        &amp;amp; next[i] stores the next punchcard that can run after punchcard i&lt;/span&gt;&lt;span class="sh"&gt;'''&lt;/span&gt;
 &lt;span class="c1"&gt;# Initialize memoization array - Step 4
&lt;/span&gt;  &lt;span class="n"&gt;memo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

 &lt;span class="c1"&gt;# Set base case
&lt;/span&gt;  &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

 &lt;span class="c1"&gt;# Build memoization table from n to 1 - Step 2
&lt;/span&gt;  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;next&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

 &lt;span class="c1"&gt;# Return solution to original problem OPT(1) - Step 3
&lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s break down its runtime:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pre-processing&lt;/strong&gt;: Here, this means building the the memoization array. O(n).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How many times the for loop runs&lt;/strong&gt;: O(n).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;How much time it takes the recurrence to run in one for loop iteration&lt;/strong&gt;: The recurrence takes constant time to run because it makes a decision between two options in each iteration. O(1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-processing&lt;/strong&gt;: None here! O(1).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The overall runtime of the punchcard problem dynamic program is O(n) O(n) * O(1) + O(1), or, in simplified form, O(n).&lt;/p&gt;

&lt;h2&gt;
  
  
  You Did It!
&lt;/h2&gt;

&lt;p&gt;Well, that’s itâ€Š–â€Šyou’re one step closer to becoming a dynamic programming wizard!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0a5lua6jy48m94vzxk97.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0a5lua6jy48m94vzxk97.gif" alt="Aziz loves CS!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One final piece of wisdom: &lt;strong&gt;keep practicing dynamic programming&lt;/strong&gt;. No matter how frustrating these algorithms may seem, repeatedly writing dynamic programs will make the sub-problems and recurrences come to you more naturally. Here’s a crowdsourced list of &lt;a href="https://www.quora.com/What-are-the-top-10-most-popular-dynamic-programming-problems-among-interviewers" rel="noopener noreferrer"&gt;classic dynamic programming problems&lt;/a&gt; for you to try.&lt;/p&gt;

&lt;p&gt;So get out there and take on your interviews, classes, and &lt;em&gt;life&lt;/em&gt; (of course) with your newfound dynamic programming knowledge!&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Many thanks to Steven Bennett, Claire Durand, and Prithaj Nath for proofreading this blog post. You all rock. Thank you Professor Hartline for getting me so excited about dynamic programming that I willingly wrote about it at length.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Enjoy what you read? Spread the love by liking and sharing this piece! Have thoughts or questions? Reach out to me on &lt;a href="https://twitter.com/alainakafkes" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or in the comments below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>dynamicprogramming</category>
      <category>algorithms</category>
      <category>interviews</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>How can someone prepare for their first full-time software engineering job?</title>
      <dc:creator>Alaina Kafkes</dc:creator>
      <pubDate>Thu, 25 May 2017 15:43:00 +0000</pubDate>
      <link>https://forem.com/alainakafkes/how-can-someone-prepare-for-their-first-full-time-software-engineering-job</link>
      <guid>https://forem.com/alainakafkes/how-can-someone-prepare-for-their-first-full-time-software-engineering-job</guid>
      <description>

&lt;p&gt;Alternatively: what do you expect from new grad/junior software engineers that join your team?&lt;/p&gt;

&lt;p&gt;Thanks in advance for your thoughts. ðŸŽ‰&lt;/p&gt;


</description>
      <category>discuss</category>
      <category>softwareengineering</category>
      <category>advice</category>
      <category>beginners</category>
    </item>
    <item>
      <title>On Literature and Linked Lists</title>
      <dc:creator>Alaina Kafkes</dc:creator>
      <pubDate>Mon, 08 May 2017 20:03:16 +0000</pubDate>
      <link>https://forem.com/alainakafkes/on-literature-and-linked-lists</link>
      <guid>https://forem.com/alainakafkes/on-literature-and-linked-lists</guid>
      <description>

&lt;p&gt;Up until recently, my experience with reading for pleasure followed a typical-but-sad trajectory. I got hooked on reading at a young age, often consuming several novels per week. Then, high school extracurriculars and college applications caused my reading frequency to nosedive to almost never. This habit followed me into college: part of why I studied computer science was its promise of project-based work instead of the incessant reading that characterizes many other majors.&lt;/p&gt;

&lt;p&gt;Reading decline is &lt;a href="https://www.washingtonpost.com/news/wonk/wp/2016/09/07/the-long-steady-decline-of-literary-reading/?utm_term=.5603c51e2c14"&gt;not a new story&lt;/a&gt;, and fortunately, it was not the end of mine. Remembering the glory days of elementary school, I grabbed &lt;a href="http://www.goodreads.com/book/show/18770267-make-it-stick"&gt;&lt;em&gt;Make It Stick&lt;/em&gt;&lt;/a&gt; and &lt;a href="http://www.goodreads.com/book/show/77103.The_Glass_Palace"&gt;&lt;em&gt;The Glass Palace&lt;/em&gt;&lt;/a&gt; â€Š– â€Štwo books that friends had lent to me – and finished them over my winter break. These books drew me back in, hook, line, and sinker. Since winter break, I have pored through ten other books. It feels fantastic to reconnect with this childhood joy!&lt;/p&gt;

&lt;p&gt;However, as a computer science latecomer, I’ve always felt the pressure to code, code, &lt;em&gt;code&lt;/em&gt; in my spare time. I couldn’t help but wonder:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;em&gt;Would spending my free time reading hinder the development of my software engineering skills?&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;My conclusion? Improving technical acumen is &lt;em&gt;super&lt;/em&gt; important, but readers have a few traits from which software engineers can learn. &lt;strong&gt;I present to you the ways in which I believe reading can make someoneâ€Š – â€Šlike me, and perhaps like you! â€Š–â€Š a better software engineer.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(Slight caveat: my arguments may also apply to non-engineering roles, but I frame them in the context of the career I know best.)&lt;/p&gt;




&lt;h2&gt;
  
  
  Reading books from different voices enhances yourÂ empathy.
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://journals.plos.org/plosone/article?id=10.1371/journal.pone.0055341"&gt;One research study&lt;/a&gt; suggests that fiction books build more empathy in readers than nonfiction books. &lt;a href="https://www.scientificamerican.com/article/novel-finding-reading-literary-fiction-improves-empathy/"&gt;Another study&lt;/a&gt;  detailed in &lt;em&gt;Scientific American&lt;/em&gt; corroborated these results in comparing popular fiction, literary fiction, and nonfiction books.&lt;/p&gt;

&lt;p&gt;I think what explains this result is that reading fictionâ€Š – â€Šespecially literary fictionâ€Š – â€Šencourages the reader to develop empathy. While many of us are guilty of pigeonholing people on the Internet into stereotypes (picture any toxic comments thread), literary fiction discourages such stereotyping. Because the characters in literary fiction tend to have complex personalities that defy labels, readers have to work harder to understand them. Through this work, readers gain a sorely needed ability: imagining reality from a different perspective.&lt;/p&gt;

&lt;p&gt;So why should you, an engineer, get comfortable with new perspectives? You &lt;em&gt;hopefully&lt;/em&gt; want to design a product that suits your users well. How can you learn what works well for your users? It takes empathy to understand your user’s needs, potential pain points, and goals. You must put yourself in the target user’s shoes – â€Š&lt;a href="https://www.youtube.com/watch?v=jajduxPD6H4"&gt;sometimes literally&lt;/a&gt; â€Š–â€Š to build meaningful products.&lt;/p&gt;

&lt;p&gt;Reading fiction is a practice that fosters empathy, and a great place to start developing the empathy necessary for your engineering work. But, let’s say fiction isn’t your cup of tea. Not to worry â€Š–â€Š the next three takeaways focus on the benefits of a broader selection of books, not just fiction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading can make you a better problem-solver.
&lt;/h2&gt;

&lt;p&gt;To most software engineers, “problem-solver” sounds like an clichÃ© resume buzzword that suggests that someone is adept at addressing bugs and failed deployments. However, the problems that engineers must solve in their day-to-day work go beyond the technical.&lt;/p&gt;

&lt;p&gt;Engineering takes teamwork, so software engineers often need to deal with team-related problems. Engineers also write a good deal of code on their own, so they also need to be able to address blockers: technical and non-technical impediments on their progress. Fortunately, reading can build the unspoken but essential problem-solving skills needed to overcome personal and interpersonal issues.&lt;/p&gt;

&lt;p&gt;As silly as it sounds, reading books like the &lt;a href="http://www.goodreads.com/book/show/3.Harry_Potter_and_the_Sorcerer_s_Stone"&gt;Harry Potter series&lt;/a&gt; and comics like &lt;a href="http://www.goodreads.com/book/show/16231346-avatar"&gt;Avatar: The Last Airbender&lt;/a&gt; when I was younger taught me the importance of healthy team dynamics. These children’s books guided me to behave with respect, honesty, and kindness in all of my relationships. If I see a teammate in trouble, I’m more than willing to take a stab at the problem myself, or connect them with someone else who can help. If a conflict arises in my team, I know that the books I have read shown me how to resolve it without damaging the integrity of my team.&lt;/p&gt;

&lt;p&gt;I also draw upon books to guide myself and others through the personal struggles we face in our professional lives. When my sister faltered in her study habits as a college freshman, I relayed advice that I had read in &lt;a href="http://www.goodreads.com/book/show/18770267-make-it-stick"&gt;&lt;em&gt;Make It Stick&lt;/em&gt;&lt;/a&gt; about how to maximize information retention. When younger members of &lt;a href="http://www.eecs.northwestern.edu/wic/"&gt;Women in Computingâ€Š&lt;/a&gt; – â€Ša club I co-organized in college – wanted to rekindle their passion for technology, I suggested they read pop science books like &lt;a href="http://www.goodreads.com/book/show/17994.The_Code_Book"&gt;&lt;em&gt;The Code Book&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Readers act as a net that accumulates knowledge from a variety of sources. Upon encountering a problem, they can use any pertinent knowledge that they’ve picked up to provide advice, direction, or even a solution. &lt;em&gt;This&lt;/em&gt; is how reading can impact a software engineer’s problem-solving skills.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading inspires constant curiosity.
&lt;/h2&gt;

&lt;p&gt;Readers are inherently curious people. After all, why even pick up a book if you weren’t curious about learning &lt;em&gt;something&lt;/em&gt;?&lt;/p&gt;

&lt;p&gt;Many software engineers treat a technical career as something routine: from stand-up to stand-up, they fall under the illusion that they must do the same thing day in and day out. They trudge along, waiting for a clear-cut promotion from engineer  to senior/staff engineer or engineering manager. Even in that new role, they lapse into the same old patterns.&lt;/p&gt;

&lt;p&gt;With this mindset, it’s easy to live humdrum lives and work humdrum jobs.&lt;/p&gt;

&lt;p&gt;I see curiosity as the antidote to monotony.&lt;/p&gt;

&lt;p&gt;Curiosity is a spark that makes you wonder about &lt;em&gt;everything&lt;/em&gt;. The more you wonder, the more you question, and the more you question, the more you can create! Software engineering is &lt;em&gt;meant&lt;/em&gt; to be a creative practice, one in which you can slip indefinitely into &lt;a href="https://www.ted.com/talks/mihaly_csikszentmihalyi_on_flow"&gt;flow state&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Of all the takeaways I’ve outlined, I think the benefits of curiosity are most noticeable in an engineering career. Though we tend to praise â€˜geniuses’ in the tech industry, I believe that we should just as strongly exalt engineers who are curious. After all, &lt;a href="https://www.ncbi.nlm.nih.gov/pubmed/23231531"&gt;research suggests&lt;/a&gt; that curiosity leads to higher investment in intellectual pursuits over time. Curiosity becomes its own kind of genius.&lt;/p&gt;

&lt;p&gt;Curiosity is impactful in the short-term, too. Curious engineers may express new ideas in meetings or try new ways of approaching bugs. &lt;a href="http://greatergood.berkeley.edu/article/item/six_habits_of_highly_empathic_people1"&gt;Curiosity underpins empathy&lt;/a&gt;. Unsurprisingly, &lt;a href="http://onlinelibrary.wiley.com/doi/10.1002/1532-1096(200021)11:1%3C5::AID-HRDQ2%3E3.0.CO;2-A/abstract"&gt;research shows&lt;/a&gt; that curiosity leads to greater performance at work because curiosity drives engineers to perform several notches above what is expected of them.Â &lt;/p&gt;

&lt;p&gt;Curiosity is the ultimate game-changer in software engineering careers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading reducesÂ stress.
&lt;/h2&gt;

&lt;p&gt;Stress management is nothing new. Many people already have techniques that they use to keep their stress levels low; from meditation to morning walks, I applaud those who have found what works for them.&lt;/p&gt;

&lt;p&gt;However, in the office, we don’t always have the space to meditate, or the time to get outside and move around. Reading acts as a convenient alternative: it’s never disruptive to pull out a book at one’s desk or in a lounge area. Better yet, &lt;a href="https://www.takingcharge.csh.umn.edu/reading-stress-relief"&gt;researchers found&lt;/a&gt; that reading reduces stress by as much as 68 percentâ€Š–â€Šsignificantly more than any other stress-relieving activity testedâ€Š–â€Šand that &lt;a href="http://www.telegraph.co.uk/news/health/news/5070874/Reading-can-help-reduce-stress.html"&gt;as little as six minutes of reading&lt;/a&gt; is enough to notice these benefits. Social anthropologist Ceridwen Dovey even penned a piece in &lt;em&gt;The New Yorker&lt;/em&gt; about the effectiveness of &lt;a href="http://www.newyorker.com/culture/cultural-comment/can-reading-make-you-happier"&gt;bibliotherapy&lt;/a&gt; on increasing happiness.&lt;/p&gt;

&lt;p&gt;It has been shown that &lt;a href="http://www.smf.co.uk/wp-content/uploads/2015/10/Social-Market-Foundation-Publication-Briefing-CAGE-4-Are-happy-workers-more-productive-281015.pdf"&gt;happiness really does boost productivity&lt;/a&gt;, and that &lt;a href="https://hbr.org/2016/04/are-you-too-stressed-to-be-productive-or-not-stressed-enough"&gt;too much stress can destroy it&lt;/a&gt;. Since reading has the power to lessen stress and raise happiness, I believe that it is the key to a more productive workplace with more content engineers.&lt;/p&gt;




&lt;p&gt;There you have it, future employers. Ask me how I can apply the books I’ve read to software engineering, not how to implement yet another balanced binary tree. ðŸ˜‰&lt;/p&gt;

&lt;p&gt;In all seriousness, the impact that reading booksâ€Š –â€Š yes, even non-technical, non-&lt;em&gt;Cracking the Coding Interview&lt;/em&gt; books â€Š–â€Š can have on a software engineer’s career is tremendous. Just as it is never too late to start coding, it is never too late to bury your nose in a good book and benefit from these takeaways.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Enjoy what you read? Spread the love by sharing this piece. Have thoughts, questions, or book recommendations? Reach out to me on &lt;a href="https://twitter.com/alainakafkes"&gt;Twitter&lt;/a&gt; or in the comments below. Want to see what I've been reading? Check out my &lt;a href="https://kafkesquartos.tumblr.com/"&gt;Tumblr&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;


</description>
      <category>softwaredevelopment</category>
      <category>books</category>
      <category>lifelessons</category>
    </item>
  </channel>
</rss>
