<?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>DEV Community: Caleb Schoepp</title>
    <description>The latest articles on DEV Community by Caleb Schoepp (@calebschoepp).</description>
    <link>https://dev.to/calebschoepp</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%2F379830%2F314d23fc-870e-49e1-88a8-fb3edca0f1f8.jpeg</url>
      <title>DEV Community: Caleb Schoepp</title>
      <link>https://dev.to/calebschoepp</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/calebschoepp"/>
    <language>en</language>
    <item>
      <title>Highest impact reading for intermediate level programmers?</title>
      <dc:creator>Caleb Schoepp</dc:creator>
      <pubDate>Mon, 17 Aug 2020 20:12:52 +0000</pubDate>
      <link>https://dev.to/calebschoepp/highest-impact-reading-for-intermediate-level-programmers-4pfh</link>
      <guid>https://dev.to/calebschoepp/highest-impact-reading-for-intermediate-level-programmers-4pfh</guid>
      <description>&lt;p&gt;In your opinion, what is the highest impact content an intermediate level programmer can read?&lt;/p&gt;

</description>
      <category>discuss</category>
    </item>
    <item>
      <title>How to Organize Your Code</title>
      <dc:creator>Caleb Schoepp</dc:creator>
      <pubDate>Sun, 31 May 2020 23:11:00 +0000</pubDate>
      <link>https://dev.to/calebschoepp/how-to-organize-your-code-7io</link>
      <guid>https://dev.to/calebschoepp/how-to-organize-your-code-7io</guid>
      <description>&lt;p&gt;When I first started programming, I struggled a lot with how I should organize all my code. I needed an orderly way to keep track of the tutorials, code snippets, and side-projects I was rapidly accumulating. It's definitely a tough problem.&lt;/p&gt;

&lt;p&gt;Thank you to Dan Fletcher for his &lt;a href="http://www.danfletcherblog.ca/2017/01/beginners-tip-organize-coding-projects/"&gt;blog post&lt;/a&gt; on this very topic. He helped me figure this out when I was starting out. The following is a summary and rehashing of his fantastic ideas.&lt;/p&gt;

&lt;h1&gt;
  
  
  Don't
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Make heavy use of nesting:&lt;/strong&gt; At first, it seems like nesting is the key to good organization. This is wrong for two main reasons. First, nesting makes long filepaths which makes navigation with the command-line inconvenient. Second, deeply nested directories impose too brittle of a hierarchy. Eventually you will have files that can live in multiple places within the hierarchy. Then the hierarchy becomes inconsistent and loses its value. Keep things simple and avoid nesting too deeply.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nesting
└── 2020
    ├── feb
    │   ├── big-projects
    │   │   └── project-1
    │   └── small-projects
    │       └── project-2
    └── mar
        ├── big-projects
        │   └── project-3
        └── small-projects
            └── project-4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Organize by language:&lt;/strong&gt; For simpler projects, this is a logical conclusion. But, as projects grow in complexity they will start to make use of many languages. It is not uncommon for my projects to involve 3-4 different languages. This makes it impossible to know which directory a project should live under.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;by-language
├── go
│   └── project-1
├── javascript
│   ├── project-2
│   └── project-3
└── python
    └── project-4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Do
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Keep everything in one place:&lt;/strong&gt; I have a single directory on my computer that all my programming work lives under. Get creative with the name! I call mine the &lt;code&gt;batcave&lt;/code&gt;. What would you call yours?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use a flat structure:&lt;/strong&gt; Use a single directory for every project. Each directory contains everything that the project needs. These project directories are the level at which I like to track things with Git i.e. every project directory would be its own GitHub repository.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep the odds and ends together:&lt;/strong&gt; I like to group the really small stuff together. I'm talking about the code snippets and the tiny test scripts. For example, when I started learning JavaScript I made a directory called &lt;code&gt;javascript-snippets&lt;/code&gt;. Anytime I wanted to quickly try something out I could make a file and toss it in that directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;batcave
├── javascript-snippets
├── project-1
├── project-2
├── project-3
└── project-4
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Sticking to these do's and don'ts will go a long way to keeping your code organized. Remember though, these are only guidelines — sometimes it will make sense to break them. Now go forth and code!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you enjoyed this article, check out my &lt;a href="https://calebschoepp.com/blog"&gt;blog&lt;/a&gt; for similar content.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>beginners</category>
      <category>codenewbie</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Storing Empty Interfaces in BigCache</title>
      <dc:creator>Caleb Schoepp</dc:creator>
      <pubDate>Mon, 25 May 2020 22:30:08 +0000</pubDate>
      <link>https://dev.to/calebschoepp/storing-empty-interfaces-in-bigcache-1b33</link>
      <guid>https://dev.to/calebschoepp/storing-empty-interfaces-in-bigcache-1b33</guid>
      <description>&lt;p&gt;&lt;strong&gt;This was also posted on my personal &lt;a href="//calebschoepp.com/blog"&gt;blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Recently at work, I was tasked with adding some caching to one of our Golang services. The particular service required incoming requests to provide an API key for authentication. So on every request, the service was making an extra round trip to the database to verify the API key even though it was usually the same key. Not great. Implementing the cache ended up being much harder than I thought it would be.&lt;/p&gt;

&lt;p&gt;After some research and exhaustive debate among the engineers, we decided that &lt;a href="https://github.com/allegro/bigcache"&gt;BigCache&lt;/a&gt; fit our needs best.&lt;/p&gt;

&lt;p&gt;Here’s the catch. The signature for the set method in BigCache is &lt;code&gt;Set(key string, entry []byte) error&lt;/code&gt;. It expects you to store a byte slice. But, we wanted to store a struct with multiple fields that represented the API key. We might have been able to just store the bytes of the actual key this time. But this would just be putting off solving the problem. We needed a signature more like those found in other Golang cache implementations, &lt;code&gt;Set(key, entry interface{})&lt;/code&gt;. This would let us store anything we wanted.&lt;/p&gt;

&lt;p&gt;The obvious solution to this problem is serialization. If we could serialize an arbitrary struct into a byte slice, then we could store anything. To actually use the struct we stored, we could just deserialize the byte slice we get from the cache. Serializing the struct is as easy as importing any number of available encoding libraries in Golang. But now comes the headache. When we deserialize the bytes, how will the language know the shape of the struct to put the data in? Turns out &lt;code&gt;encoding/gob&lt;/code&gt;, the Golang specific serialization library, has this capability.&lt;/p&gt;

&lt;p&gt;I highly recommend you go read the &lt;a href="https://blog.golang.org/gob"&gt;blog post&lt;/a&gt; by Rob Pike that talks all about Gob; it’s a great read. In short, Gob is a Go native way of serializing data and it also comes with the ability to serialize interface types. To enable this you need to register your type with the aptly named &lt;a href="https://golang.org/pkg/encoding/gob/#Register"&gt;register function&lt;/a&gt; before serializing it. I ended up getting stuck here, because any code sample for &lt;code&gt;register()&lt;/code&gt; I found would always register a singular concrete struct or interface; I needed it to register an arbitrary &lt;code&gt;interface{}&lt;/code&gt; type. With a little messing around in the Go playground I discovered it can do that too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// What most code samples did&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;bar&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;gob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;

&lt;span class="c"&gt;// What I needed and discovered is possible&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="c"&gt;// Could be anything&lt;/span&gt;

&lt;span class="n"&gt;gob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  Putting It All Together
&lt;/h1&gt;

&lt;p&gt;With the problem of storing an arbitrary struct as bytes solved, I'll show you how I put it all together. First we want an interface for the cache that the rest of the system can interact with. For a simple cache we don’t need much more than get and set methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Cache&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
    &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

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



&lt;p&gt;Now let’s define the BigCache implementation that will fulfill the above interface. First we need a struct that holds the cache and can have methods added to it. You could also build other things like metrics into this struct.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;bigCache&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bigcache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;BigCache&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Next are the implementations of the get and set methods. Both methods assert the key is a string. From there, get and set are contrapositives of each other. One serializes a value and stores it. The other retrieves a value and deserializes it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bigCache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Assert the key is of string type&lt;/span&gt;
    &lt;span class="n"&gt;keyString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&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;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a cache key must be a string"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Serialize the value into bytes&lt;/span&gt;
    &lt;span class="n"&gt;valueBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;err&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;valueBytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;bigCache&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Assert the key is of string type&lt;/span&gt;
    &lt;span class="n"&gt;keyString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"a cache key must be a string"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Get the value in the byte format it is stored in&lt;/span&gt;
    &lt;span class="n"&gt;valueBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;keyString&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c"&gt;// Deserialize the bytes of the value&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valueBytes&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Finally the &lt;code&gt;encoding/gob&lt;/code&gt; serialization logic. Beyond the use of &lt;code&gt;register()&lt;/code&gt; this is the fairly standard way to serialize things in Go.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;serialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;enc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;gob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;value&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;buf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;deserialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valueBytes&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bytes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valueBytes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;dec&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gob&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;value&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;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&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;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And with all of that we’ve managed to store &lt;code&gt;interface{}&lt;/code&gt; values in BigCache. Now my team's service is a bit more efficient. Pretty cool! If you are looking for a more comprehensive implementation, checkout my &lt;a href="https://gist.github.com/calebschoepp/0165d92de412e288aa7441e792d0aa3a"&gt;gist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you enjoyed this article, check out my &lt;a href="//calebschoepp.com/blog"&gt;blog&lt;/a&gt; for similar content.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>computerscience</category>
      <category>webdev</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The Importance of Maintaining Dev/Prod Parity</title>
      <dc:creator>Caleb Schoepp</dc:creator>
      <pubDate>Wed, 13 May 2020 05:49:42 +0000</pubDate>
      <link>https://dev.to/calebschoepp/the-importance-of-maintaining-dev-prod-parity-9ap</link>
      <guid>https://dev.to/calebschoepp/the-importance-of-maintaining-dev-prod-parity-9ap</guid>
      <description>&lt;p&gt;&lt;strong&gt;This was also posted on my &lt;a href="https://calebschoepp.com/blog/the_importance_of_maintaining_dev_prod_parity/"&gt;personal blog&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of my side projects recently died on the vine and it didn’t have to.&lt;/p&gt;

&lt;p&gt;The project was a web service to convert an email newsletter into a PDF for offline reading where all the links would still work. This was done by pre-rendering the content of the links into the PDF.&lt;/p&gt;

&lt;p&gt;Generating the PDF required a plethora of tools. I used &lt;a href="https://github.com/puppeteer/puppeteer"&gt;Puppeteer&lt;/a&gt; to render links as HTML documents and convert them to PDFs; a mix of the Python PDF libraries &lt;a href="https://pymupdf.readthedocs.io/en/latest/"&gt;PyMuPDF&lt;/a&gt; and &lt;a href="https://github.com/claird/PyPDF4"&gt;PyPDF4&lt;/a&gt; to merge and link the generated PDFs; and &lt;a href="https://www.ghostscript.com/"&gt;GhostScript&lt;/a&gt; to compress the final result.&lt;/p&gt;

&lt;h1&gt;
  
  
  A World Without
&lt;/h1&gt;

&lt;p&gt;At the start, I hacked together a mess of Python, Node.js, and Bash scripts to prove I could get it to work. When it came time to try and deploy it in a more robust fashion, I had three obvious choices for hosting — a VPS like Digital Ocean, a PaaS like Heroku, or something serverless like AWS Lambda.&lt;/p&gt;

&lt;p&gt;Right out of the gate I knew Heroku wasn’t going to be an option. The process I had developed required using Puppeteer, which is really just a JavaScript wrapper around a Chromium binary. PaaS offerings are generally too sandboxed to allow you to run arbitrary binaries alongside your code. I learned this the hard way at a previous internship where I spent an entire week trying to run &lt;a href="https://developers.google.com/web/tools/lighthouse"&gt;Google Lighthouse&lt;/a&gt; on Azure’s PaaS. Suffice to say I was not successful.&lt;/p&gt;

&lt;p&gt;The remaining options were to use a VPS or serverless. I knew a VPS would have been more simple to deploy to. A task queue and worker model would have let me use what I already had. It could even run on one machine. Dirt simple.&lt;/p&gt;

&lt;p&gt;Instead, the allure of the sub-second billing and “infinite scale” tempted me to use a serverless offering. Both these reasons shouldn’t have mattered to my zero-user side project. Regardless, I chose to use the &lt;a href="https://www.serverless.com/"&gt;Serverless Framework&lt;/a&gt; hosted on &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt;. I came to regret it.&lt;/p&gt;

&lt;p&gt;Things went okay at first. Spinning up a few Lambdas with the Serverless Framework is a breeze. But the deployment rapidly grew in complexity. First, my build step needed a bespoke docker plugin so that I could use one of my PDF libraries. Next, I had to find a way to package in binaries with my deployment so that my Lambdas could use them. Once I had the individual functions working I realized I would need to coordinate them somehow so that I could robustly handle failures. No trouble, just add a plugin to start using AWS Step Functions.&lt;/p&gt;

&lt;p&gt;I added all these tools/plugins/shims to be able to solve my problem, but they were starting to become a problem. My deployment times had grown to be as long as 10-15 minutes because the build step was so bloated. This alone isn’t terrible, but the straw that broke the camels back was that I couldn’t test my changes locally.&lt;/p&gt;

&lt;p&gt;Without the ability to test locally, development ground to a halt. Every syntax error, typo, and silly accident was locked behind 15 minutes of waiting. So much for rapid iteration. Over time, I lost interest and the project died.&lt;/p&gt;

&lt;h1&gt;
  
  
  Dev/Prod Parity
&lt;/h1&gt;

&lt;p&gt;If I had been able to test my project locally it wouldn’t have died. Testing changes locally would have shortened my feedback loop. 30 seconds is much more manageable than 15 minutes. In other words, what killed my project was a failure to maintain Dev/Prod parity.&lt;/p&gt;

&lt;p&gt;Dev/Prod parity is the idea that your local testing environment should be as close to identical to your production environment as possible. I like the description of it from the &lt;a href="https://12factor.net/dev-prod-parity"&gt;Twelve-Factor App&lt;/a&gt;. If you aren’t already familiar with the idea of a Twelve-Factor App, you should go check it out - it’s great.&lt;/p&gt;

&lt;p&gt;My failed side project is an extreme example of losing environment parity; in fact, by the end, there was no parity. The only working environment was prod. But even in less extreme cases, differences in environments can start to cause big problems. &lt;/p&gt;

&lt;h1&gt;
  
  
  Key Takeaways
&lt;/h1&gt;

&lt;p&gt;It’s clear to me now why Dev/Prod parity is so valuable. There are two ways it will change how I work on future projects.&lt;/p&gt;

&lt;p&gt;First, I’m going to be more cognizant of how new features break Dev/Prod parity. When differences arise, I shouldn’t rush ahead with the feature anyway. My story is a clear example of how pushing the fix off can snowball and cause real problems. Instead, I should find a way to implement the feature without breaking parity. If this isn’t possible, it’s time to reassess how important the feature is. I want to work in a world where saying, “It runs on my machine”, also means that it runs on the production machine.&lt;/p&gt;

&lt;p&gt;Second, I need to start thinking about Dev/Prod parity from the very start of the project. Even before I write the first line of code. In hindsight, choosing to use AWS Lambda was a mistake for my particular situation. The tooling for doing advanced workflows with AWS Lambda is still very nascent and set me up for failure. Sure it’s easy to locally emulate a simple CRUD app with only a few functions. But local emulation is untenable once you need binaries, Step Functions, and Docker build steps.&lt;/p&gt;

&lt;p&gt;My side project died on the vine, but yours doesn’t have to. With the discipline to maintain Dev/Prod parity, you can avoid the issues I had.&lt;/p&gt;

&lt;p&gt;Thanks for reading! If you liked what I had to say you can find more of my writing on my &lt;a href="https://calebschoepp.com/blog/"&gt;personal blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>devops</category>
      <category>codequality</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
