<?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: Ovid</title>
    <description>The latest articles on DEV Community by Ovid (@ovid).</description>
    <link>https://dev.to/ovid</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%2F576474%2F6f8e4d4b-ddf6-46a5-a521-9e91c376d764.jpg</url>
      <title>DEV Community: Ovid</title>
      <link>https://dev.to/ovid</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ovid"/>
    <language>en</language>
    <item>
      <title>Rewriting the Monolith, Part 2</title>
      <dc:creator>Ovid</dc:creator>
      <pubDate>Wed, 19 May 2021 07:03:36 +0000</pubDate>
      <link>https://dev.to/ovid/rewriting-the-monolith-part-2-2bgf</link>
      <guid>https://dev.to/ovid/rewriting-the-monolith-part-2-2bgf</guid>
      <description>&lt;h1&gt;
  
  
  How To Rewrite The Monolith
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://dev.to/ovid/rewriting-the-monolith-part-1-478h"&gt;In part 1 of this article&lt;/a&gt;, we gave an overview of some (very) basic architectural concepts that you will need to know for the actual rewrite. Now we'll explain how to make that rewrite successful. Again, this isn't the only way to make this work, but it's a solid approach that works better than just sitting down and saying "we'll just rewrite everything from scratch."&lt;/p&gt;

&lt;h2&gt;
  
  
  OpenAPI
&lt;/h2&gt;

&lt;p&gt;We need to understand OpenAPI. &lt;a href="https://swagger.io/specification/" rel="noopener noreferrer"&gt;OpenAPI, formerly known as Swagger&lt;/a&gt;, is a specification for defining your REST API. With that, you can generate clients and/or servers which conform to that API. To save time, you can use &lt;a href="https://openapi-generator.tech/" rel="noopener noreferrer"&gt;the OpenAPI generator&lt;/a&gt; to generate a working client or a server stub (you have to supply the actual logic for the server, of course) for a wide variety of languages.&lt;/p&gt;

&lt;p&gt;Furthermore, if you design your OpenAPI document well, &lt;a href="https://nordicapis.com/7-open-source-openapi-documentation-generators/" rel="noopener noreferrer"&gt;you can get free documentation for it&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;OpenAPI is truly a powerful tool. What makes it a good choice for rewrites is that it helps you create a black box with an extremely well-defined interface. We'll need that.&lt;/p&gt;

&lt;p&gt;Here's what part of an OpenAPI document might look like. This is YAML, but you can use JSON if you prefer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;paths:
  /product/category/{category}:
    get:
      summary: Returns a list of products for a given category.
      responses:
        '200':
          description: A JSON array of product objects
          content:
            application/json:
              schema: 
                type: array
                items: 
                  type: object
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above snippet, we have routes like &lt;code&gt;GET /product/category/some_category&lt;/code&gt;. The your server is expected to pass &lt;code&gt;some_category&lt;/code&gt; to the endpoint which handles that route and for a &lt;code&gt;200 OK&lt;/code&gt; response, you must return an array of objects.&lt;/p&gt;

&lt;p&gt;Of course, you can return other responses and define their response structure, too, allowing the client to know how to automatically handle 404s and 500s, rather than simply guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using OpenAPI to rewrite
&lt;/h2&gt;

&lt;p&gt;If you look at the diagram about layering, you're going to think of your horizontal layers (search, shopping cart, checkout, etc.) as potential services you can expose to OpenAPI. If you've never done anything like this before, you'll need to tread slowly.&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%2F21gar94k04h7vjbx3s5w.png" 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%2F21gar94k04h7vjbx3s5w.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You're going to take one of those horizontal layers and convert it to OpenAPI. Which one is up to you, but to start with, I'd recommend the smallest, simplest layer you can find. This will make the OpenAPI specification easier to write and will give you more experience in how to make this work.&lt;/p&gt;

&lt;p&gt;First, you identify all of the core functionality that this layer needs to expose. For search, maybe it's just search by category and search by name. So you'll need at least two endpoints that are exposed.&lt;/p&gt;

&lt;p&gt;Build out your OpenAPI spec, build out your server stubs, and then fill them in, writing tests against the API along the way.&lt;/p&gt;

&lt;p&gt;Next, you start slowly disentangling the search layer from the rest of the code, often like slowly picking apart a knot. Depending on how well your system is designed, this could be quick and easy, or slow and painful. Over time, this gets easier with other layers due to previous layers being removed, and having more experience doing this.&lt;/p&gt;

&lt;p&gt;As part of this disentangling, you have identified everywhere in your code that requires those searches. One-by-one, start having them call your OpenAPI routes and &lt;em&gt;not&lt;/em&gt; call the search code directly.&lt;/p&gt;

&lt;p&gt;When done, you've started on the path to creating a SOA (Service-Oriented Architecture). Before we go on, let's take a look at where we are.&lt;/p&gt;

&lt;p&gt;First, more code can conceivably use the service because it's much less coupled with the existing code. Second, you might find you have an actual product! If your service is interesting enough, you could potentially offer that to the outside world. Imagine selling something that hobbyists enjoy and now their web sites, with an API key and a little bit of Javascript, could embed your search on their site.&lt;/p&gt;

&lt;p&gt;Heck, you could even write the Javascript for them and publish it on your site to make it easier for others to include your content on their sites.&lt;/p&gt;

&lt;h2&gt;
  
  
  Peeling off the layer
&lt;/h2&gt;

&lt;p&gt;Now that we've gotten a good start on creating a service, we need to make it more robust. Specifically, you want to separate out this code so that it can be deployed separately and &lt;em&gt;removed&lt;/em&gt; from the original repository. The details of that are very much up to you and your team. However, when it can be deployed separately, you could even deploy it on other servers in order to lighten the load on the main app servers. In fact, that's one of the nice benefits of SOA: decoupling components so you can manage them separately.&lt;/p&gt;

&lt;p&gt;As part of this "peeling off", you also want to write even more tests against the OpenAPI endpoints, with particular emphasis on capturing corner cases that might not be obvious. What's important is that you try to avoid writing tests that depend on the particular programming language you use. Instead, treat the service as a black box and adopt a data-driven approach. You have a series of tests in, say, a YAML document, and each is a request/response cycle. The beginning of the document might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
tests:
  - name: Fetch widgets category
    request:
      body: |
          GET /products/category/widgets HTTP/1.1
          Accept-Encoding: gzip
          User-Agent: Mojolicious (Perl)
          Content-Length: 0
          X-API-Key: 7777777
          Host: localhost:7777
          Content-Type: application/json
    response:
      body: |
        HTTP/1.1 200 OK
        Accept-Ranges: none
        Vary: accept-encoding
        Date: Thu, 25 Mar 2021 15:56:48 GMT
        Content-Length: 88
        Content-Type: application/json; charset=utf-8
        Connection: keep-alive

        [{...}]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why a data-driven approach? First, it discourages "cheating" by using a white-box approach. The second deals with the rewrite phase.&lt;/p&gt;

&lt;h1&gt;
  
  
  The actual rewrite
&lt;/h1&gt;

&lt;p&gt;You've peeled off a horizontal layer. It can be deployed separately. You run your data-driven, black-box tests against it. You use Redoc or something similar to have full documentation automatically written for it. Now you hand it off to the other team for a rewrite into the target language.&lt;/p&gt;

&lt;p&gt;First, they use the OpenAPI generator to generate full server stubs for their language of choice. Now, they take the documentation and your tests and just fill in each server stub. They keep going until all tests pass. At that point, they can deploy and your sysadmins can switch switch the URL/port in your API gateway and away you go! If it fails, they switch it back and you figure out what when wrong.&lt;/p&gt;

&lt;p&gt;Your test were data-driven to enforce the black box approach, but also to allow them to easily port the tests to their target language, if they so desire, and extend them.&lt;/p&gt;

&lt;p&gt;While this all seems very hand-wavy at the end, the reality is that most of heavy lifting has been done at this point. It's taken a lot to get here, but it's a heck of a lot less than rewriting the entire application from scratch.&lt;/p&gt;

&lt;p&gt;Once you've successfully pulled the first layer out into its own OpenAPI service, you keep doing this with successive layers, handing them off to the rewrite teams.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why This Works
&lt;/h1&gt;

&lt;p&gt;The "never rewrite" dogma got locked in hard after Joel Spolsky's article about it. And frankly, he makes very good points, but he was talking about monolithic applications. By deconstructing them into SOA, you can do small chunks at a time, reducing the risk considerably. And with the OpenAPI approach outlined above, you have great documentation and tests to give your rewrite developers a fighting chance, something that is often lacking in traditional rewrite scenarios.&lt;/p&gt;

&lt;h1&gt;
  
  
  Obstacles
&lt;/h1&gt;

&lt;p&gt;It wouldn't be fair to say it's all rainbows and unicorns. It's often &lt;em&gt;not&lt;/em&gt; easy to extract a horizontal layer from a monolithic application.&lt;/p&gt;

&lt;p&gt;You also have to worry about the quality of the tests being written. It's amazingly easy to skimp on tests and only write a few to demonstrate the basics. It's even easier when you are writing tests for code that's already working. The new team won't have working code, so the tests might not be up to snuff.&lt;/p&gt;

&lt;p&gt;And if Perl is either the source or the destination language, you'll be disappointed to learn that the OpenAPI Generator doesn't have code to handle server stubs for Perl (it can generate a client, though). There's &lt;a href="https://metacpan.org/pod/Mojolicious::Plugin::OpenAPI" rel="noopener noreferrer"&gt;Mojolicious::Plugin::OpenAPI&lt;/a&gt; which can help, but not only does it require weaving in non-standard bits like &lt;code&gt;x-mojo-to&lt;/code&gt; and &lt;code&gt;x-mojo-name&lt;/code&gt; into your OpenAPI doc, but you have to wire all of the bits together yourself. We've done this and frankly, it's not fun. Further, some of our clients have &lt;em&gt;very&lt;/em&gt; old codebases that run on older versions of Perl than Mojolicious supports, thus rendering it useless for them.&lt;/p&gt;

&lt;p&gt;To address that, &lt;a href="https://allaroundtheworld.fr" rel="noopener noreferrer"&gt;our company&lt;/a&gt; has started to develop &lt;a href="https://github.com/Ovid/Net-OpenAPI" rel="noopener noreferrer"&gt;Net::OpenAPI&lt;/a&gt;. It's framework agnostic and while it does rely on &lt;a href="https://metacpan.org/pod/JSON::Validator" rel="noopener noreferrer"&gt;JSON::Validator&lt;/a&gt;, which in turns relies on Mojolicious, we almost have a full &lt;code&gt;JSON::Validator58&lt;/code&gt; working, which should support older versions of Perl.&lt;/p&gt;

&lt;p&gt;We still have a lot more work to do on this, but so far it will build out full server stubs, with a PSGI file, and working documentation using Redoc. You just need to fill in the endpoints:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;endpoint 'get /pet/{petId}' =&amp;gt; sub {
    my ( $request, $params ) = @_;

    # replace this code with your code
    return Net::OpenAPI::App::Response-&amp;gt;new(
        status_code =&amp;gt; HTTPNotImplemented,
        body        =&amp;gt; {
            error =&amp;gt; 'Not Implemented',
            code  =&amp;gt; HTTPNotImplemented,
            info  =&amp;gt; 'get /pet/{petId}',
        },
    );
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's open source, and free software, so if you'd like to contribute, pull requests welcome!&lt;/p&gt;

&lt;h1&gt;
  
  
  Digressions
&lt;/h1&gt;

&lt;p&gt;As a final note, there are a couple of points we need to touch on.&lt;/p&gt;

&lt;h2&gt;
  
  
  SOA vs Microservices
&lt;/h2&gt;

&lt;p&gt;What's the difference? Are we developing microservices here, or heading towards an SOA?&lt;/p&gt;

&lt;p&gt;The easy answers is that microservices are small and self-contained applications, while the services in SOA are larger, more "enterprise-ready" tools. But that doesn't really help.&lt;/p&gt;

&lt;p&gt;Microservices are generally expected to have all the logic and data they need to perform a task. They're not coordinating with a bunch of other services and thus are loosely coupled and fit well with agile development.&lt;/p&gt;

&lt;p&gt;SOA services, however, need to coordinate with other services in the same way that your &lt;code&gt;Order&lt;/code&gt; object needs a &lt;code&gt;Collection&lt;/code&gt; of &lt;code&gt;Item&lt;/code&gt; objects, along with a &lt;code&gt;Customer&lt;/code&gt; object, and so on. In the layers diagram above, all three of the layers will need to know about items, but they'll frequently be treated as data instead of instantiated objects which are passed around.&lt;/p&gt;

&lt;h2&gt;
  
  
  Duplicate Code
&lt;/h2&gt;

&lt;p&gt;But won't this result in a lot of duplicated code?&lt;/p&gt;

&lt;p&gt;For microservices, if they're completely stand-alone but you have several which need to deal with the same business logic, then yes, you could easily have duplicated code and data. &lt;a href="https://www.ibm.com/cloud/blog/soa-vs-microservices" rel="noopener noreferrer"&gt;This IBM article sums up the dilemma nicely&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In SOA, reuse of integrations is the primary goal, and at an enterprise level, striving for some level of reuse is essential.&lt;/p&gt;

&lt;p&gt;In microservices architecture, creating a microservices component that is reused at runtime throughout an application results in dependencies that reduce agility and resilience. Microservices components generally prefer to reuse code by copy and accept data duplication to help improve decoupling.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So when striving for a SOA, because different services coordinate, you can strive for the "single responsibility principle" and avoid duplication. However, if you're rewriting, you probably have a legacy mess. That means technical debt. That means hard decisions. You may have to copy code to get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full or partial rewrite?
&lt;/h2&gt;

&lt;p&gt;One of the benefits of this approach is that you can choose to go for a partial rewrite. Just as you might throw away your custom search engine code in favor of ElasticSearch or Typesense, so might you find that a CPU-intensive part of your application can be safely rewritten in Go or Rust. You don't have to make this an all or nothing scenario. You can also take the time to do it right, pausing as needed, rather than the death march scenario of the full rewrite.&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;strong&gt;TL;DR&lt;/strong&gt;
&lt;/h1&gt;

&lt;p&gt;The "never rewrite" mantra needs to stop. Instead, it should be "almost never rewrite", but if you must, do so incrementally.  We've laid out a clean approach, but be warned: it's clean on paper. If you're backed into the rewrite corner, it's not easy getting out. But you can get out so long as you choose an incremental approach that's more likely to succeed—and bring plenty of patience and courage with you.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://www.pexels.com/photo/empty-hallway-1209962/" rel="noopener noreferrer"&gt;Cover Photo by Francesco Paggiaro from Pexels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openapi</category>
      <category>refactoring</category>
      <category>soa</category>
      <category>design</category>
    </item>
    <item>
      <title>Rewriting the Monolith, Part 1</title>
      <dc:creator>Ovid</dc:creator>
      <pubDate>Wed, 19 May 2021 07:03:15 +0000</pubDate>
      <link>https://dev.to/ovid/rewriting-the-monolith-part-1-478h</link>
      <guid>https://dev.to/ovid/rewriting-the-monolith-part-1-478h</guid>
      <description>&lt;p&gt;Oh no! You have to rewrite that huge legacy app! You already argued that &lt;a href="https://dev.to/ovid/fixing-legacy-code-2kp9"&gt;you can fix the legacy code&lt;/a&gt; and you've sent the managers the &lt;a href="https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/" rel="noopener noreferrer"&gt;Joel Spolsky "never rewrite" article&lt;/a&gt;. Management responds by pointing out that both &lt;a href="https://medium.com/@herbcaudill/lessons-from-6-software-rewrite-stories-635e4c8f7c22" rel="noopener noreferrer"&gt;Basecamp and Visual Studio both had famously successful rewrites&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Do you update your CV or do you tuck in and get the job done?&lt;/p&gt;




&lt;p&gt;Sometimes the rewrite is where you're at. Either you've made the decision or you were given the order. Maybe it's because your product is written in VB 6, or UniBasic, or Easytrieve (all of which I've programmed in) and you can't find developers. Or maybe the software is a slow, CPU-bound memory hog and Python or Perl just aren't great choices there.&lt;/p&gt;

&lt;p&gt;Whatever your reasons, you need a clean strategy. A problem this hard can't be covered in a couple of paragraphs, but by the time we get to the end, I'll give you a fighting chance of not only making that rewrite succeed, but also avoiding &lt;a href="https://dev.to/ovid/you-need-measurable-goals-47g9"&gt;exchanging one plate of spaghetti code for another&lt;/a&gt;. This isn't the only way to approach this problem, but I've seen this work and as an added bonus, you'll be developing a SOA (service-oriented architecture). If you're unsure why you should do that, &lt;a href="https://gist.github.com/chitchcock/1281611" rel="noopener noreferrer"&gt;read Steve Yegge's epic rant on the topic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We start by giving a quick refresher on structured programming, along with vertical and horizontal layering of applications. If you're comfortable with that, &lt;a href="https://dev.to/ovid/rewriting-the-monolith-part-2-2bgf"&gt;you can jump straight to Part 2&lt;/a&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Structured Programming
&lt;/h1&gt;

&lt;p&gt;I learned to program in the early 80s, first with BASIC before moving on to 6809 assembler. Being self-taught, "structured programming" wasn't a concept I was familiar with and neither BASIC nor assembly prepared me for that. But moving on to C and later learning &lt;a href="https://link.springer.com/content/pdf/10.3758/BF03207927.pdf" rel="noopener noreferrer"&gt;Warnier/Orr diagramming&lt;/a&gt; (pdf) taught me the basics of structured programming. We all learn this after a while. &lt;em&gt;This&lt;/em&gt; module handles payments while &lt;em&gt;that&lt;/em&gt; module handles orders and this is how you use subroutines, and so on.&lt;/p&gt;

&lt;p&gt;Unfortunately, for some programmers, that's where their design experience stops. They're not concerned with separation of concerns. They see no problem that huge subroutine that concatenates a bunch of strings, based on conditionals, to form SQL, and then returns an HTML table that will be concatenated with other HTML snippets to try to make a web page.&lt;/p&gt;

&lt;h1&gt;
  
  
  Vertical Layering
&lt;/h1&gt;

&lt;p&gt;Eventually we start learning about the vertical &lt;em&gt;layers&lt;/em&gt; that our application might have. For example, in a classic MVC pattern, you might have a view layer, which accepts JSON from a controller and renders it as HTML. All kept very separate from the main logic. The controller merely dispatches requests from the view to the business model layer and that layer gets its data from a lower-level data layer (often a database).&lt;/p&gt;

&lt;p&gt;The layers have distinct boundaries around their responsibilities. Ignoring this approach makes it much harder to manage different parts of an application. For example, I frequently see people using ORMs and then embedding business logic in them (I've made this mistake more than once). Thus, when they try to change that logic, or change the data layer, you're often working on code with two sets of responsibilities and it gets hard to untangle them if needed.&lt;/p&gt;

&lt;p&gt;A key point to layering is to remember that, for vertical layering, each layer can only talk to adjacent layers. The view can talk to a controller, but never the data layer. Keeping vertical layers separate helps to minimize spaghetti code.&lt;/p&gt;

&lt;p&gt;For an example of how bad layering can make your life miserable, you can read my &lt;a href="https://ovid.github.io/articles/project-500.html" rel="noopener noreferrer"&gt;Project 500 case study&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Horizontal Layering
&lt;/h1&gt;

&lt;p&gt;Horizontal layering is less well-known, but it can be a great tool.&lt;/p&gt;

&lt;p&gt;Imagine you have a &lt;code&gt;ShoppingCart&lt;/code&gt; class for a simple e-commerce web site. Is that useful? Not by itself. You probably need plenty of other classes to make it useful. You might need a &lt;code&gt;Customer&lt;/code&gt; object, and &lt;code&gt;Product&lt;/code&gt; objects, and &lt;code&gt;Currency&lt;/code&gt; objects, and all sorts of other things (assuming you're going OO).&lt;/p&gt;

&lt;p&gt;So let's step back and ask ourselves what kinds of things that site might need. This example is obviously not a huge system, but we're keeping things simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product search&lt;/li&gt;
&lt;li&gt;Shopping cart&lt;/li&gt;
&lt;li&gt;Payment&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For many monolithic sites, those are often all lumped together in one big code base, with their edges overlapping. But if you start to think of those as &lt;em&gt;services&lt;/em&gt;, you could look at those as horizontal layers.&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%2F21gar94k04h7vjbx3s5w.png" 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%2F21gar94k04h7vjbx3s5w.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might think that you only want horizontal layers to talk to adjacent layers, but this time it's different. You have a variety of different services and they often need to share domain knowledge across different services. For example, if you also have a blog "service", when someone is searching for a product, you might show related blog entries. When reading the blog, you might be able to add an item directly to the shopping cart for that.&lt;/p&gt;

&lt;p&gt;What's important is that your layers are separated cleanly, with each preferably being a black box. If you have a monolithic, legacy codebase, there's a good chance layers &lt;em&gt;aren't&lt;/em&gt; separated cleanly (or at at all). But you need to at least understand the concepts.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://dev.to/ovid/rewriting-the-monolith-part-2-2bgf"&gt;In part 2, we'll cover the actual process of rewriting&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://www.pexels.com/photo/empty-hallway-1209962/" rel="noopener noreferrer"&gt;Cover Photo by Francesco Paggiaro from Pexels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>openapi</category>
      <category>refactoring</category>
      <category>soa</category>
      <category>design</category>
    </item>
    <item>
      <title>Fixing Legacy Code</title>
      <dc:creator>Ovid</dc:creator>
      <pubDate>Sat, 08 May 2021 10:04:07 +0000</pubDate>
      <link>https://dev.to/ovid/fixing-legacy-code-2kp9</link>
      <guid>https://dev.to/ovid/fixing-legacy-code-2kp9</guid>
      <description>&lt;p&gt;If you've been in this business long enough, sooner or later you're going to be faced with a terrible problem: fixing the legacy codebase. What follows isn't the only way to proceed, but it's a tried-and-true strategy that unfortunately doesn't appear to be well-known. The core of what follows is risk minimization. Assuming you're facing the problem of fixing a legacy application, you already have risk and you don't need more added to it. What follows is lower risk and lower cost than rewriting the system from scratch.&lt;/p&gt;

&lt;p&gt;If you like what you read here and you need help, especially with Perl, get in touch with me and see how our company can help out.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why You (Probably) Don't Rewrite The Code
&lt;/h2&gt;

&lt;p&gt;Before we start, there are a few things you should know. First, &lt;a href="https://www.joelonsoftware.com/2000/04/06/things-you-should-never-do-part-i/"&gt;read this now-famous Joel Spolsky article about why you should never rewrite your code&lt;/a&gt; (trust me, read it, but don't forget to come back). In that article, Spolsky makes a strong case about why you should refactor your codebase instead of rewriting it. Refactoring, if you're not familiar with the term, is the process of making a series of gradual improvements to code quality without changing the behavior. When you're trying to fix code, trying to change its structure and behavior at the same time is begging for trouble.&lt;/p&gt;

&lt;p&gt;That being said, I don't believe in the word "never". If your code is written in UniBasic, rewriting might be your only option since you can't find developers who know the language (or are willing to learn it). Heck, I used to program in UniBasic and I've forgotten the language entirely.&lt;/p&gt;

&lt;p&gt;Or if you're working with a relatively small piece of software with low impact, rewriting may not be that dangerous.&lt;/p&gt;

&lt;p&gt;But let's say you can find or train developers for the language your software is written in, the software is mission-critical, and it's a very large codebase. Rewriting begins to make much less sense. Refactoring it means that you always have working code, you're not throwing away business knowledge or obscure bug-fixes, and your developers aren't starting from scratch, hoping they can make something work. In other words, you're minimizing your risk.&lt;/p&gt;

&lt;p&gt;That being said, many companies (and developers) still opt for the rewrite. New code is exciting. New code promises new opportunities. New code is fun but fixing old code is often seen as drudgery. However, if you have a large, legacy codebase, the new code you're writing is, by definition, a large project and &lt;a href="https://www.informationweek.com/strategic-cio/executive-insights-and-innovation/why-do-big-it-projects-fail-so-often/d/d-id/1112087"&gt;large projects are very high risk&lt;/a&gt; (emphasis mine):&lt;/p&gt;

&lt;blockquote&gt;In a landmark 1995 study, the Standish Group established that only about 17% of IT projects could be considered "fully successful," another 52% were "challenged" (they didn't meet budget, quality or time goals) and 30% were "impaired or failed." In a recent update of that study conducted for ComputerWorld, Standish examined 3,555 IT projects between 2003 and 2012 that had labor costs of at least $10 million and found that &lt;strong&gt;only 6.4% of [IT projects] were successful&lt;/strong&gt;.&lt;/blockquote&gt;

&lt;p&gt;That's an old study, but there's still plenty of newer work which bears this out. The larger the project, the larger the risk. In fact, of the large projects I have been involved with for various companies, few were both on time and on budget. Some were cancelled outright and still others dragged on, long after it was clear they were a disaster, simply because no one wanted to take the blame for failure. One of them  was approaching its fourth year of a one-year schedule and was riddled with bugs and design flaws, but the company made the new software backwards-incompatible, switched over their clients and now have no way out. The only reason the company is still in business is that they bought another company that is very profitable and is paying for the company's mistake.&lt;/p&gt;

&lt;p&gt;That last examples alludes to a dirty little secret that's often not talked about in our industry: large-scale rewrites often exchange one pile of spaghetti code for another. Rather than truly solve the underlying problems, the companies have traded a known set of problems for an unknown set of problems. If you need to fix your legacy code it's because you need to minimize your risk; why on earth would you knowingly adopt unquantifiable risk?&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Refactor Your Legacy Code
&lt;/h2&gt;

&lt;p&gt;Assuming you've decided that you don't want to face the cost and risk of a large-scale rewrite, how do you refactor your code?&lt;/p&gt;

&lt;p&gt;First, you need to assess where you are. At a bare minimum:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;What are the functional requirements of the code? (very high-level here)&lt;/li&gt;
    &lt;li&gt;What documentation exists, if any?&lt;/li&gt;
    &lt;li&gt;What areas of the code are more fragile? (Check your bug tracker)&lt;/li&gt;
    &lt;li&gt;What external resources does it require?&lt;/li&gt;
    &lt;li&gt;What tests exist, if any?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these things need to be written down so that anyone can consult this information at a glance. This information represents the bare necessities for the expert you're going to hire to fix the mess.&lt;/p&gt;

&lt;p&gt;If the above list seems simplistic, that's because we're refactoring, not rewriting.&lt;/p&gt;

&lt;p&gt;And yes, you're probably going to hire an outside expert. Not only will they see things that you didn't, but while your current developers may be good, if they can't clearly lay down a solid plan to fix the legacy codebase while simultaneously minimizing risk, you need to bring in someone with experience with this area. What follows is not always intuitive and the expert's experience will help you navigate the rough waters you're already in. At a minimum, your expert needs to have the following:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;Expert in the primary language(s) of your codebase&lt;/li&gt;
    &lt;li&gt;A strong automated testing background&lt;/li&gt;
    &lt;li&gt;Very comfortable with &lt;a href="https://en.wikipedia.org/wiki/Code_coverage"&gt;code coverage&lt;/a&gt; tools&lt;/li&gt;
    &lt;li&gt;A strong database background (probably)&lt;/li&gt;
    &lt;li&gt;An expert in system design/architecture&lt;/li&gt;
    &lt;li&gt;Ability to admit when they're wrong&lt;/li&gt;
    &lt;li&gt;Understanding of business needs&lt;/li&gt;
    &lt;li&gt;A persuasive personality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last points seems strange, but hard choices will need to be made and there will be strong disagreements about how to make them.&lt;/p&gt;

&lt;p&gt;It's hard to find this mix in top-notch developers, but it will definitely pay off.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;The first thing you'll want to do is get a rough idea of how you want your new application laid out. Call this your architecture roadmap, but keep in mind that your landscape will change over time and this roadmap should be flexible. This is where your expert's architecture skills will come in. Various functional parts of your application will be decoupled and put into separate areas to ensure that each part of your application has a "specialty" that it focuses on. When each part of your application has one area it focuses on,&lt;br&gt;
it's easier to maintain, extend, and reuse, and that's primarily why we want to fix our legacy codebase. However, don't make detailed plans at this time; no battle plan survives first contact with the enemy.&lt;/p&gt;

&lt;p&gt;Instead, just ensure that you have a rough sense of where you're going to go.&lt;/p&gt;

&lt;p&gt;Next, you're going to refactor your application the same way you eat an elephant: one bite (byte?) at a time. You'll pick a small initial target to get familiar with your new tools. Over time, it will get easier, but you don't want to bite off too big a chunk when you get started.&lt;/p&gt;

&lt;p&gt;Refactoring a large application means writing tests, but unless you know what you're doing, you're probably going to get it wrong. There's often little TDD here — the code is already written — and you can't write tests for everything — you'll never finish. Instead, you'll be tactically applying integration tests piece by piece.&lt;/p&gt;

&lt;p&gt;The first thing you need to do is understand what won't change in your application. By "won't change" I mean whatever it is that uses your application's output, whether it be through a JSON API, a Web site, a SOAP interface or what have you. Since something has to use the software, that something is what is going to make everything work.&lt;/p&gt;

&lt;p&gt;You're going to be writing integration tests against whatever that something is. For the sake of argument, we'll assume we're refactoring a Web application. You've decided that you'll start by writing tests to verify that you can list users on your admin page.&lt;/p&gt;

&lt;p&gt;Inside those tests, you'll create a browser object, log in as an admin user, fetch the users page and write tests to assert that the expected users show up on that page. Just getting to this point can often take a huge amount of work. For example, how do you get code to connect to a test database? How do you ensure data isolation between tests (in other words, the order in which tests are run should not matter)? Heck, how do you create that browser object (hint: Selenium is a good choice here)? These and many more questions need to be answered when you're first starting out.&lt;/p&gt;

&lt;p&gt;Getting to to this point may be easy if you already have some tests, or it may be very hard if you don't, but it's the important first step in the refactoring.&lt;/p&gt;

&lt;p&gt;Once you have that first integration test targeting a small and (relatively) unchanging part of your interface, run your code coverage tools over the test(s) to see what code is covered with these high-level integration tests. Code which is covered is code which is generally safe to refactor (there are plenty of exceptions, but that's another article entirely).&lt;/p&gt;

&lt;p&gt;Now you can start looking at which functional parts of the application are embedded in that tested code and make a plan for moving those sections into your architecture roadmap. At this point, it's tempting to rip everything apart, but don't give in to that temptation. Instead, focus on one piece at a time. For example, if you have SQL scattered throughout the code, start pulling that out into your architecture roadmap so that you have a clean API to work with the data you need. Or perhaps you have a Web application and you have been printing the HTML directly: look at using a templating system and start pulling the HTML out into templates. Don't fix everything at once or you'll be trying to do too much. Instead, focus on one area of responsibility and understand it well.&lt;/p&gt;
&lt;h2&gt;
  
  
  Don't Do Unit Testing (Yet)
&lt;/h2&gt;

&lt;p&gt;Note that we've been talking about integration testing but not unit testing. There's a very good reason for that: with heavy refactoring of a legacy system, your units will change quite heavily when you first start, but the integration tests focusing on the rather static interfaces will not. You want to spend time refactoring your application, not your tests, so until you've stabilized how the code works internally, unit tests can actually be a distraction.&lt;/p&gt;

&lt;p&gt;Integration testing has the advantage that you can cover (if not actually test) huge portions of your code at once and if done correctly, can be very fast to write. Further, with poorly structured applications, unit testing may be very difficult, if not impossible.&lt;/p&gt;

&lt;p&gt;Integration testing will also help uncover bugs that unit testing cannot: bugs where different components have different expectations when talking to one another. However, there are some downsides to integration testing:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;Integration tests run slower than unit tests&lt;/li&gt;
    &lt;li&gt;Bugs are harder to track down&lt;/li&gt;
    &lt;li&gt;It's easier to break real things if you've not isolated your code well enough&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That being said, the advantage of integration testing at this stage is clear: refactoring is much easier when you have some basic tests to protect against the worst errors. It's also worth keeping in mind that if you've done little to no testing before this, you're not significantly worse off if you have some solid tests than if you have none. Don't obsess too much on this point: you don't want perfect to be the enemy of the good.&lt;/p&gt;

&lt;p&gt;If you haven't already implemented a continuous integration (CI) system, this is the time to start. Even if your developers forget to run the tests, your CI system shouldn't. You want to find out fast if tests are failing.&lt;/p&gt;
&lt;h2&gt;
  
  
  Pushing Forward
&lt;/h2&gt;

&lt;p&gt;After you've started refactoring one functional piece of a small part of your system, you'll probably quickly uncover some bad assumptions made in the original plan. That's OK. You've started small to minimize your risk. Correct those bad assumptions and then start integration tests with code coverage for another small part of your system, pulling out the functional portions (database calls, HTML, or whatever) that you've already been working on. When you feel comfortable that you've shaken out some of the worst issues, start looking at another functional bit of the system that your currently tested code shares and see if you can pull that out.&lt;/p&gt;

&lt;p&gt;Note that this is where your expert's architectural skills are going to shine. They'll understand the importance of decoupling different functional portions of the application. They'll understand how to write robust, flexible interfaces. They'll learn to recognize patterns in your business logic which can be abstracted out. Do not hand this responsibility over to an existing programmer unless you are absolutely confident they have the skills and experience necessary to get this done.&lt;/p&gt;

&lt;p&gt;At this point, what follows is a wash/rinse/repeat cycle which in worst case scenarios can take years to finish. It takes a long time, but it has some significant advantages:&lt;/p&gt;

&lt;ul&gt;
    &lt;li&gt;The code is always working&lt;/li&gt;
    &lt;li&gt;You're not paying to maintain two systems at the same time&lt;/li&gt;
    &lt;li&gt;Business knowledge is not lost&lt;/li&gt;
    &lt;li&gt;New features can still be added&lt;/li&gt;
    &lt;li&gt;Tests can now be easily written to target existing bugs (even if you don't refactor that code yet)&lt;/li&gt;
    &lt;li&gt;You can always stop if you've made your codebase "good enough"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why does this approach work? Any large project can seem daunting, but by breaking it down into smaller, manageable pieces, you can at least know where to start and get a sense of where you are going without the nail-biting worry about whether or not a huge project is going to fail.&lt;/p&gt;

&lt;p&gt;When I've used this technique before, I've often found that it's a pleasure to finally have a cleaner sense of how the code is evolving and the current experienced team doesn't face the demoralizing prospect of watching their jobs disappear. The downside of this technique is that while code quality improves tremendously, there's always a feeling that it's not good enough. However, as I previously alluded to, many rewritten systems merely create new design flaws to replace old ones. This is far too common of a problem and it means swapping known problems for unknown ones.&lt;/p&gt;

&lt;p&gt;For more information, you can watch this presentation I've given on the topic:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/4c8ixLgpxwo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The above strategy isn't appealing to many people and it can be a hard sell to those who are convinced that newer is better. In fact, in many respects it can be viewed as boring (though I love refactoring code), but I've successfully used this approach on multiple legacy codebases. However, if you're still trying to decide between a rewrite and a refactor, keep in mind that this approach is a relatively low-cost, low-risk approach. If it proves unworkable, you've likely risked very little. If the rewrite proves unworkable, you could cost the company a ton of money.&lt;/p&gt;

&lt;p&gt;So the final question to ask yourself is when you should consider fixing your legacy codebase. The only advice I can offer is to suggest that you not wait until the storm before you fix your roof. Fixing a legacy code base isn't rocket science, but it does require a degree of expert knowledge in how to transform an existing codebase. Sadly, it's not a skill most developers seem interested in acquiring, but then, most don't seem interested in working on legacy codebases in the first place.&lt;/p&gt;




&lt;p&gt;In a follow-up post, I'll explain a safe approach if an rewrite cannot be avoided.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Don‘t Estimate Project Costs</title>
      <dc:creator>Ovid</dc:creator>
      <pubDate>Wed, 28 Apr 2021 08:05:20 +0000</pubDate>
      <link>https://dev.to/ovid/don-t-estimate-project-costs-346m</link>
      <guid>https://dev.to/ovid/don-t-estimate-project-costs-346m</guid>
      <description>&lt;p&gt;I know, I know. Management posts are boring, especially on a dev site. But many of you will eventually head down that road, or at least have to talk to a manager at some point. And when you do, the question of “how much will this cost to build?” will eventually come up. But that’s the wrong question.&lt;/p&gt;




&lt;p&gt;Possibly you’ve not heard of &lt;a href="https://en.wikipedia.org/wiki/Douglas_W._Hubbard"&gt;Douglas Hubbard&lt;/a&gt;. If you’re interested at all in estimating the value of IT projects, his works should be required reading. In fact, &lt;a href="https://www.soa.org/Education/Exam-Req/Syllabus-Study-Materials/exam-book-list.aspx"&gt;his books are required reading for the Society of Actuaries&lt;/a&gt; if you want to pass their exams and become an actuary.&lt;/p&gt;

&lt;p&gt;But we’ll come back to him in a moment.&lt;/p&gt;

&lt;p&gt;If there’s one key thing I’ve learned in consulting, it’s that potential clients who start discussions by talking about building value for their customers have usually been better clients than those who start conversations with “what’s your rate?” Why? Because the “what’s your rate” clients are focused on controlling costs, not building value. I’ve found that their approach problems is different and those who are paying more attention to minimizing risk than maximizing value simply have different ideas about how to approach a project.&lt;/p&gt;

&lt;p&gt;And you know what? I get it. If you want to go out to dinner and you only have £15 in your pocket, you’re probably not going to a fancy restaurant. You just might grab a pint at a pub and hit a kebab shop after. Budget constraints are real.&lt;/p&gt;

&lt;p&gt;But curiously, while development costs on a project are one of the first things we look at, they’re also one of the worst metrics you can use to judge the value of a project. It’s part of a problem that Hubbard refers to as the &lt;a href="https://www.cio.com/article/2438748/it-organization/the-it-measurement-inversion.html"&gt;IT Measurement Inversion&lt;/a&gt;. Specifically:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The variables having the greatest impact on project success are the variables we measure the least.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So what sort of variables are there? There are the ongoing development and maintenance costs when the project is delivered. There are marketing costs. There are costs to have your people learn the new system. And so on.  The costs are myriad and complex, but if we’re going to judge the &lt;em&gt;value&lt;/em&gt; of a new system, which variables matter the most?  Those are the ones we should be focusing on.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/w4fHGTsZZD8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Hubbard’s work will guide you here. In particular, you might want to consider his book &lt;a href="https://www.amazon.com/How-Measure-Anything-Intangibles-Business/dp/1118539273"&gt;How to Measure Anything&lt;/a&gt; (that’s not an affiliate link, by the way), where he teaches you how to break problems down to &lt;em&gt;things that can be measured&lt;/em&gt; and &lt;em&gt;how to measure them&lt;/em&gt;. You’ll learn quite a bit about statistical analysis and running &lt;a href="https://en.wikipedia.org/wiki/Monte_Carlo_method"&gt;Monte Carlo simulations&lt;/a&gt;. In fact, one of the things you’ll learn is that of the various techniques which have been developed to estimate project success, Monte Carlo simulations win hands down.&lt;/p&gt;

&lt;p&gt;In fact, Monte Carlo simulations are so powerful that Hubbard talks about research at NASA showing that, for over 100 projects, the accountants outperformed the subject matter experts (scientists and engineers) on project estimates because the accountants used Monte Carlo simulations rather than other methodologies.&lt;/p&gt;

&lt;p&gt;But for the purposes of our article, the key takeaway is that Hubbard has done a lot of this work for you and has nailed down what your real risks are. And it’s almost intuitive once you think of it. Let’s start with the most important.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The greatest risk to project success is whether or not it will be canceled.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That seems so brain-dead obvious that it’s almost insulting to say, but when was the last time you quantified that risk? When was the last time you looked at the potential value of a large project and said “there’s a 25% chance that this project will be canceled” and then factored that into your risk estimates? It’s the single most important variable, but most people ignore it! Can you imagine running a company and discovering your managers are repeatedly ignoring the most valuable information at their disposal?&lt;/p&gt;

&lt;p&gt;The next most important variable is system utilization. This is actually complex and includes how fast your product will roll out, whether people will use it, their rate of adoption, and so on. Again, this seems so obvious given 20/20 hindsight. Sure, your project took six months to develop instead of three, but customers will be using it for many years. When it’s put that way, it’s obvious that estimating system utilization is more valuable than estimating development costs. But people still ignore it.&lt;/p&gt;

&lt;p&gt;So let’s look at development costs.&lt;/p&gt;

&lt;p&gt;Of &lt;em&gt;course&lt;/em&gt; you have to factor in development costs; I’d be insane to suggest otherwise (despite the provocative title of this post). You have a budget and when you’re looking at backing your project with Oracle or PostgreSQL, most of the time you find that Oracle isn’t a terribly cost-effective solution. So yes, costs are important, but consider this: if the first version of Amazon’s code cost twice as much to develop, Jeff Bezos would probably still be insanely rich. Uber would still be dominating their space. Facebook would still be selling your pesonal data to dodgy companies. I can confidently say that because these companies are providing real &lt;em&gt;value&lt;/em&gt; to people (you might disagree about Facebook, but &lt;a href="https://www.statista.com/statistics/264810/number-of-monthly-active-facebook-users-worldwide/"&gt;I can offer you 2.32 billion rebuttals&lt;/a&gt;). The fact that these projects weren’t canceled and are being used far, far outweighs the initial project costs. Or to restate all of this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Initial development costs are incurred once. Your revenue stream is continuous. That’s why development costs are less important.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“But Ovid, I don’t know how to do Monte Carlo simulations or estimate projected system utilization!”&lt;/p&gt;

&lt;p&gt;Well ... that’s actually a fair point. These articles are intended, if nothing else, to be practical.&lt;/p&gt;

&lt;p&gt;Since the two most importance variables in project success are the odds of it being canceled and system utilization, we need to reduce the risk of each of those. How do we do that?&lt;/p&gt;

&lt;p&gt;Simply put, you go lean/agile. Figure out the smallest possible work you can do which would add value and build that. Instead of a year-long project, you have a crazy one month race to get a simple prototype in front of your customers. If it turns out your customers have zero interest in your “Recycled Food” project, it’s better to find out after a month instead of a year.&lt;/p&gt;

&lt;p&gt;But let’s say there’s some interest. What now? Well, you’ve greatly reduced the chance of project cancelation because it looks viable, so you need to focus on system utilization. Amongst the key factors, getting customers to actually use the damned thing is incredibly important.  You will be amazed at the ideas they come up with and the feedback they give you. As you push forward, you address customer concerns and have a lower risk of building features on top of things they say aren’t working. You’re constantly pushing new versions of the product in front of your customers and iterating based on their feedback. Every step of the way, you’re improving your project based on customer behavior, not your hunches or market research.&lt;/p&gt;

&lt;p&gt;This, incidentally, is why &lt;a href="http://theleanstartup.com/"&gt;Lean Startups&lt;/a&gt; are such a powerful movement in business today. They assume they’re not canceling their entire business, so they need to maximize system utilization. That means rolling the product out quickly and immediately responding to customer feedback. By doing this, assuming your product isn’t canceled, you’ve now reduced the single greatest risk your project faces.&lt;/p&gt;

&lt;p&gt;Stop stop worrying about how much the project costs to develop. Worry about building a great product.&lt;/p&gt;

&lt;p&gt;And if you'd like to learn more about this way of looking at problems, hit a search engine and search for &lt;a href="https://duckduckgo.com/?q=noestimates&amp;amp;t=h_&amp;amp;ia=web"&gt;noestimates&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;If you have any thoughts about this, please leave a comment below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.pexels.com/photo/orange-and-white-plastic-container-on-white-printer-paper-6120214/"&gt;Cover Photo by Nataliya Vaitkevich from Pexels&lt;/a&gt;&lt;/p&gt;

</description>
      <category>management</category>
      <category>agile</category>
    </item>
    <item>
      <title>Use Immutable Objects</title>
      <dc:creator>Ovid</dc:creator>
      <pubDate>Tue, 20 Apr 2021 06:54:25 +0000</pubDate>
      <link>https://dev.to/ovid/use-immutable-objects-4pbl</link>
      <guid>https://dev.to/ovid/use-immutable-objects-4pbl</guid>
      <description>&lt;h1&gt;
  
  
  Immutable Objects
&lt;/h1&gt;

&lt;p&gt;I’ve been spending time designing &lt;a href="https://github.com/Ovid/Cor/wiki"&gt;Corinna&lt;/a&gt;, a new object system to be shipped with the Perl language. Amongst its many features, it’s designed to make it easier to create immutable objects, but not everyone is happy with that. For example, consider the following class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="nv"&gt;class&lt;/span&gt; &lt;span class="nv"&gt;Box&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;has&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$height&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$depth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;reader&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;has&lt;/span&gt; &lt;span class="nv"&gt;$volume&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$width&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$height&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$depth&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$original_box&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$updated_box&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$original_box&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;depth&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;# h=1, w=2, d=9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because none of the slots have a &lt;code&gt;:writer&lt;/code&gt; attribute, there is no way to mutate this object. Instead you call a &lt;code&gt;clone&lt;/code&gt; method, supplying an overriding value for the constructor argument you need to change. The &lt;code&gt;$volume&lt;/code&gt; argument doesn’t get copied over because it’s derived from the constructor arguments.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Ovid/Cor/issues/8"&gt;But not everyone is happy with this approach&lt;/a&gt;. Aside from arguments about utility of the &lt;code&gt;clone&lt;/code&gt; method, the notion that objects should be immutable by default has frustrated some developers reading the Corinna proposal. Even when I point out just adding a &lt;code&gt;:writer&lt;/code&gt; attribute is all you need to do to get your mutability, people still object. So let’s have a brief discussion about immutability and why it’s useful.&lt;/p&gt;

&lt;p&gt;But first, here’s my last 2020 Perl Conference presentation on Corinna (at the time, called "Cor").&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/Tst-vs4Br2A"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  The Problem
&lt;/h1&gt;

&lt;p&gt;Imagine, for example, that you have a very simple &lt;code&gt;Customer&lt;/code&gt; object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$customer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;Customer&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;name&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Ovid&lt;/span&gt;&lt;span class="p"&gt;",&lt;/span&gt; 
    &lt;span class="s"&gt;birthdate&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;new&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="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code above, we’ll assume the &lt;code&gt;$customer&lt;/code&gt; can give us useful information about the state of that object. For example, we have a section of code guarded by a check to see if they are old enough to drink alcohol:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$ovid&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;old_enough_to_drink_alcohol&lt;/span&gt; &lt;span class="p"&gt;)&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above looks innocent enough and it’s the sort of thing we regularly see in code. But then this happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$ovid&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;old_enough_to_drink_alcohol&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ovid&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;birthdate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;# deep in the bowels of your code&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$cutoff_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;year&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$last_year&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;# oops!&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We had a guard to ensure that this code would not be executed if the customer wasn’t old enough to drink, but now in the middle of that code, due to how &lt;code&gt;DateTime&lt;/code&gt; is designed, someone’s set the customer birth date to last year! The code, at this point, is probably in an invalid state and its behavior can no longer be considered correct.&lt;/p&gt;

&lt;p&gt;But clearly no one would do something so silly, would they?&lt;/p&gt;

&lt;h1&gt;
  
  
  Global State
&lt;/h1&gt;

&lt;p&gt;We’ve known about &lt;a href="https://softwareengineering.stackexchange.com/questions/148108/why-is-global-state-so-evil"&gt;the dangers of global state&lt;/a&gt; for a long time. For example, if I call the following subroutine, will the program halt or not?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;next&lt;/span&gt; &lt;span class="p"&gt;($number) {&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;BLESS_ME_LARRY_FOR_I_HAVE_SINNED&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nb"&gt;die&lt;/span&gt; &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;This was a bad idea.&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$number&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You literally &lt;em&gt;cannot&lt;/em&gt; inspect the above code and tell me if it will &lt;code&gt;die&lt;/code&gt; when called because you cannot know, by inspection, what the &lt;code&gt;BLESS_ME_LARRY_FOR_I_HAVE_SINNED&lt;/code&gt; environment variable is set to. This is one of the reasons why global environment variables are discouraged.&lt;/p&gt;

&lt;p&gt;But here we’re talking about &lt;em&gt;mutable&lt;/em&gt; state.  You don’t want the above code to die, so you do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;BLESS_ME_LARRY_FOR_I_HAVE_SINNED&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="nv"&gt;say&lt;/span&gt; &lt;span class="k"&gt;next&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Except that now you’ve altered that mutable state and anything else which relies on that environment variable being set is unpredicatable. So we need to use &lt;code&gt;local&lt;/code&gt; to safely change that in the local scope:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;local&lt;/span&gt; &lt;span class="nv"&gt;$ENV&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;BLESS_ME_LARRY_FOR_I_HAVE_SINNED&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="nv"&gt;say&lt;/span&gt; &lt;span class="k"&gt;next&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even that is not good because there’s no indication of why we’re doing this but at least you can see how we can safely change that global variable in our local scope.&lt;/p&gt;

&lt;h1&gt;
  
  
  ORMs
&lt;/h1&gt;

&lt;p&gt;And I can hear your objection now:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“But Ovid, the DateTime object in your first example isn’t global!”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That’s true. What we had was this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$ovid&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;old_enough_to_drink_alcohol&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$ovid&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;birthdate&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;# deep in the bowels of your code&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$cutoff_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;year&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$last_year&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;# oops!&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the offending line should have been this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;    &lt;span class="c1"&gt;# note the clone().&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$cutoff_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$date&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;clone&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;year&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$last_year&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because the &lt;code&gt;set&lt;/code&gt; method mutates the object in place, causing &lt;em&gt;everything holding a reference to that object to silently change&lt;/em&gt;. It’s not global in the normal sense, but this &lt;a href="https://rjbs.manxome.org/rubric/entry/1929"&gt;action at a distance is a source of very real bugs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It’s a serious enough problem that &lt;a href="https://metacpan.org/pod/DateTime::Moonpig"&gt;DateTime::Moonpig&lt;/a&gt; and &lt;a href="https://metacpan.org/pod/DateTimeX::Immutable"&gt;DateTimeX::Immutable&lt;/a&gt; have both been written to provide immutable &lt;code&gt;DateTime&lt;/code&gt; objects, and that brings me to &lt;a href="https://metacpan.org/release/DBIx-Class"&gt;DBIx::Class&lt;/a&gt;, an excellent ORM for Perl.&lt;/p&gt;

&lt;p&gt;As of this writing, it’s been around for about 15 years and provides a component called &lt;a href="https://dev.toDBIx::Class::InflateColumn::DateTime"&gt;DBIx::Class::InflateColumn::DateTime&lt;/a&gt;. This allows you to do things like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="nb"&gt;package&lt;/span&gt; &lt;span class="nv"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;base&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DBIx::Class::Core&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;

&lt;span class="nv"&gt;__PACKAGE__&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;load_components&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sx"&gt;qw/InflateColumn::DateTime/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;__PACKAGE__&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;add_columns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;starts_when&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;data_type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="s"&gt;create_date&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;data_type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;date&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, whenever you call &lt;code&gt;starts_when&lt;/code&gt; or &lt;code&gt;create_date&lt;/code&gt; on an &lt;code&gt;Event&lt;/code&gt; instance, you’ll get a &lt;code&gt;DateTime&lt;/code&gt; object instead of just the raw string from the database. Further, you can &lt;em&gt;set&lt;/em&gt; a &lt;code&gt;DateTime&lt;/code&gt; object and not worry about your particular database’s date syntax. It &lt;em&gt;just works&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Except that the object is mutable and we don’t want that. You can fix this by writing your own &lt;code&gt;DBIx::Class&lt;/code&gt; component to use immutable &lt;code&gt;DateTime&lt;/code&gt; objects.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="nb"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;My::Schema::Component::&lt;/span&gt;&lt;span class="nv"&gt;ImmutableDateTime&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;DateTimeX::&lt;/span&gt;&lt;span class="nv"&gt;Immutable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DBIx::Class::InflateColumn::DateTime&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;

&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;_post_inflate_datetime&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;@args&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;@_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$dt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$self&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;next&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nv"&gt;method&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nn"&gt;DateTimeX::&lt;/span&gt;&lt;span class="nv"&gt;Immutable&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;from_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$dt&lt;/span&gt; &lt;span class="p"&gt;);&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;And then load this component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="nv"&gt;__PACKAGE__&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;load_components&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sx"&gt;qw/+My::Schema::Component::ImmutableDateTime/&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, when you fetch your objects from the database, you get nice, immutable &lt;code&gt;DateTime&lt;/code&gt;s. And it will be interesting to see where your codebase fails!&lt;/p&gt;

&lt;p&gt;Does all of this mean we should never use mutable objects? Of course not.  Imagine creating an immutable cache where, if you wanted to add or delete an entry, you had to clone the entire cache to set the new state. That would likely defeat the main purpose of a cache: speeding things up. But in general, immutability is a good thing and is something to strive for. Trying to debug  why code far, far away from your code has reset your data is not fun.&lt;/p&gt;

</description>
      <category>perl</category>
      <category>oop</category>
    </item>
    <item>
      <title>A Tiny Note About Interfaces</title>
      <dc:creator>Ovid</dc:creator>
      <pubDate>Mon, 12 Apr 2021 07:02:42 +0000</pubDate>
      <link>https://dev.to/ovid/a-tiny-note-about-interfaces-4i52</link>
      <guid>https://dev.to/ovid/a-tiny-note-about-interfaces-4i52</guid>
      <description>&lt;p&gt;A client wanted to take their current monolithic application and break it up into a series of interconnected services. They wanted to go down the &lt;a href="https://www.ibm.com/cloud/learn/soa" rel="noopener noreferrer"&gt;Service Oriented Archicture (SOA)&lt;/a&gt; road, but they had made some curious choices regarding &lt;em&gt;what&lt;/em&gt; they wanted to expose in their APIs.&lt;/p&gt;

&lt;p&gt;That got me to thinking about my daughter, Lilly-Rose. When she was rather young, I had an Android phone and she liked to play with that when we were riding in the car. One day we were driving along when she noticed a new icon on the phone. It had her face. And her name.&lt;/p&gt;

&lt;p&gt;“Papa, what’s that?”&lt;/p&gt;

&lt;p&gt;“I don’t know. Why don’t you click it?”&lt;/p&gt;

&lt;p&gt;So she did. And this is what she saw.&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%2Fh43pyvvqudavvygjnp0a.png" 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%2Fh43pyvvqudavvygjnp0a.png" alt="A blank application with a single button reading "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;She smiled, but looked confused and showed me the phone (I was in the back with her). “It just says ‘click me’.”&lt;/p&gt;

&lt;p&gt;“So, why not?”&lt;/p&gt;

&lt;p&gt;So she clicked it and saw something like this:&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%2Fe3u1sztd99xlj7a5vaqi.png" 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%2Fe3u1sztd99xlj7a5vaqi.png" alt="The application now reads "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then she clicked it again and saw something like this:&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%2Fefbxdfk5m61d7nqmeb70.png" 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%2Fefbxdfk5m61d7nqmeb70.png" alt="The application now reads "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;She kept getting weird things like “the cats are waiting for us to make a mistake before they take over” or “what do lawyers wear to court? Lawsuits.” (I had to explain that one) and she was laughing and having a blast. Every time she pressed the button she'd get a random joke on a random background color. Chalk that up as a win for papa.&lt;/p&gt;

&lt;p&gt;So what does this have to do with APIs? You can think of that application as an API, albeit for my daughter and not another piece of software. I created it so that even my young daughter could figure out how to use it. I even got a bug report from her! (“Papa, it doesn’t work!”) It took me a while to realize that sometimes she would get the same joke twice &lt;em&gt;and&lt;/em&gt; the same background color. So I made sure she’d never get the same joke twice in row.&lt;/p&gt;

&lt;p&gt;Now you, as a software developer, might wonder ”what programming language did Ovid use?” or “are the jokes stored in a database?” My daughter didn’t care and neither should you. The technology used was the scaffolding to build the application. The application’s interface should hide the scaffolding.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Exposing the scaffolding is making an implicit promise that it can be relied on. People will be unhappy if you break that promise.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;To be honest, this is so trivial that it seems ridiculous to write a post about it, but I've seen this violated so many times that I &lt;em&gt;have&lt;/em&gt; to write a post about it. What language did I use? Who cares? So long as the app is the same, my daughter would still laugh. Were the jokes hard-coded, stored in a database, fetched from a remote service? Again, it doesn't matter. And it shouldn't be exposed in the interface.&lt;/p&gt;

&lt;p&gt;It’s not that those technical decisions are unimportant. It’s that the end consumer shouldn’t have to be aware of them. When you hide them, you can change them, or fix them. When you expose them, whether it be in an application or an API, consumers of what you’re providing learn to rely on those details and then you get stuck providing them. So don’t do that.&lt;/p&gt;




&lt;p&gt;For those who &lt;em&gt;must&lt;/em&gt; know the technical details, here’s the core of the app, written in Kotlin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;io.github.ovid.lilly_roserules&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;android.graphics.Color&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.appcompat.app.AppCompatActivity&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;android.os.Bundle&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;android.widget.Button&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;android.widget.TextView&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;kotlin.random.Random&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.constraintlayout.widget.ConstraintLayout&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainActivity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;AppCompatActivity&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;lateinit&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ConstraintLayout&lt;/span&gt;

    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;setContentView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;activity_main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;layout&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;findViewById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;mainlayout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;rollButton&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;findViewById&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rollButton&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;quoteTextView&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;findViewById&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextView&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quoteTextView&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;messages&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getMessages&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;colors&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getColors&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;lastChoice&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="n"&gt;rollButton&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setOnClickListener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;thisChoice&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

            &lt;span class="c1"&gt;// make sure we never get the same message twice in a row&lt;/span&gt;
            &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;thisChoice&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;lastChoice&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;thisChoice&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nextInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;lastChoice&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;thisChoice&lt;/span&gt;

            &lt;span class="c1"&gt;// picks a random message&lt;/span&gt;
            &lt;span class="n"&gt;quoteTextView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;thisChoice&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="c1"&gt;// picks a random background color&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;randColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nextInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBackgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parseColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;randColor&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getColors&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;colors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;arrayOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="c1"&gt;// list of colors&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;colors&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getMessages&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;messages&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;arrayOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="c1"&gt;// list of jokes&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;messages&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;It was dead-simple and just thrown together quickly to amuse my daughter. But if I ever needed to make it “production ready,” all the important bits were hidden away. Because this was an application on a smart phone and not an API, this sounds silly, but I’m hard-pressed to find a better analogy to explain this mistake I see made over and over again.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>api</category>
      <category>android</category>
      <category>interfaces</category>
    </item>
    <item>
      <title>You Need Measurable Goals</title>
      <dc:creator>Ovid</dc:creator>
      <pubDate>Thu, 08 Apr 2021 05:30:06 +0000</pubDate>
      <link>https://dev.to/ovid/you-need-measurable-goals-47g9</link>
      <guid>https://dev.to/ovid/you-need-measurable-goals-47g9</guid>
      <description>&lt;p&gt;One thing I try to reinforce with clients, particularly those who have "big" projects, is that they need measurable success criteria. Sometimes that's dangerous because numbers can be gamed—such as the support manager who won an award for reducing support calls, only to find he had hidden the support phone number on our web site—but if you have &lt;em&gt;measurable&lt;/em&gt; goals, you can use that to declare success or failure, rather than punting on the problem.&lt;/p&gt;

&lt;p&gt;Case in point is my favorite "successful" project: I was with a company that decided to rewrite a massive Perl system in C++ because Perl was too slow.&lt;/p&gt;

&lt;p&gt;Already, I know several of you want to know what "too slow" means in this context, but no, you gotta wait for the punchline.&lt;/p&gt;

&lt;p&gt;This project was written years ago and was thus "legacy" code. It was clunky, but critical. Honestly, fixing it would have been cheaper and faster than a rewrite, but you probably know that. The dangers of rewrites are well-known.&lt;/p&gt;

&lt;p&gt;But it didn't matter. Perl was too slow, so C++ it was.&lt;/p&gt;

&lt;p&gt;And unlike many rewrites, this one was finished. It took them years, but they finished. The new code was a bit of a pig, but they finally lifted the millstone of Perl's performance from around their neck.&lt;/p&gt;

&lt;p&gt;But there was a small problem. Turns out the C++ system, years in development, a steaming pile of ones and zeros, and possibly just a wee bit unmaintainable ...&lt;/p&gt;

&lt;p&gt;... didn't run any faster than the Perl system it replaced.&lt;/p&gt;

&lt;p&gt;You know what makes software slow? Network congestion. I/O issues. SOAP instead of JSON. RPC. Oh, and the database. It's always the database. Unless a system is CPU-bound, changing the programming language will often gain you no performance improvements!&lt;/p&gt;

&lt;p&gt;The new project was started years ago, so when I asked, no one was sure if the system that was "too slow" had any performance profiling done to find the bottlenecks.&lt;/p&gt;

&lt;p&gt;But management crowned it a success anyway, because now that it was done, no one wanted to admit that its only measurable success criteria showed it to be a complete failure.&lt;/p&gt;

&lt;p&gt;When I left the company there was talk—I'm not kidding—about rewriting the system in Perl.&lt;/p&gt;

</description>
      <category>perl</category>
      <category>cpp</category>
      <category>performance</category>
    </item>
    <item>
      <title>Sane Database Change Management</title>
      <dc:creator>Ovid</dc:creator>
      <pubDate>Mon, 05 Apr 2021 09:52:08 +0000</pubDate>
      <link>https://dev.to/ovid/sane-database-change-management-3cdm</link>
      <guid>https://dev.to/ovid/sane-database-change-management-3cdm</guid>
      <description>&lt;p&gt;This happens all the time when dealing with new clients:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Me: How do you update the database?&lt;br&gt;
Client: Check the 'sql' directory to see if there's new SQL in there. Run that.&lt;br&gt;
Me: Manually? Are you serious?&lt;br&gt;
Client: Yes. We're used to it.&lt;br&gt;
Me: Um, OK. So how do you back out the change if there's a problem?&lt;br&gt;
Client: There's hardly ever a problem, but we fix it by hand.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And things go downhill from there. I frequently meet clients with insane database migration strategies. The "dump SQL in a directory" for people to apply is an annoyingly common strategy. There's no clear way to roll it back and, you can't declare dependencies. If you're using a database like MySQL or Oracle, if it contains DDL changes, those aren't transaction safe, so they really should be in their own migration, but they're not. I even had one client where they &lt;em&gt;emailed&lt;/em&gt; developers to let them know which SQL to apply.&lt;/p&gt;

&lt;p&gt;A few clients have an in-house database migration strategy involving numbered migrations. It often looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;...
213-up-add-index-on-address-state.sql
213-down-add-index-on-address-state.sql
214-up-add-customer-notes-table.sql
214-down-add-customer-notes-table.sql
215-up-add-sales-tax-sproc.sql
215-down-add-sales-tax-sproc.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;That, at least, can allow devs to back out changes (but tricky if your DDL isn't transaction-safe), but it's amazing when you get to migration 215 and you have eight developers, four of whom need to make a database change and they're arguing over who gets number 216. Yes, I've seen this happen more than once.&lt;/p&gt;

&lt;p&gt;With a naïve numbering strategy, you can't declare dependencies, you get numbering conflicts, you really can't "tag" a particular migration for deployment, and so on.&lt;/p&gt;

&lt;p&gt;Or there are the migration strategies which allow migrations to be written in your favorite programming language. Those are often nice, but can't always leverage the strength of the database, often write very poor SQL, and make it hard for other teams not using the language to write migrations.&lt;/p&gt;

&lt;p&gt;There's a better way.&lt;/p&gt;


&lt;h1&gt;
  
  
  Sqitch
&lt;/h1&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%2F162wd8xdh88ifxlueps2.png" 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%2F162wd8xdh88ifxlueps2.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://sqitch.org/" rel="noopener noreferrer"&gt;sqitch&lt;/a&gt; (pronounced "skitch", &lt;em&gt;not&lt;/em&gt; "skwitch") Web site describes sqitch as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sensible database-native change management for framework-free development and dependable deployment.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is, uh, quite the mouthful. But it's accurate. It has &lt;a href="https://sqitch.org/docs/" rel="noopener noreferrer"&gt;great documentation&lt;/a&gt; with tutorials for Postgres, SQLite, MySQL, Firebird, Exasol, Oracle, Snowflake, and Vertica. Out of the box, sqitch offers sane, easy-to-use database change management. Since it's both free and open source, it's also easy to hook into and customize, if needed.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/sqitchers" rel="noopener noreferrer"&gt;
        sqitchers
      &lt;/a&gt; / &lt;a href="https://github.com/sqitchers/sqitch" rel="noopener noreferrer"&gt;
        sqitch
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Sensible database change management
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;App/Sqitch version v1.4.2-dev&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;br&gt;
&lt;thead&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;th&gt;Release&lt;/th&gt;
&lt;br&gt;
&lt;th&gt;Coverage&lt;/th&gt;
&lt;br&gt;
&lt;th&gt;Database&lt;/th&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/thead&gt;
&lt;br&gt;
&lt;tbody&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://metacpan.org/dist/App-Sqitch" title="Latest version on CPAN" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/c5dc8f5acaa4efa4693242aa18d6015458874378b8a3bc1d7cd70d99db0123c0/68747470733a2f2f696d672e736869656c64732e696f2f6370616e2f762f4170702d5371697463683f6c6162656c3d2546302539462539332539412532304350414e" alt="CPAN"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://github.com/sqitchers/sqitch/actions/workflows/os.yml" title="Tested on Linux, macOS, and Windows" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/sqitchers/sqitch/actions/workflows/os.yml/badge.svg" alt="OSes"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://github.com/sqitchers/sqitch/actions/workflows/exasol.yml" title="Tested with Exasol 7.0–7.1" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/sqitchers/sqitch/actions/workflows/exasol.yml/badge.svg" alt="Exasol"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://github.com/sqitchers/sqitch/actions/workflows/oracle.yml" title="Tested with Oracle 11, 18, and 21" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/sqitchers/sqitch/actions/workflows/oracle.yml/badge.svg" alt="Oracle"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://hub.docker.com/r/sqitch/sqitch" title="Latest version on Docker Hub" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/c630ea142e172b125094ff0d17a69b7e2980daa4f72b0adee1a8d96cef067341/68747470733a2f2f696d672e736869656c64732e696f2f646f636b65722f762f7371697463682f7371697463683f6c6162656c3d254630253946253930254233253230446f636b657226736f72743d73656d766572" alt="Docker"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://github.com/sqitchers/sqitch/actions/workflows/perl.yml" title="Tested with Perl 5.12–5.38" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/sqitchers/sqitch/actions/workflows/perl.yml/badge.svg" alt="Perl"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://github.com/sqitchers/sqitch/actions/workflows/firebird.yml" title="Tested with Firebird 2.5, 3, and 4" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/sqitchers/sqitch/actions/workflows/firebird.yml/badge.svg" alt="Firebird"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://github.com/sqitchers/sqitch/actions/workflows/snowflake.yml" title="Tested with Snowflake" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/sqitchers/sqitch/actions/workflows/snowflake.yml/badge.svg" alt="Snowflake"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://github.com/sqitchers/homebrew-sqitch#readme" title="Latest Homebrew Tap version" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/d1fa20b8e607130a12030b664fc6e63858d203bd4e4ee602d7c95c57099b5e22/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f762f7461672f7371697463686572732f686f6d65627265772d7371697463683f6c6162656c3d254630253946253844254241253230486f6d656272657726736f72743d73656d766572" alt="Homebrew"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://coveralls.io/r/sqitchers/sqitch" title="Test Coverage" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/fe72b25cdaab9d5c8c4effa0ffdc338723c5ed5f024bc4021dd63166a727efee/68747470733a2f2f696d672e736869656c64732e696f2f636f766572616c6c732f6769746875622f7371697463686572732f7371697463683f6c6162656c3d254630253946253933253838253230436f766572616765" alt="Coverage"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://github.com/sqitchers/sqitch/actions/workflows/mysql.yml" title="Tested with MySQL 5.5–8 and MariaDB 10.0–11.0" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/sqitchers/sqitch/actions/workflows/mysql.yml/badge.svg" alt="MySQL"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://github.com/sqitchers/sqitch/actions/workflows/sqlite.yml" title="Tested with SQLite 3.8–3.42" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/sqitchers/sqitch/actions/workflows/sqlite.yml/badge.svg" alt="SQLite"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://packages.debian.org/stable/sqitch" title="Latest version on Debian" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/c3ec38573c5d2a2e0b7b031e7d952c55609cfa66c3707e86348e95ad245882ce/68747470733a2f2f696d672e736869656c64732e696f2f64656269616e2f762f7371697463683f6c6162656c3d25463025394625384425413525323044656269616e" alt="Debian"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://github.com/sqitchers/sqitch/actions/workflows/pg.yml" title="Tested with PostgreSQL 8.4–16" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/sqitchers/sqitch/actions/workflows/pg.yml/badge.svg" alt="Postgres"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://github.com/sqitchers/sqitch/actions/workflows/vertica.yml" title="Tested with Vertica 7.2–12.0" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/sqitchers/sqitch/actions/workflows/vertica.yml/badge.svg" alt="Vertica"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;tr&gt;


&lt;td&gt;&lt;a href="https://github.com/sqitchers/sqitch/actions/workflows/yugabyte.yml" title="Tested with YugabyteDB 2.6–2.19" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/sqitchers/sqitch/actions/workflows/yugabyte.yml/badge.svg" alt="Yugabyte"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;td&gt;&lt;a href="https://github.com/sqitchers/sqitch/actions/workflows/cockroach.yml" title="Tested with CockroachDB v21-23" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/sqitchers/sqitch/actions/workflows/cockroach.yml/badge.svg" alt="Cockroach"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;br&gt;
&lt;/tr&gt;
&lt;br&gt;
&lt;/tbody&gt;
&lt;br&gt;
&lt;/table&gt;&lt;/div&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://sqitch.org/" rel="nofollow noopener noreferrer"&gt;Sqitch&lt;/a&gt; is a database change management application. It currently supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://postgresql.org/" rel="nofollow noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; 8.4+&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.yugabyte.com/yugabytedb/" rel="nofollow noopener noreferrer"&gt;YugabyteDB&lt;/a&gt; 2.6+&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.cockroachlabs.com/product/" rel="nofollow noopener noreferrer"&gt;CockroachDB&lt;/a&gt; 21+&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sqlite.org/" rel="nofollow noopener noreferrer"&gt;SQLite&lt;/a&gt; 3.8.6+&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://dev.mysql.com/" rel="nofollow noopener noreferrer"&gt;MySQL&lt;/a&gt; 5.1+&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://mariadb.org" rel="nofollow noopener noreferrer"&gt;MariaDB&lt;/a&gt; 10.0+&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.oracle.com/database/" rel="nofollow noopener noreferrer"&gt;Oracle&lt;/a&gt; 10g+,&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.firebirdsql.org/" rel="nofollow noopener noreferrer"&gt;Firebird&lt;/a&gt; 2.0+&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.vertica.com/" rel="nofollow noopener noreferrer"&gt;Vertica&lt;/a&gt; 7.2+&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.exasol.com/" rel="nofollow noopener noreferrer"&gt;Exasol&lt;/a&gt; 6.0+&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.snowflake.net/" rel="nofollow noopener noreferrer"&gt;Snowflake&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What makes it different from your typical migration approaches? A few things:&lt;/p&gt;


&lt;ul&gt;

&lt;li&gt;

&lt;p&gt;No opinions&lt;/p&gt;

&lt;p&gt;Sqitch is not tied to any framework, ORM, or platform. Rather, it is a
standalone change management system with no opinions about your database
engine, application framework, or development environment.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Native scripting&lt;/p&gt;
&lt;p&gt;Changes are implemented as scripts native to your selected database engine
Writing a &lt;a href="https://postgresql.org/" rel="nofollow noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; application? Write SQL scripts for &lt;a href="https://www.postgresql.org/docs/current/static/app-psql.html" rel="nofollow noopener noreferrer"&gt;&lt;code&gt;psql&lt;/code&gt;&lt;/a&gt;. Writing
an &lt;a href="https://www.oracle.com/database/" rel="nofollow noopener noreferrer"&gt;Oracle&lt;/a&gt;-backed app? Write SQL scripts for &lt;a href="https://www.orafaq.com/wiki/SQL*Plus" rel="nofollow noopener noreferrer"&gt;SQL*Plus&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dependency resolution&lt;/p&gt;
&lt;p&gt;Database changes may declare dependencies on other changes -- even on
changes from other Sqitch projects. This ensures proper order of
execution, even when you've committed changes to your VCS out-of-order.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Deployment integrity&lt;/p&gt;
&lt;p&gt;…&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/sqitchers/sqitch" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;





&lt;h2&gt;
  
  
  Sqitch 101
&lt;/h2&gt;

&lt;p&gt;I won’t cover setting up sqitch. It's pretty easy and the tutorials handle it well. Instead, I'll explain how I get teams up-and-running quickly with it.&lt;/p&gt;

&lt;p&gt;However, there is one small change I recommend. By default, in the current directory, sqitch will add &lt;code&gt;deploy&lt;/code&gt;, &lt;code&gt;revert&lt;/code&gt;, and &lt;code&gt;verify&lt;/code&gt; directories. Your SQL will go into those directories. I prefer to minimize the number of top-level directories in my projects, so after I have things set up, I usually run a command similar to this (the following assumes PostgreSQL):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sqitch engine alter pg --top-dir sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tells the &lt;code&gt;sqitch&lt;/code&gt; program that when you're using the PostgreSQL engine, create &lt;code&gt;sql/deploy&lt;/code&gt;/, &lt;code&gt;sql/revert&lt;/code&gt;, and &lt;code&gt;sql/verify&lt;/code&gt; directories. Thus, you only have one top-level &lt;code&gt;sql&lt;/code&gt; directory for managing your sqitch files.&lt;/p&gt;

&lt;p&gt;And for the sake of what follows, we'll assume that we have a &lt;code&gt;acme_test&lt;/code&gt; target that we run our tests against and that we've set that target to be the default (that will make more sense when you've read the docs).&lt;/p&gt;

&lt;p&gt;Also, please note that my usage pattern is not quite the same as what's taught in the tutorials. Instead, it's designed to be an easy workflow for any developer to understand.&lt;/p&gt;

&lt;p&gt;After you have sqitch set up and have your initial schema added to sqitch, you can add a change. The basic pattern is "create a branch", "add sql changes", "modify code as needed", "commit" and merge back.&lt;/p&gt;

&lt;p&gt;To be more specific, let's say that you want to add a &lt;code&gt;title&lt;/code&gt; column to the &lt;code&gt;customers&lt;/code&gt; table (note that the name passed to the &lt;code&gt;sqitch add&lt;/code&gt; command is arbitrary, but I recommend you pick a naming convention and stick to it).&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a branch in your source control&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;sqitch add customers/title&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Edit &lt;code&gt;sql/deploy/customers/title&lt;/code&gt; and &lt;code&gt;sql/revert/customers/title&lt;/code&gt; to add your sql&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;sqitch deploy&lt;/code&gt; to deploy those changes&lt;/li&gt;
&lt;li&gt;Edit your code if needed&lt;/li&gt;
&lt;li&gt;Run your tests&lt;/li&gt;
&lt;li&gt;Commit your changes&lt;/li&gt;
&lt;li&gt;Merge back to your main branch&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;(Note: for the first and last steps, if you're using &lt;code&gt;git&lt;/code&gt;, see my &lt;a href="https://dev.to/ovid/an-easy-git-workflow-4gm0"&gt;easy git workflow&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;When doing this, it's also a good idea to revert your the sqitch change(s) you've added and redeploy. This makes it easier to spot the case where your &lt;code&gt;revert&lt;/code&gt; file doesn't properly revert the &lt;code&gt;deploy&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqitch rebase &lt;span class="nt"&gt;--onto&lt;/span&gt; @HEAD^ &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, if you're not comfortable with the &lt;code&gt;rebase&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;bounce&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'sqitch revert --to @HEAD^ -y &amp;amp;&amp;amp; sqitch deploy'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every change goes through the same pattern. It's almost like working with git, with a steady queue of changes adding up. This is a simpler pattern than what is explained in the sqitch docs, but you don't have to explain reworking or rebasing changes. &lt;a href="https://sqitch.org/docs/" rel="noopener noreferrer"&gt;There are some presentations you might want to watch&lt;/a&gt; if you'd like to learn more.&lt;/p&gt;

&lt;p&gt;Sqitch was also highlighted on &lt;a href="https://twit.tv/shows/floss-weekly" rel="noopener noreferrer"&gt;FLOSS Weekly&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/TSfFzMdLHYg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;




&lt;h2&gt;
  
  
  So What?
&lt;/h2&gt;

&lt;p&gt;First and foremost, you get to write your database changes in SQL, not in some "DSL" that you're provided with. You can leverage the full power of your database. Often, I find SQL generators produce poor SQL, or simply won't produce the SQL that I need. When was the last time your DSL let you create optimizer hints?&lt;/p&gt;

&lt;p&gt;However, if you &lt;em&gt;do&lt;/em&gt; need more than just SQL, it's easy enough to write sqitch middleware to intercept the call to the database with your own wrapper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sqitch engine add pg --client /path/to/my/middleware
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've done this for a client who wanted to write database changes using Percona's excellent (and free) &lt;a href="https://www.percona.com/doc/percona-toolkit/LATEST/pt-online-schema-change.html" rel="noopener noreferrer"&gt;pt-online-schema-change&lt;/a&gt; tool.&lt;/p&gt;

&lt;p&gt;Also, what happens if you get a conflict with &lt;code&gt;git&lt;/code&gt; and the &lt;code&gt;sqitch.plan&lt;/code&gt; file? It's easy to have a bad rebase and fix it incorrectly. Internally, &lt;code&gt;sqitch&lt;/code&gt; uses checksums to determine the changes you've applied and their order, so a bad rebase won't allow you to accidentally apply the wrong changes.&lt;/p&gt;




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

&lt;p&gt;I've barely scratched the surface of what you can do with &lt;code&gt;sqitch&lt;/code&gt;. It's amazing how many other database change management systems get this wrong. When I switch teams over to &lt;code&gt;sqitch&lt;/code&gt;, most of their database development pain just goes away. If you're having trouble with database migrations, try &lt;code&gt;sqitch&lt;/code&gt;. You won't regret it.&lt;/p&gt;




&lt;p&gt;Cover Photo by &lt;a href="https://unsplash.com/@tofi?utm_source=unsplash&amp;amp;utm_medium=referral&amp;amp;utm_content=creditCopyText" rel="noopener noreferrer"&gt;Tobias Fischer&lt;/a&gt;&lt;/p&gt;

</description>
      <category>database</category>
    </item>
    <item>
      <title>The Zen of Test Suites</title>
      <dc:creator>Ovid</dc:creator>
      <pubDate>Tue, 30 Mar 2021 07:23:47 +0000</pubDate>
      <link>https://dev.to/ovid/the-zen-of-test-suites-304f</link>
      <guid>https://dev.to/ovid/the-zen-of-test-suites-304f</guid>
      <description>&lt;h1&gt;
  
  
  The Zen of Application Test Suites
&lt;/h1&gt;

&lt;p&gt;This is a long read, but it's an important one about one of the most common problems I see with my clients: they all have broken test suites. Learning testing is as much a skill as learning coding and this long article only scratches the surface.&lt;/p&gt;

&lt;p&gt;Much of what I describe below is generic and applies to test suites written in any programming language, despite many examples being written in Perl.&lt;/p&gt;




&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;I often speak with developers who take a new job and they describe a Web site built out of a bunch of separate scripts scattered randomly through directories, lots of duplicated code, poor use of modules, with embedded SQL and printing HTTP headers and HTML directly. The developers shake their head in despair, but grudgingly admit an upside: job security. New features are time-consuming to add, changes are difficult to implement and may have wide-ranging side-effects, and reorganizing the codebase to have a proper separation of concerns, to make it cheaper and safer to hack on, will take lots and lots of time.&lt;/p&gt;

&lt;p&gt;A bunch of randomly scattered scripts, no separation of concerns, lots of duplicated code, poor use of modules, SQL embedded directly in them? Does this sound familiar? It's your standard test suite. We're horrified by this in the code, but don't bat an eyelash at the test suite.&lt;/p&gt;

&lt;p&gt;Part of this is because much, if not most, of the testing examples we find focus on testing distributions, not applications. If you were to look at the tests for my module &lt;a href="https://github.com/Ovid/dbix-class-easyfixture" rel="noopener noreferrer"&gt;DBIx::Class::EasyFixture&lt;/a&gt;, you'd see the following tests:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00-load.t
basic.t
definitions.t
groups.t
load_multiple_fixtures.t
many_to_many.t
no_transactions.t
one_to_many.t
one_to_one.t
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;These tests were added one by one, as I added new features to &lt;code&gt;DBIx::Class::EasyFixture&lt;/code&gt; and each &lt;code&gt;*.t&lt;/code&gt; file represents (more or less) a different feature.&lt;/p&gt;

&lt;p&gt;For a small distribution, this isn't too bad because it's very easy to keep it all in your head. With only nine files, it's trivial to glance at them, or grep them, to figure out where the relevant tests are. Applications, however, are a different story. This is the number of test classes from the &lt;a href="https://taustation.space" rel="noopener noreferrer"&gt;Tau Station MMORPG&lt;/a&gt; test suite:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ find t/tests -name '*.pm' | wc -l
589
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;One codebase I worked on had close to a million lines of code with thousands of test scripts. You couldn't hold the codebase in your head, you're couldn't &lt;em&gt;glance&lt;/em&gt; at the tests to figure out what went where, nor was grepping necessarily going to tell you as tests for particular sections of code were often scattered around multiple test scripts.  And, of course, I regularly hear the lament I've heard at many shops with larger codebases: where are the tests for feature &lt;em&gt;X&lt;/em&gt;? Instead of just sitting down and writing code, the developers are hunting for the tests, wondering if there are any tests for the feature they're working on and, if not, trying to figure out where to put their new tests.&lt;/p&gt;

&lt;p&gt;Unfortunately, this disorganization is only the start of the problem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Large-scale test suites
&lt;/h2&gt;

&lt;p&gt;I've worked with many companies with large test suites and they tend to share some common problems. I list them below in the order I try to address these problems (in other words, roughly easiest to hardest).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests often emit warnings&lt;/li&gt;
&lt;li&gt;Tests often fail ("oh, that sometimes fails. Ignore it.")&lt;/li&gt;
&lt;li&gt;There is little evidence of organization&lt;/li&gt;
&lt;li&gt;Much of the testing code is duplicated&lt;/li&gt;
&lt;li&gt;Testing fixtures are frequently not used (or poorly used)&lt;/li&gt;
&lt;li&gt;Code coverage is spotty&lt;/li&gt;
&lt;li&gt;They take far too long to run&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Problems are one thing, but what features do we want to see in large-scale test suites?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests should be very easy to write and run&lt;/li&gt;
&lt;li&gt;They should run relatively quickly&lt;/li&gt;
&lt;li&gt;The order in which tests run should not matter&lt;/li&gt;
&lt;li&gt;Test output should be clean&lt;/li&gt;
&lt;li&gt;It should be obvious where to find tests for a particular piece of code&lt;/li&gt;
&lt;li&gt;Testing code should not be duplicated&lt;/li&gt;
&lt;li&gt;Code coverage should be able to analyze different aspects of the system&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's take a look at some of the problems and try to understand their impacts.  While it's good to push a test suite into a desirable state, often this is risky if the underlying problems are ignored. I will offer recommendations for resolving each problem, but it's important to understand that these are &lt;em&gt;recommendations&lt;/em&gt;. They may not apply to your situation.&lt;/p&gt;




&lt;h3&gt;
  
  
  Tests often emit warnings
&lt;/h3&gt;

&lt;p&gt;This seems rather innocuous. Sure, code emits warnings and we're used to that.  Unfortunately, we sometimes forget that warnings are &lt;em&gt;warnings&lt;/em&gt;: there might very well be something wrong.&lt;/p&gt;

&lt;p&gt;In my time at the BBC, one of the first things I did was try to clean up all of the warnings. One was a normal warning about use of an undefined variable, but it was unclear to me from the code if this should be an acceptable condition. Another developer looked at it with me and realized that the variable should never be undefined: this warning was masking a very serious bug in the code, but the particular condition was not explicitly tested. By rigorously eliminating all warnings, we found it easier to make our code more correct, and in those places where things were dodgy, comments were inserted into the code to explain why warnings were suppressed.  In short: the code became easier to maintain.&lt;/p&gt;

&lt;p&gt;Another issue with warnings in the test suite is that they condition developers to ignore warnings. We get so used to them that we stop reading them, even if something serious is going on (on a related note, I often listen to developers complain about stack traces, but a careful reading of a stack trace will often reveal the exact cause of the exception). New warnings crop up, warnings change, but developers conditioned to ignore them often overlook serious issues with their code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommendation&lt;/strong&gt;: Eliminate all warnings from your test suite, but investigate each one to understand if it reflects a serious issue. Also, some tests will capture STDERR, effectively hiding warnings. Making warnings fatal while running tests can help to overcome this problem.&lt;/p&gt;




&lt;h3&gt;
  
  
  Tests often fail ("oh, that sometimes fails. Ignore it.")
&lt;/h3&gt;

&lt;p&gt;For one client, their hour-long test suite had many failing tests. When I first started working on it, I had a developer walk me through all of the failures and explain why they failed and why they were hard to fix. Obviously this is a far more serious problem than warnings, but in the minds of the developers, they were under constant deadline pressures and as far as management was concerned, the test suite was a luxury to keep developers happy, not "serious code." As a result, developers learned to recognize these failures and consoled themselves with the thought that they understood the underlying issues.&lt;/p&gt;

&lt;p&gt;Of course, that's not really how it works. The developer explaining the test failures admitted that he didn't understand some of them and with longer test suites that routinely fail, more failures tend to crop up. Developers conditioned to accept failures tend not to notice them. They kick off the test suite, run and grab some coffee and later glance over the results to see if they look reasonable (that's assuming they run all of the tests, something which often stops happening at this point). What's worse, continuous integration tools are often built to accomodate this. From the Jenkin's &lt;a href="https://wiki.jenkins-ci.org/display/JENKINS/xUnit+Plugin" rel="noopener noreferrer"&gt;xUnit Plugin page&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;h1&gt;
  
  
  Features
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Records xUnit tests&lt;/li&gt;
&lt;li&gt;Mark the build unstable or fail according to threshold values&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, there's an "acceptable" level of failure. What's the acceptable level of failure when you debit someone's credit card, or you're sending their medical records to someone, or you're writing embedded software that can't be easily updated?&lt;/p&gt;

&lt;p&gt;Dogmatism aside, you can make a case for acceptable levels of test failure, but you need to understand the risks and be prepared to accept them. However, for the purposes of this document, we'll assume that the acceptable level of failure is zero.&lt;/p&gt;

&lt;p&gt;If you absolutely cannot fix a particular failure, you should at least mark the test as &lt;code&gt;TODO&lt;/code&gt; so that the test suite can pass. Not only does this help to guide you to a clean test suite, the &lt;code&gt;TODO&lt;/code&gt; reason is generally embedded in the test, giving the next developer a clue to what's going on.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommendation&lt;/strong&gt;: Do not allow any failing tests. If tests fail which do not impact the correctness of the application (such as documentation or "coding style" tests), they should be separated from your regular tests in some manner and your systems should recognize that it's OK for them to fail.&lt;/p&gt;

&lt;h3&gt;
  
  
  There is little evidence of organization
&lt;/h3&gt;

&lt;p&gt;As mentioned previously, a common lament amongst developers is the difficulty of finding tests for the code they're working on. Consider the case of &lt;a href="https://metacpan.org/release/HTML-TokeParser-Simple" rel="noopener noreferrer"&gt;HTML::TokeParser::Simple&lt;/a&gt;.  The library is organized like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lib/
└── HTML
    └── TokeParser
        ├── Simple
        │   ├── Token
        │   │   ├── Comment.pm
        │   │   ├── Declaration.pm
        │   │   ├── ProcessInstruction.pm
        │   │   ├── Tag
        │   │   │   ├── End.pm
        │   │   │   └── Start.pm
        │   │   ├── Tag.pm
        │   │   └── Text.pm
        │   └── Token.pm
        └── Simple.pm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There's a class in there named &lt;code&gt;HTML::TokeParser::Simple::Token::ProcessInstruction&lt;/code&gt;. Where, in the following tests, would you find the tests for process instructions?&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t
├── constructor.t
├── get_tag.t
├── get_token.t
├── internals.t
└── munge_html.t
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You might think it's in the &lt;code&gt;get_token.t&lt;/code&gt; test, but are you sure? And what's that strange &lt;code&gt;munge_html.t&lt;/code&gt; test? Or the &lt;code&gt;internals.t&lt;/code&gt; test? As mentioned, for a small library, this really isn't too bad. However, what if we reorganized our tests to reflect our library hierarchy?&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t/
└── tests/
    └── html/
        └── tokeparser/
            ├── simple/
            │   ├── token/
            │   │   ├── comment.t
            │   │   ├── declaration.t
            │   │   ├── tag/
            │   │   │   ├── end.t
            │   │   │   └── start.t
            │   │   ├── tag.t
            │   │   └── text.t
            │   └── token.t
            └── simple.t
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;It's clear that the tests for &lt;code&gt;HTML::TokeParser::Simple::Token::Tag::Start&lt;/code&gt; are in &lt;code&gt;t/tests/html/tokeparser/simple/token/tag/start.t&lt;/code&gt;. And you can see easily that there is no file for &lt;code&gt;processinstruction.t&lt;/code&gt;. This test organization not only makes it easy to find where your tests are, it's also easy to program your editor to automatically switch between the code and the tests for the code. For large test suites, this saves a huge amount of time.  When I reorganized the test suite of the BBC's central metadata repository, &lt;a href="http://www.bbc.co.uk/blogs/bbcinternet/2009/02/what_is_pips.html" rel="noopener noreferrer"&gt;PIPs&lt;/a&gt;, I followed a similar pattern and it made our life much easier.&lt;/p&gt;

&lt;p&gt;(&lt;strong&gt;Note&lt;/strong&gt;: the comment about programming your editor is important. Effective use of your editor/IDE is one of the most powerful tools in a developer's toolbox.)&lt;/p&gt;

&lt;p&gt;Of course, your test suite could easily be more complicated and your top-level directories inside of your test directory may be structured differently:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t
├── unit/
├── integration/
├── api/
└── web/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Recommendation&lt;/strong&gt;: Organize your test files to have a predictable, discoverable structure. The test suite should be much easier to work with.&lt;/p&gt;




&lt;h3&gt;
  
  
  Much of the testing code is duplicated
&lt;/h3&gt;

&lt;p&gt;We're aghast that people routinely cut-n-paste their application code, but we don't even notice when people do this in their test code. More than once I've worked on a test suite with a significant logic change and I've had to find this duplicated code and either change it many places or try to refactor it so that it's in a single place and then change it. We already know why duplicated code is bad, I'm unsure why we tolerate this in test suites.&lt;/p&gt;

&lt;p&gt;Much of my work in tests has been to reduce this duplication. For example, many test scripts list the same set of modules at the top. I did a heuristic analysis of tests on the CPAN and chose the most popular testing modules and that allowed me to change this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Test::&lt;/span&gt;&lt;span class="nv"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Test::&lt;/span&gt;&lt;span class="nv"&gt;Differences&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Test::&lt;/span&gt;&lt;span class="nv"&gt;Deep&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Test::&lt;/span&gt;&lt;span class="nv"&gt;Warn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Test::&lt;/span&gt;&lt;span class="nv"&gt;More&lt;/span&gt; &lt;span class="s"&gt;tests&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Test::&lt;/span&gt;&lt;span class="nv"&gt;Most&lt;/span&gt; &lt;span class="s"&gt;tests&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can easily use similar strategies to bundle up common testing modules into a single testing module that all of your tests use. Less boilerplate and you can easily dive into testing.&lt;/p&gt;

&lt;p&gt;Or as a more egregious example, I often see something like this (a silly example just for illustration purposes):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="nv"&gt;set_up_some_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;is&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;attr1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$expected1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attr1 works&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
&lt;span class="nv"&gt;is&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;attr2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$expected2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attr2 works&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
&lt;span class="nv"&gt;is&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;attr3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$expected3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attr3 works&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
&lt;span class="nv"&gt;is&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;attr4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$expected4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attr4 works&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
&lt;span class="nv"&gt;is&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;attr5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$expected5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attr5 works&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then a few lines later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="nv"&gt;set_up_some_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$new_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$new_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nv"&gt;is&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;attr1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$expected1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attr1 works&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
&lt;span class="nv"&gt;is&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;attr2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$expected2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attr2 works&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
&lt;span class="nv"&gt;is&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;attr3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$expected3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attr3 works&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
&lt;span class="nv"&gt;is&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;attr4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$expected4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attr4 works&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
&lt;span class="nv"&gt;is&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;attr5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$expected5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;attr5 works&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then a few lines later, the same thing ...&lt;/p&gt;

&lt;p&gt;And in another test file, the same thing ...&lt;/p&gt;

&lt;p&gt;Put that in its own test function and wrap those attribute tests in a loop. If this pattern is repeated in different test files, put it in a custom test library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;test_fetching_by_id&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$tests&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;@_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$class&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$test&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@$tests&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$attribute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$expected&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;@$test&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;is&lt;/span&gt; &lt;span class="nv"&gt;$object&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;$attribute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
          &lt;span class="p"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$attribute&lt;/span&gt;&lt;span class="s2"&gt; works for &lt;/span&gt;&lt;span class="si"&gt;$class&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then you call it like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;@id_tests&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;tests&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;attr1&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$expected1&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;attr2&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$expected2&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;attr3&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$expected3&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;attr4&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$expected4&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;attr5&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$expected5&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;]},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$new_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s"&gt;tests&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;attr1&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$new_expected1&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;attr2&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$new_expected2&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;attr3&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$new_expected3&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;attr4&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$new_expected4&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;attr5&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$new_expected5&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;]},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$test&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;@id_tests&lt;/span&gt; &lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="nv"&gt;test_fetching_by_id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; 
       &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;
        &lt;span class="nv"&gt;$test&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;id&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="nv"&gt;$tests&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;test&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a cleanly refactored data-driven approach. By not repeating yourself, if you need to test new attributes, you can just add an extra line to the data structures and the code remains the same. Or, if you need to change the logic, you only have one spot in your code where this is done. Once a developer understands the &lt;code&gt;test_fetching_by_id()&lt;/code&gt; function, they can reuse this understanding in multiple places. Further, it makes it easier to find patterns in your code and any competent programmer is always on the lookout for patterns because those are signposts leading to cleaner designs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommendation&lt;/strong&gt;: Keep your test code as clean as your application code.&lt;/p&gt;




&lt;h3&gt;
  
  
  Testing fixtures are frequently not used (or poorly used)
&lt;/h3&gt;

&lt;p&gt;One difference between your application code and the test suite is in an application, we often have no idea what the data will be and we try to have a clean separation of data and code.&lt;/p&gt;

&lt;p&gt;In your test suite, we also want a clean separation of data and code (in my experience, this is very hit-or-miss), but we often &lt;em&gt;need&lt;/em&gt; to know the data we have. We set up data to run tests against to ensure that we can test various conditions. Can we give a customer a birthday discount if they were born on February 29th? Can a customer with an overdue library book check out another?  If our employee number is no longer in the database, is our code properly deleted, along with the backups and the git history erased? (kidding!)&lt;/p&gt;

&lt;p&gt;When we set up the data for these known conditions under which to test, we call the data a &lt;a href="http://en.wikipedia.org/wiki/Test_fixture" rel="noopener noreferrer"&gt;test fixture&lt;/a&gt;.  Test fixtures, when properly designed, allow us generate clean, understandable tests and make it easy to write tests for unusual conditions that may otherwise be hard to analyze.&lt;/p&gt;

&lt;p&gt;There are several common anti-patterns I see in fixtures.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hard to set up and use&lt;/li&gt;
&lt;li&gt;Adding them to the database and not rolling them back&lt;/li&gt;
&lt;li&gt;Loading all your test data at once with no granularity&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In reviewing various fixture modules on the CPAN and for clients I have worked with, much of the above routinely holds true. On top of that, documentation is often rather sparse or non-existent. Here's a (pseudo-code) example of an almost undocumented fixture system for one client I worked with and it exemplified common issues in this area.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="nv"&gt;load_fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;database&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sales&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;
    &lt;span class="s"&gt;client&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$client_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;datasets&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sx"&gt;qw/customers orders items order_items/&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;This had several problems, all of which could be easily corrected &lt;em&gt;as code&lt;/em&gt;, but they built a test suite around these problems and had backed themselves into a corner, making their test suite dependent on bad behavior.&lt;/p&gt;

&lt;p&gt;The business case is that my client had a product serving multiple customers and each customer would have multiple separate databases. In the above, client &lt;em&gt;$client_id&lt;/em&gt; connects to their sales database and we load several test datasets and run tests against them. However, loading of data was not done in a transaction, meaning that there was no isolation between different test cases in the same process. More than once I caught issues where running an individual test case would often fail because it depended on data loaded by a different test case, but it wasn't always clear which test cases were coupled with which.&lt;/p&gt;

&lt;p&gt;Another issue is that fixtures were not fine-tuned to address particular test cases. Instead, if you loaded "customers" or "referrals", you got &lt;em&gt;all&lt;/em&gt; of them in the database. Do you need a database with a single customer with a single order and only one order item on it to test that obscure bug that occurs when a client first uses your software? There really wasn't any clean way of doing that; data was loaded in an "all or nothing" context. Even if you violated the paradigm and tried to create fine-tuned fixtures, it was very hard to write them due to the obscure, undocumented format needed to craft the data files for them.&lt;/p&gt;

&lt;p&gt;Because transactions were not used and changes could not be rolled back, each &lt;code&gt;*.t&lt;/code&gt; file would rebuild its own test database, a very slow process. Further, due to lack of documentation about the fixtures, it was often difficult to figure out which combination of fixtures to load to test a given feature. Part of this is simply due to the complex nature of the business rules, but the core issues stemmed from a poor understanding of fixtures. This client now has multiple large, slow test suites, spread across multiple repositories, all of which constantly tear down and set up databases and load large amounts of data. The test suites are both slow and fragile. The time and expense to fix this problem is considerable due to how long they've pushed forward with this substandard setup.&lt;/p&gt;

&lt;p&gt;What you generally want is the ability to easily create understandable fixtures which are loaded in a transaction, tests are run, and then changes are rolled back.  The fixtures need to be fine-grained so you can tune them for a particular test case.&lt;/p&gt;

&lt;p&gt;One attempt I've made to fix this situation is releasing &lt;a href="http://search.cpan.org/dist/DBIx-Class-EasyFixture/lib/DBIx/Class/EasyFixture.pm" rel="noopener noreferrer"&gt;DBIx::Class::EasyFixture&lt;/a&gt;, along with &lt;a href="http://search.cpan.org/dist/DBIx-Class-EasyFixture/lib/DBIx/Class/EasyFixture/Tutorial.pm" rel="noopener noreferrer"&gt;a tutorial&lt;/a&gt;.  It does rely on &lt;code&gt;DBIx::Class&lt;/code&gt;, the most popular ORM for Perl. This will likely make it unsuitable for some use cases.&lt;/p&gt;

&lt;p&gt;Using fixtures is now very simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$fixtures&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;DBIx::Class::&lt;/span&gt;&lt;span class="nv"&gt;EasyFixture&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$schema&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$fixtures&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;load&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;customer_with_order_without_items&lt;/span&gt;&lt;span class="p"&gt;');&lt;/span&gt;

&lt;span class="c1"&gt;# run your tests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the customer's code, we could satisfy the different database requirements by passing in different schemas. Other (well-documented) solutions, particularly those which are pure &lt;code&gt;DBI&lt;/code&gt; based are welcome in this area.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommendation&lt;/strong&gt;: Use fine-grained, well-documented fixtures which are easy to create and easy to clean up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code coverage is poorly understood
&lt;/h3&gt;

&lt;p&gt;Consider the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;float recip(float number) {
    return 1.0 / number;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And a sample test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;assert recip(2.0) returns .5;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Congratulations! You now have 100% code coverage of that function.&lt;/p&gt;

&lt;p&gt;For a statically typed language, I'm probably going to be moderately comfortable with that test. Alas, for dynamically typed languages we're fooling ourselves. An equivalent function in Perl will pass that test if we use &lt;code&gt;recip("2 apples")&lt;/code&gt; as the argument. And what happens if we pass a file handle? And would a Unicode number work? What happens if we pass no arguments?  Perl is powerful and lets us write code quickly, but there's a price: it expects us to know what we're doing and passing unexpected kinds of data is a very common source of errors, but one that 100% code coverage will never (no pun intended) uncover. This can lead to false confidence.&lt;/p&gt;

&lt;p&gt;To work around false confidence in your code, always assume that you write applications to create things and you write tests to destroy them. Testing is, and should be, an act of violence. If you're not breaking anything with your tests, you're probably doing it wrong.&lt;/p&gt;

&lt;p&gt;Or what if you have that code in a huge test suite, but it's dead code? We tend to blindly run code coverage over our entire test suite, never considering whether or not we're testing dead code. This is because we slop our unit, integration, API and other tests all together.&lt;/p&gt;

&lt;p&gt;Or consider the following test case:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;test_forum&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$test&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;shift&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$site&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$test&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;test_website&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$site&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$pass&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$site&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;get&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;/forum&lt;/span&gt;&lt;span class="p"&gt;');&lt;/span&gt;
    &lt;span class="nv"&gt;$site&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;follow_link&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="s"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Off Topic&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$site&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;post_ok&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="s"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;What is this?&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;
        &lt;span class="s"&gt;body&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;This is a test&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="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;We should be able to post to the forum&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;&lt;code&gt;Devel::Cover&lt;/code&gt; doesn't know which code is test code and which is not.  &lt;code&gt;Devel::Cover&lt;/code&gt; merely tells you if your application code was exercised in your tests. &lt;a href="http://search.cpan.org/dist/Devel-Cover/lib/Devel/Cover.pm#UNCOVERABLE_CRITERIA" rel="noopener noreferrer"&gt;You can annotate your code with "uncoverable" directives&lt;/a&gt; to tell &lt;code&gt;Devel::Cover&lt;/code&gt; to ignore the following code, but that potentially means sprinkling your code with annotations all over the place.&lt;/p&gt;

&lt;p&gt;There are multiple strategies to deal with this. One of the simplest is to merely run your code coverage tools over the public-facing portions of your code, such as web or API tests. If you find uncovered code, you either have code that is not fully tested (in the sense that you don't know if your API can really use that code) or, if you cannot write an API test to reach that code, investigate if it is dead code.&lt;/p&gt;

&lt;p&gt;You can do this by grouping your tests into subdirectories:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;t/
|--api/
|--integration/
`--unit/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Alternatively, if you use &lt;code&gt;Test::Class::Moose&lt;/code&gt;, you can tag your tests and only run coverage over tests including the tags you wish to test:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;My::Test::Class::Moose-&amp;gt;new({
  include_tags =&amp;gt; [qw/api/],
})-&amp;gt;runtests;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If you start tagging your tests by the subsystems they are testing, you can then start running code coverage on specific subsystems to determine which ones are poorly tested.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommendation&lt;/strong&gt;: Run coverage over public-facing code and on different subsystems to find poor coverage.&lt;/p&gt;




&lt;h3&gt;
  
  
  They take far too long to run
&lt;/h3&gt;

&lt;p&gt;The problem with long-running test suites is well known, but it's worth covering this again here. These are problems that others have discussed and that I have also personally experienced many times.&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/http%3A%2F%2Fi.imgur.com%2FJNfyxoo.png" 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/http%3A%2F%2Fi.imgur.com%2FJNfyxoo.png" alt="Perl's version of waiting for a compile"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;With apologies to &lt;a href="http://xkcd.com/303/" rel="noopener noreferrer"&gt;XKCD&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In the best case scenario for developers who always run that long-running test suite, expensive developer time is wasted while the test suite is running.  When they launch that hour-long (or more) test suite, they frequently take a break, talk to (read: interrupt) other developers, check their Facebook, or do any number of things which equate to "not writing software." Yes, some of those things involve meetings or research, but meetings don't conveniently schedule themselves when we run tests and for mature products (those which are more likely to have long-running test suites), there's often not that much research we really need to do.&lt;/p&gt;

&lt;p&gt;Here are some of the issues with long-running test suites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Expensive developer time is wasted while the test suite runs&lt;/li&gt;
&lt;li&gt;Developers often don't run the entire test suite&lt;/li&gt;
&lt;li&gt;Expensive code coverage is not generated as a result&lt;/li&gt;
&lt;li&gt;Code is fragile as a result&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I find particularly curious is that we accept this state of affairs. Even a back-of-the-envelope calculation can quickly show significant productivity benefits that will pay off in the long run by taking care of our test suite.  &lt;a href="http://www.slideshare.net/Ovid/turbo-charged-test-suites-presentation" rel="noopener noreferrer"&gt;I once reduced a BBC test suite's run time from one hour and twenty minutes down to twelve minutes&lt;/a&gt; (&lt;em&gt;Note: today I use a saner approach that results in similar or greater performance benefits&lt;/em&gt;).  We had six developers on that team. When the test suite took over an hour to run, they often didn't run the test suite. They would run tests on their section of code and push their code when they were comfortable with it. This led to other developers finding buggy code and wasting time trying to figure out how they had broken it when, in fact, someone else broke the code.&lt;/p&gt;

&lt;p&gt;But let's assume each developer was running the test suite at least once a day (I'm careful about testing and often ran mine twice a day). By cutting test suite run time by over an hour, we reclaimed a &lt;em&gt;full day&lt;/em&gt; of developer productivity every day! Even if it takes a developer a month to increase perfomance by that amount it pays for itself many times over very quickly.  Why would you not do this?  As a business owner, wouldn't you want your developers to save time on their test suite so they can create features faster for you?&lt;/p&gt;

&lt;p&gt;There are several reasons why this is difficult. Tasking a developer with a block of time to speed up a test suite means the developer is not creating user-visible features during that time. For larger test suites, it's often impossible to know in advance just how much time you can save or how long it will take you to reach your goal. In most companies I've worked with, the people who can make the decision to speed up the test suite are often not the people feeling the pain. Productivity and quality decrease slowly over time, leading to the &lt;a href="http://en.wikipedia.org/wiki/Boiling_frog" rel="noopener noreferrer"&gt;boiling frog problem&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What's worse: in order to speed up your test suite without affecting behavior, the test suite often has to be "fixed" (eliminating warnings, failures, and reducing duplication) to ensure that no behavior has been changed during the refactor.&lt;/p&gt;

&lt;p&gt;Finally, some developers simply don't have the background necessary to implement performance optimizations. While performance profiles such as Perl's &lt;a href="http://search.cpan.org/dist/Devel-NYTProf/lib/Devel/NYTProf.pm" rel="noopener noreferrer"&gt;Devel::NYTProf&lt;/a&gt; can easily point out problem areas in the code, it's not always clear how to overcome the discovered limitations.&lt;/p&gt;

&lt;p&gt;The single biggest factor in poor test suite performance for applications is frequently I/O. In particular, working with the database tends to be a bottleneck and there's only so much database tuning that can be done. After you've profiled your SQL and optimized it, several database-related optimizations which can be considered are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Using transactions to clean up your database rather than rebuilding the database&lt;/li&gt;
&lt;li&gt;Only connect to the database once per test suite (hard when you're using a separate process per test file)&lt;/li&gt;
&lt;li&gt;If you must rebuild the database, maintain a pool of test databases and assign them as needed, rebuilding used ones in the background&lt;/li&gt;
&lt;li&gt;Use smaller database fixtures instead of loading everything at once&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After you've done all you can to improve your database access, you may find that your test suite is "fast enough", but if you wish to go further, there are several steps you can take.&lt;/p&gt;




&lt;h4&gt;
  
  
  Use Test::Aggregate
&lt;/h4&gt;

&lt;p&gt;&lt;a href="http://search.cpan.org/dist/Test-Aggregate/" rel="noopener noreferrer"&gt;Test::Aggregate&lt;/a&gt; can often double the speed of your test suite (I've had it speed up test suites by around 65%). It does this by taking your separate &lt;code&gt;*.t&lt;/code&gt; files and runs them in a single process. Not all tests can be run this way (tests that munge global state without cleaning up are prime examples), but it's the easiest way to get a quick boost to test suite performance.&lt;/p&gt;




&lt;h4&gt;
  
  
  Aggressively search for and remove duplicated tests.
&lt;/h4&gt;

&lt;p&gt;For poorly organized test suites, developers sometimes make the mistake of putting tests for something in a new &lt;code&gt;*.t&lt;/code&gt; file or add them to a different &lt;code&gt;*.t&lt;/code&gt; file, even if related tests already exist. This strategy can be time-consuming and often does not result in quick wins.&lt;/p&gt;




&lt;h4&gt;
  
  
  Run Performance Profiling
&lt;/h4&gt;

&lt;p&gt;For one test suite, I found that we were using a pure Perl implementation of JSON. As the test suite used JSON extensively, switching to &lt;a href="http://search.cpan.org/dist/JSON-XS/XS.pm" rel="noopener noreferrer"&gt;JSON::XS&lt;/a&gt; gave us a nice performance boost. We may not have noticed that if we hadn't been profiling our code with &lt;code&gt;Devel::NYTProf&lt;/code&gt;.&lt;/p&gt;




&lt;h4&gt;
  
  
  Look for code with "global" effects
&lt;/h4&gt;

&lt;p&gt;On one test suite, I ensured that &lt;code&gt;Universal::isa&lt;/code&gt; and &lt;code&gt;Universal::can&lt;/code&gt; cannot be loaded. It was a quick fix and sped up the test suite by 2% (several small accumulations of improvements can add up quickly).&lt;/p&gt;




&lt;h4&gt;
  
  
  Inline "hot" functions.
&lt;/h4&gt;

&lt;p&gt;Consider the following code which runs in about 3.2 seconds on my computer:&lt;/p&gt;

&lt;p&gt;`&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env perl&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;no&lt;/span&gt; &lt;span class="nv"&gt;warnings&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recursion&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$j&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="nv"&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="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$j&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;factorial&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;shift&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;if&lt;/span&gt; &lt;span class="nv"&gt;$num&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&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="nv"&gt;$num&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$num&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
`&lt;/p&gt;

&lt;p&gt;By rewriting the recursive function as a loop, the code takes about .87 seconds:&lt;/p&gt;

&lt;p&gt;`&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;factorial&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$num&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;shift&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;if&lt;/span&gt; &lt;span class="nv"&gt;$num&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$num&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="nv"&gt;$num&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="nv"&gt;$num&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;&lt;br&gt;
`&lt;/p&gt;

&lt;p&gt;By inlining the calculation, the code completes in .69 seconds:&lt;/p&gt;

&lt;p&gt;`&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$i&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$j&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="nv"&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="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$j&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$y&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$y&lt;/span&gt; &lt;span class="o"&gt;*=&lt;/span&gt; &lt;span class="vg"&gt;$_&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="nv"&gt;$y&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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
`&lt;/p&gt;

&lt;p&gt;In other words, in our trivial example, the inlined behavior is roughly 20% faster than the iterative function and 80% faster than the recursive function.&lt;/p&gt;




&lt;h4&gt;
  
  
  Recompile your Perl
&lt;/h4&gt;

&lt;p&gt;You may wish to recompile your Perl to gain a performance improvement. Many Linux distributions ship with a threaded Perl by default. Depending on the version of Perl you ship with, you can gain performance improvements of up to 30% by recompiling without threads. Of course, if you use threads, you'll feel very stupid for doing this. However, if you don't make heavy use of threads, switching to a forking model for the threaded code may make the recompile worth it. Naturally, you'll need to heavily benchmark your code (preferably under production-like loads) to understand the trade-offs here.&lt;/p&gt;




&lt;h4&gt;
  
  
  Preload modules
&lt;/h4&gt;

&lt;p&gt;If your codebase makes heavy use of modules that are slow to load, such as &lt;code&gt;Moose&lt;/code&gt;, &lt;code&gt;Catalyst&lt;/code&gt;, &lt;code&gt;DBIx::Class&lt;/code&gt; and others, preloading them might help.  &lt;a href="http://search.cpan.org/~miyagawa/forkprove-v0.4.9/script/forkprove" rel="noopener noreferrer"&gt;forkprove&lt;/a&gt; is a utility written by Tatsuhiko Miyagawa that allows you to preload slow-loading modules and then forks off multiple processes to run your tests.  Using this tool, &lt;a href="http://blogs.perl.org/users/ovid/2013/12/merry-christmas-parallel-testing-with-testclassmoose-has-arrived.html" rel="noopener noreferrer"&gt;I reduced one sample test suite's run time from 12 minutes to about a minute&lt;/a&gt;.  Unfortunately, &lt;code&gt;forkprove&lt;/code&gt; doesn't allow schedules, a key component often needed for larger test suites. I'll explain that in the next section.&lt;/p&gt;




&lt;h4&gt;
  
  
  Parallel tests
&lt;/h4&gt;

&lt;p&gt;Running tests in parallel is tricky. Some tests simply &lt;em&gt;can't&lt;/em&gt; be run with other tests. Usually these are tests which alter global state in some manner that other processes will pick up, or might cause resource starvation of some kind.&lt;/p&gt;

&lt;p&gt;Or some tests &lt;em&gt;can&lt;/em&gt; be run in parallel with other tests, but if several tests are updating the same records in the database at the same time, locking behavior might slow down the tests considerably.&lt;/p&gt;

&lt;p&gt;Or maybe you're running 4 jobs, but all of your slowest tests are grouped in the same job: not good.&lt;/p&gt;

&lt;p&gt;To deal with this, you can create a schedule that assigns different tests to different jobs, based on a set of criteria, and then puts tests which cannot run in parallel in a single job that runs after the others have completed.&lt;/p&gt;

&lt;p&gt;You can use &lt;a href="http://search.cpan.org/dist/Test-Harness/lib/TAP/Parser/Scheduler.pm" rel="noopener noreferrer"&gt;TAP::Parser::Scheduler&lt;/a&gt; to create an effective parallel testing setup. You can use this with &lt;code&gt;TAP::Parser::Multiplexer&lt;/code&gt; to create your parallel tests. Unfortunately, as of this writing there's a bug in the Multiplexer whereby it uses &lt;code&gt;select&lt;/code&gt; in a loop to read the parser output. If one parser blocks, none of the other output is read. Further, the schedule must be created prior to loading your test code, meaning that if your tests would prefer a different schedule, you're out of luck. Also, &lt;code&gt;make test&lt;/code&gt; currently doesn't handle this well. There is work being done by David Golden to alleviate this problem.&lt;/p&gt;

&lt;p&gt;My preferred solution is to use &lt;a href="http://search.cpan.org/dist/Test-Class-Moose/" rel="noopener noreferrer"&gt;Test::Class::Moose&lt;/a&gt;. It has built-in parallel testing and writing schedules is very easy. Further, different test cases can simply use a &lt;code&gt;Tags(noparallel)&lt;/code&gt; attribute to ensure that they're run sequentially after the parallel tests.&lt;/p&gt;

&lt;p&gt;Aside from the regular benefits of &lt;code&gt;Test::Class::Moose&lt;/code&gt;, an interesting benefit of this module is that it loads all of your test and application code into a single process and &lt;em&gt;then&lt;/em&gt; forks off subprocesses. As a result, your code is loaded once and only once. Alternate strategies which try to fork before loading your code might still cause the code to be loaded multiple times.&lt;/p&gt;

&lt;p&gt;I have used this strategy to reduce a &lt;a href="http://blogs.perl.org/users/ovid/2013/12/merry-christmas-parallel-testing-with-testclassmoose-has-arrived.html" rel="noopener noreferrer"&gt;12 minute test suite to 30 seconds&lt;/a&gt;.&lt;/p&gt;




&lt;h4&gt;
  
  
  Distributed tests
&lt;/h4&gt;

&lt;p&gt;Though I haven't used this module, Alex Vandiver has written &lt;a href="http://search.cpan.org/dist/TAP-Harness-Remote/lib/TAP/Harness/Remote.pm" rel="noopener noreferrer"&gt;TAP::Harness::Remote&lt;/a&gt;.  This module allows you to rsync directory trees to multiple servers and run tests on those servers. Obviously, this requires multiple servers.&lt;/p&gt;

&lt;p&gt;If you want to roll your own version of this, I've also released &lt;a href="http://search.cpan.org/dist/TAP-Stream/" rel="noopener noreferrer"&gt;TAP::Stream&lt;/a&gt;, a module that allows you to take streams (the text, actually) of TAP from multiple sources and combine them into a single TAP document.&lt;/p&gt;




&lt;h4&gt;
  
  
  Devel::CoverX::Covered
&lt;/h4&gt;

&lt;p&gt;There is yet another interesting strategy: only run tests that exercise the code that you're changing. Johan Lindström wrote &lt;a href="http://search.cpan.org/dist/Devel-CoverX-Covered/" rel="noopener noreferrer"&gt;Devel::CoverX::Covered&lt;/a&gt;.  This module is used in conjunction with Paul Johnson's &lt;a href="http://search.cpan.org/dist/Devel-Cover/" rel="noopener noreferrer"&gt;Devel::Cover&lt;/a&gt; to identify all the places in your tests which cover a particular piece of code. In the past, I've written tools for vim to read this data and only run relevant tests. This is a generally useful approach, but there are a couple of pitfalls.&lt;/p&gt;

&lt;p&gt;First, if your test suite takes a long time to run, it will take much, much longer to run with &lt;code&gt;Devel::Cover&lt;/code&gt;. As a result, I recommend that this be used with a special nightly "cover build" and have the results synched back to the developers.&lt;/p&gt;

&lt;p&gt;Second, when changing code, it's easy to change which tests cover your code, leading to times when this technique won't cover your actual changes thoroughly. In practice, this hasn't been a problem for me, but I've not used it enough to say that with confidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommendation&lt;/strong&gt;: Don't settle for slow test suites. Pick a goal and work to achieving that goal (it's easy to keep optimizing for too long and start getting diminishing marginal returns).&lt;/p&gt;




&lt;h1&gt;
  
  
  &lt;a href="https://github.com/houseabsolute/test-class-moose" rel="noopener noreferrer"&gt;Test::Class::Moose&lt;/a&gt;
&lt;/h1&gt;

&lt;p&gt;If you start creating a large Web site, do you start writing a bunch of individual scripts, each designed to handle one URL and each handling their own database access and printing their output directly to STDOUT? Of course not. Today, professional developers reach for Sinatra, Seaside, Catalyst, Ruby on Rails or other Web frameworks. They take a bit more time to set up and configure, but we know they generally save more time in the long run. Why wouldn't you do that with your test suite?&lt;/p&gt;

&lt;p&gt;If you're using Perl, many of the problems listed in this document can be avoided by switching to &lt;code&gt;Test::Class::Moose&lt;/code&gt;. This is a testing framework I designed to make it very easy to test applications. Once you understand it, it's actually easy to use for testing libraries, but it really shines for application testing.&lt;/p&gt;

&lt;p&gt;Note that I now regret putting &lt;code&gt;Moose&lt;/code&gt; in the name. &lt;code&gt;Test::Class::Moose&lt;/code&gt; is a rewrite of &lt;code&gt;Test::Class&lt;/code&gt; using &lt;code&gt;Moose&lt;/code&gt;, but it's &lt;em&gt;not&lt;/em&gt; limited to testing &lt;code&gt;Moose&lt;/code&gt; applications. It uses &lt;code&gt;Moose&lt;/code&gt; because internally it relies on the &lt;code&gt;Moose&lt;/code&gt; meta-object protocol for introspection.&lt;/p&gt;

&lt;p&gt;Out of the box you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reporting&lt;/li&gt;
&lt;li&gt;Parallel tests (which optionally accept a custom schedule)&lt;/li&gt;
&lt;li&gt;Tagging tests (slice and dice your test suite!)&lt;/li&gt;
&lt;li&gt;Test inheritance (xUnit for the win!)&lt;/li&gt;
&lt;li&gt;Full Moose support&lt;/li&gt;
&lt;li&gt;Test control methods (startup, setup, teardown, shutdown)&lt;/li&gt;
&lt;li&gt;Extensibility&lt;/li&gt;
&lt;li&gt;All the testing functions and behavior from &lt;code&gt;Test::Most&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To learn about xUnit testing in Perl, you may wish to read a five-part tutorial I published at Modern Perl Books:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="http://www.modernperlbooks.com/mt/2009/03/organizing-test-suites-with-testclass.html" rel="noopener noreferrer"&gt;Organizing test suites with Test::Class&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.modernperlbooks.com/mt/2009/03/reusing-test-code-with-testclass.html" rel="noopener noreferrer"&gt;Reusing test code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.modernperlbooks.com/mt/2009/03/making-your-testing-life-easier.html" rel="noopener noreferrer"&gt;Making your testing life easier&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.modernperlbooks.com/mt/2009/03/using-test-control-methods-with-testclass.html" rel="noopener noreferrer"&gt;Using test control methods&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.modernperlbooks.com/mt/2009/03/working-with-testclass-test-suites.html" rel="noopener noreferrer"&gt;Working with Test::Class test suites&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That tutorial is slightly out of date (I wrote it in 2009), but it explains effective use of &lt;code&gt;Test::Class&lt;/code&gt; and some common anti-patterns when using it.&lt;/p&gt;




&lt;h1&gt;
  
  
  About The Author
&lt;/h1&gt;

&lt;p&gt;For those of you who may be reading this and are not familiar with me, I am Curtis "Ovid" Poe. I authored the test harness that ships with the Perl programming language. I wrote the well-reviewed book &lt;a href="http://www.amazon.com/Beginning-Perl-Curtis-Poe/dp/1118013840/ref=sr_1_1?s=books&amp;amp;ie=UTF8&amp;amp;qid=1395074590&amp;amp;sr=1-1&amp;amp;keywords=beginning+perl+curtis+poe&amp;amp;tag=overse-20" rel="noopener noreferrer"&gt;Beginning Perl&lt;/a&gt; and am one of the authors of &lt;a href="http://www.amazon.com/Perl-Hacks-Programming-Debugging-Surviving/dp/0596526741/" rel="noopener noreferrer"&gt;Perl Hacks&lt;/a&gt; (how's that for a redundant title?). I also sit on &lt;a href="http://www.perlfoundation.org/who_s_who" rel="noopener noreferrer"&gt;the Board of Directors of the Perl Foundation&lt;/a&gt; and am one of the people behind &lt;a href="http://www.allaroundtheworld.fr/" rel="noopener noreferrer"&gt;All Around The World&lt;/a&gt;, a company offering software development, consulting and training.&lt;/p&gt;

&lt;p&gt;If you'd like to hire me to fix your test suite or write software for you, drop me a line at &lt;a href="//mailto:ovid@allaroundtheworld.fr"&gt;ovid@allaroundtheworld.fr&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>perl</category>
    </item>
    <item>
      <title>Managing a Test Database</title>
      <dc:creator>Ovid</dc:creator>
      <pubDate>Mon, 22 Mar 2021 13:36:07 +0000</pubDate>
      <link>https://dev.to/ovid/managing-a-test-database-2oje</link>
      <guid>https://dev.to/ovid/managing-a-test-database-2oje</guid>
      <description>&lt;h1&gt;
  
  
  The Problem
&lt;/h1&gt;

&lt;p&gt;Test databases are very easy to get wrong. Very easy. Decades ago when I first learned testing, the team shared a test database. If you ran your test at the same time another developer, both of your test suites would fail! However, we were using a database where we had to pay for individual licenses, so we were limited in what we could do.&lt;/p&gt;

&lt;p&gt;Later, I worked for a company using MySQL and I created an elaborate system of triggers to track all database changes. This let me “fake” transactions by starting a test run, see what had changed last time, and automatically reverting those changes. It had the advantage that multiple database handles could see each other’s changes (hard to do for many databases if you have separate transactions). It had the disadvantage of everything else: it was fragile and slow.&lt;/p&gt;

&lt;p&gt;Later, I started using xUnit frameworks, &lt;a href="https://metacpan.org/pod/Test::Class::Moose"&gt;eventually writing a new one&lt;/a&gt; that’s popular for companies needing a large-scale testing solution. With this, it was easy for each test class to run in a separate transaction, cleaning itself up as it went. Using transactions provides great isolation, leverages what databases are already good at, and let’s you run many classes in parallel.&lt;/p&gt;

&lt;p&gt;But it can easily break embedded transaction logic. And you have to guarantee everything shares the same database handle, and you can’t really test the transactions in your code, and, and, and ...&lt;/p&gt;

&lt;p&gt;What finally drove me over the edge was writing some code for a client using the &lt;a href="https://github.com/mojolicious/minion"&gt;Minion job queue&lt;/a&gt;. The queue is solid, but it creates new database connections, thus ensuring that it can’t see anything in your database transactions. I figured out a (hackish) solution, but I was tired of hackish solutions.&lt;/p&gt;

&lt;p&gt;While I was researching the solution, Matt Trout was reminding me (again) why the “database transactions for tests” approach was broken. Just spawn off temporary test databases and use those, throwing them away when you’re done.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Client
&lt;/h1&gt;

&lt;p&gt;A company wanting to hire me gave me a technical test and there was a task to add a simple feature to a &lt;a href="http://www.catalystframework.org/"&gt;Catalyst web application&lt;/a&gt;. It was trivial.  They handed me a Vagrant file and after a quick &lt;code&gt;vagrant up&lt;/code&gt; and &lt;code&gt;vagrant ssh&lt;/code&gt;, I was ready to begin.  Then I looked at the test they had for the controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Test::&lt;/span&gt;&lt;span class="nv"&gt;More&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Catalyst::&lt;/span&gt;&lt;span class="nv"&gt;Test&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;

&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;/some_path&lt;/span&gt;&lt;span class="p"&gt;')&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;is_success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Request should succeed&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;done_testing&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The task involved a &lt;code&gt;POST&lt;/code&gt; to a URL. There was no test for the existing feature that I was adding to, but any test I wrote meant I’d be changing the state of the database. Run the code multiple times and I’d leave junk in the database.  There were various ways I could approach this, but I decided it was time to build a quick database on the fly, write to that, and then dispose of it after. The code for this was trivial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="nb"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;Test::&lt;/span&gt;&lt;span class="nv"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;File::&lt;/span&gt;&lt;span class="nv"&gt;Temp&lt;/span&gt; &lt;span class="sx"&gt;qw(tempfile)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;DBI&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;parent&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Exporter&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Client::&lt;/span&gt;&lt;span class="nv"&gt;Policy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;BEGIN&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nb"&gt;exists&lt;/span&gt; &lt;span class="nv"&gt;$INC&lt;/span&gt;&lt;span class="p"&gt;{'&lt;/span&gt;&lt;span class="s1"&gt;Client/Model/ClientDB.pm&lt;/span&gt;&lt;span class="p"&gt;'}&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;croak&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;You must load Test::DB before Client::Model::ClientDB&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Client::Model::&lt;/span&gt;&lt;span class="nv"&gt;ClientDB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;our&lt;/span&gt; &lt;span class="nv"&gt;@EXPORT_OK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;qw(test_dbh)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$connect_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Client::Model::&lt;/span&gt;&lt;span class="nv"&gt;ClientDB&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;connect_info&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$dsn&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$connect_info&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$connect_info&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$connect_info&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;# $$ is the process id (PID)&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$db_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sprintf&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test_db_%d_%d&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vg"&gt;$$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;$fh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$filename&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;tempfile&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$dbh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;DBI&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nv"&gt;$dsn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;RaiseError&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;AutoCommit&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; 
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nv"&gt;$dbh&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;CREATE DATABASE &lt;/span&gt;&lt;span class="si"&gt;$db_name&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
&lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;mysqldump -u &lt;/span&gt;&lt;span class="si"&gt;$user&lt;/span&gt;&lt;span class="s2"&gt; --password=&lt;/span&gt;&lt;span class="si"&gt;$password&lt;/span&gt;&lt;span class="s2"&gt; test_db &amp;gt; &lt;/span&gt;&lt;span class="si"&gt;$filename&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="ow"&gt;or&lt;/span&gt; &lt;span class="nv"&gt;croak&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;mysqldump failed: $?&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
&lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;mysql -u &lt;/span&gt;&lt;span class="si"&gt;$user&lt;/span&gt;&lt;span class="s2"&gt; --password=&lt;/span&gt;&lt;span class="si"&gt;$password&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;$db_name&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt; &lt;/span&gt;&lt;span class="si"&gt;$filename&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="ow"&gt;or&lt;/span&gt; &lt;span class="nv"&gt;croak&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;importing schema to mysql failed: $?&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;

&lt;span class="c1"&gt;# XXX We’re being naughty in this quick hack. We’re writing&lt;/span&gt;
&lt;span class="c1"&gt;# this back to the Model so that modules which use this connect &lt;/span&gt;
&lt;span class="c1"&gt;# to the correct database.&lt;/span&gt;
&lt;span class="nv"&gt;$connect_info&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;dsn&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="s2"&gt;dbi:mysql:&lt;/span&gt;&lt;span class="si"&gt;$db_name&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;

&lt;span class="c1"&gt;# This is just a quick hack to get tests working for this code.&lt;/span&gt;
&lt;span class="c1"&gt;# A catastrophic failure in the test means this might not get&lt;/span&gt;
&lt;span class="c1"&gt;# run and we have a junk test database lying around.&lt;/span&gt;
&lt;span class="c1"&gt;# Obviously we want something far more robust&lt;/span&gt;

&lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$dbh&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;DROP DATABASE &lt;/span&gt;&lt;span class="si"&gt;$db_name&lt;/span&gt;&lt;span class="p"&gt;")&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;test_dbh&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$dbh&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;The above is very naughty in many ways, but the client hinted that how fast I returned the test might be a factor (or maybe they didn’t and I misread the signals). They also made it clear they were looking at how I approached problems, not whether or not the code was perfect. Thus, I thought I was on safe territory.  And it meant I could do this in my test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;warnings&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Test::&lt;/span&gt;&lt;span class="nv"&gt;More&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;lib&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;t/lib&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Test::&lt;/span&gt;&lt;span class="nv"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Catalyst::&lt;/span&gt;&lt;span class="nv"&gt;Test&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;

&lt;span class="nv"&gt;ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nv"&gt;request&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;/some_path&lt;/span&gt;&lt;span class="p"&gt;')&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;is_success&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Request should succeed&lt;/span&gt;&lt;span class="p"&gt;'&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;# anything I do here is against a temporary test database&lt;/span&gt;
&lt;span class="c1"&gt;# and will be discarded when the test finishes&lt;/span&gt;

&lt;span class="nv"&gt;done_testing&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Test::DB&lt;/code&gt; code was quick and easy to write and made it trivial for me to safely write tests. I was pleased.&lt;/p&gt;

&lt;h1&gt;
  
  
  What’s Wrong With Test::DB?
&lt;/h1&gt;

&lt;p&gt;For a junior developer, &lt;code&gt;Test::DB&lt;/code&gt; might look awesome. For an experienced developer, it’s terrible. So what would I do to make it closer to production ready? &lt;/p&gt;

&lt;p&gt;Here are just a few of the things I would consider.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stronger Data Validation
&lt;/h2&gt;

&lt;p&gt;First, let’s look at our connection information:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$connect_info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Client::Model::&lt;/span&gt;&lt;span class="nv"&gt;ClientDB&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;connect_info&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$dsn&lt;/span&gt;          &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$connect_info&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;dsn&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;         &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$connect_info&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$connect_info&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;password&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above relied on how Catalyst often sets up its DBIx::Class (a Perl ORM) model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="nb"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;Client::Model::&lt;/span&gt;&lt;span class="nv"&gt;ClientDB&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nv"&gt;base&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Catalyst::Model::DBIC::Schema&lt;/span&gt;&lt;span class="p"&gt;';&lt;/span&gt;

&lt;span class="nv"&gt;__PACKAGE__&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;schema_class&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Client::Schema::ClientDB&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;
    &lt;span class="s"&gt;connect_info&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;dsn&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dbi:mysql:test_db&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;
        &lt;span class="s"&gt;user&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;
        &lt;span class="s"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rootpass&lt;/span&gt;&lt;span class="p"&gt;',&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you load that class, you get a &lt;code&gt;config&lt;/code&gt; class method which can tell you how that class is configured. However, there’s no guarantee in the &lt;code&gt;Test::DB&lt;/code&gt; side that the data is structured the way that I expect. Thus, I need to validate that data and throw an exception immediately if something has changed. &lt;/p&gt;

&lt;p&gt;And how do we create our test database?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="nv"&gt;$dbh&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;CREATE DATABASE &lt;/span&gt;&lt;span class="si"&gt;$db_name&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
&lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;mysqldump -u &lt;/span&gt;&lt;span class="si"&gt;$user&lt;/span&gt;&lt;span class="s2"&gt; --password=&lt;/span&gt;&lt;span class="si"&gt;$password&lt;/span&gt;&lt;span class="s2"&gt; test_db &amp;gt; &lt;/span&gt;&lt;span class="si"&gt;$filename&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="ow"&gt;or&lt;/span&gt; &lt;span class="nv"&gt;croak&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;mysqldump failed: $?&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
&lt;span class="nb"&gt;system&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;mysql -u &lt;/span&gt;&lt;span class="si"&gt;$user&lt;/span&gt;&lt;span class="s2"&gt; --password=&lt;/span&gt;&lt;span class="si"&gt;$password&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;$db_name&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt; &lt;/span&gt;&lt;span class="si"&gt;$filename&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="ow"&gt;or&lt;/span&gt; &lt;span class="nv"&gt;croak&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;importing schema to mysql failed: $?&lt;/span&gt;&lt;span class="p"&gt;");&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;CREATE DATABASE&lt;/code&gt; command is fast, so I’m not worried about that.  And the test had a single table with very little data, so this was quick. But for &lt;a href="https://taustation.space/"&gt;Tau Station&lt;/a&gt;, we have a couple of hundred tables and tons of data. This would be slow. For any reasonably mature system, dumping the database each time would be a bad idea. There are also ways you could easily avoid dumping it multiple times, but that hits the next problem: adding that data to your new test database. That would need to be done for each test and &lt;em&gt;that&lt;/em&gt; is not something you can trivially speed up. &lt;/p&gt;

&lt;p&gt;For a more robust system, I’d probably create a local database service that would simply build a set number of test databases and have them waiting. The test would request the next test database, the service would register that the database had been taken, and create a new test database in the background while your test runs. The service would also probably clean up old test databases based on whatever policies you think are appropriate. &lt;/p&gt;

&lt;h1&gt;
  
  
  No Action At A Distance
&lt;/h1&gt;

&lt;p&gt;This line is terrible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="nv"&gt;$connect_info&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;dsn&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="s2"&gt;dbi:mysql:&lt;/span&gt;&lt;span class="si"&gt;$db_name&lt;/span&gt;&lt;span class="p"&gt;";&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reason that works is because the &lt;code&gt;config&lt;/code&gt; data in &lt;code&gt;Client::Model::ClientDB&lt;/code&gt; is global &lt;em&gt;and&lt;/em&gt; mutable and &lt;code&gt;$connect_info&lt;/code&gt; is merely a reference to that data. Instead, if I have a "database service" that tells the code which database it can use, then &lt;code&gt;Test::DB&lt;/code&gt; can call that service, and so can &lt;code&gt;Client::Model::ClientDB&lt;/code&gt;. Everything relies on a single source of truth instead of hacking global variables and hoping you don’t mess up. &lt;/p&gt;

&lt;h1&gt;
  
  
  Don’t Drop The Test Database
&lt;/h1&gt;

&lt;p&gt;If there is one thing which I hate about many testing systems, it’s a watching a test horribly fail, but the database is cleaned up (or dropped) and I can’t see the actual data after the test is done. What I often have to do is fire up the debugger and run the code up to the test failure and grab a database handle and try to inspect the data that way. It’s a mess. &lt;/p&gt;

&lt;p&gt;Here, we can fix that by simply dropping this line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;$dbh&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;("&lt;/span&gt;&lt;span class="s2"&gt;DROP DATABASE &lt;/span&gt;&lt;span class="si"&gt;$db_name&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;At the beginning and end of every test run, we can &lt;code&gt;diag&lt;/code&gt; the test database name and if I need to see if there’s an issue in the database, I can still use it. Our database service would have code to drop the database on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The next day&lt;/li&gt;
&lt;li&gt;The next test run&lt;/li&gt;
&lt;li&gt;After exceeding a threshold of databases&lt;/li&gt;
&lt;li&gt;... or whatever else you need&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In short, keep the valuable data around for debugging.&lt;/p&gt;

&lt;h1&gt;
  
  
  Rapid Database Development
&lt;/h1&gt;

&lt;p&gt;The database service solution would also have to tie into your database change management strategy. I heavily use &lt;a href="https://sqitch.org/"&gt;sqitch&lt;/a&gt; to manage database changes and I’ve written a lot of code to support odd edge cases. It wouldn’t be hard to write code to let the database service see if it’s up to date with your local version of &lt;code&gt;sqitch&lt;/code&gt;. Whatever database change management strategy you use, it needs to be discoverable to properly automate the database service. &lt;/p&gt;

&lt;p&gt;Of course, you think, that’s obvious. Yet you’d be shocked how many times I’ve worked with clients whose database change management strategy involves listing a bunch of SQL files and checking their &lt;code&gt;mtime&lt;/code&gt; to see which ones need to be applied to your database. Yikes!&lt;/p&gt;

&lt;h1&gt;
  
  
  Faster Tests
&lt;/h1&gt;

&lt;p&gt;If this is done well, your tests should also be faster. You won’t have the overhead of transactions beyond what your code already has. Plus, you can avoid issues like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight perl"&gt;&lt;code&gt;&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;test_basic_combat_attack_behavior&lt;/span&gt; &lt;span class="p"&gt;($test,$) {&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$ovid&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$test&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;load_fixture&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;character.ovid&lt;/span&gt;&lt;span class="p"&gt;');&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$winston&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$test&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;load_fixture&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;character.winston&lt;/span&gt;&lt;span class="p"&gt;');&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="nv"&gt;$station&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$test&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;load_fixture&lt;/span&gt;&lt;span class="p"&gt;('&lt;/span&gt;&lt;span class="s1"&gt;station.tau-station&lt;/span&gt;&lt;span class="p"&gt;');&lt;/span&gt;

    &lt;span class="nv"&gt;$test&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;move_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$station&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$ovid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$winston&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nv"&gt;$ovid&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nv"&gt;attack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$winston&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;We should not be able to attack someone on the home station.&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above, we’re loading some fixtures. Sometimes those fixtures are very complicated and loading them takes time. For one client, when I would run &lt;code&gt;$test-&amp;gt;load_all_fixtures('connection');&lt;/code&gt;, that would add an extra couple of seconds to every test which needed to do that.&lt;/p&gt;

&lt;p&gt;Instead, pre-built test databases can have the test fixtures already loaded. Further, having a pre-populated database helps your code deal with something closer to a real-world problem instead of dealing with an empty database and not catching corner cases that might cause.&lt;/p&gt;

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

&lt;p&gt;By using a database service which merely hands you a temporary test database, you don’t have to worry about leaving the database a mess, managing transactions in tests, or having nasty hacks in your tests to workaround these issues. Most importantly, you’re not changing the behavior of your code. You just use the database like normal. It might be a little bit more work up front to create that database, but it’s worth the effort.&lt;/p&gt;

&lt;p&gt;I really do want to get around to creating a proper database tool like this some day. Today is not that day. But I was delighted how even my quick hack, written in just a couple of minutes, made it so much easier to test my code. I should have done this ages ago.&lt;/p&gt;

</description>
      <category>database</category>
      <category>perl</category>
      <category>testing</category>
    </item>
    <item>
      <title>Three-Number Project Management</title>
      <dc:creator>Ovid</dc:creator>
      <pubDate>Fri, 19 Mar 2021 13:59:40 +0000</pubDate>
      <link>https://dev.to/ovid/three-number-project-management-1bnn</link>
      <guid>https://dev.to/ovid/three-number-project-management-1bnn</guid>
      <description>&lt;h1&gt;
  
  
  Congrats on Your New Role!
&lt;/h1&gt;

&lt;p&gt;So there you are, a brand new project manager (PM), or product owner (PO) and ... wait a minute. What the heck's the difference? In many agile projects, there is no project manager and the PM's responsibility is distributed across the various members of the team: product owner, developers, ScrumMaster (Scrum), Coach (XP), and so on. Thus, the definition of a PM is sometimes muddied and in my experience, varies wildly across organizations.&lt;/p&gt;

&lt;p&gt;But because the waters are often muddied here, I‘m going to handwave this entire issue and hope you don‘t notice. Instead, we‘ll focus on an age-old problem in the PM/PO space: what should we do next?&lt;/p&gt;

&lt;p&gt;So ...&lt;/p&gt;

&lt;h1&gt;
  
  
  What Should We Do Next?
&lt;/h1&gt;

&lt;p&gt;As a fledgling team lead, PO, PM, or whatever title your company‘s uses for person responsibile for getting stuff done, figuring out what to work on next is a daunting task. It involves having an in-depth knowledge of the project, the product, the business, and the team. And when you're juggling 27 things at once, it can be maddening to have a bunch of baby bird cheeping "what do I do next?", demanding you vomit up user stories for them to work on.&lt;/p&gt;

&lt;p&gt;You might think that &lt;a href="https://www.agilealliance.org/glossary/backlog-grooming/" rel="noopener noreferrer"&gt;backlog grooming/refinement&lt;/a&gt; is all you need, but that still doesn't tell you, out of the 374 tasks that need to be done, which should be done next. Fortunately, there's a very simple way to do this, and that's to build a business case for each task, using just three numbers.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Three Number Business Case
&lt;/h1&gt;

&lt;p&gt;A business case is really nothing more than making an argument that a thing should or should not be done, but using numbers to back it up. If you want to learn more, I have a "rescuing a legacy codebase" talk that covers this.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/4c8ixLgpxwo"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;But what numbers do we use? For this case, try to pick the three most important numbers that represent value in your tasks. At one point, when I had the project management role in developing &lt;a href="https://taustation.space/" rel="noopener noreferrer"&gt;Tau Station&lt;/a&gt;, a free-to-play (F2P) narrative sci-fi adventure, the complexity of the role was overwhelming, but each task's "three most important numbers" were clear: complexity, usability, monetization.&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%2F7lsiya4kzyrclj1cm5nv.png" 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%2F7lsiya4kzyrclj1cm5nv.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;complexity&lt;/strong&gt; of a task is simply how many "story points" (or whatever you call them) that team members have assigned to a task. I recommend using &lt;a href="http://www.velocitycounts.com/2013/05/why-do-high-performing-scrum-teams-tend-to-use-story-point-estimation/" rel="noopener noreferrer"&gt;Fibonacci numbers for story point estimates&lt;/a&gt;, especially for what I'm about to show you.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;usability&lt;/strong&gt; of a task was how useful it was to the customers. Again, you want to use Fibonacci numbers here. You need a feel for what this number is because, in reality, you cannot get a perfect estimate. However, it's clear that a feature which lets customers download reports is more valuable to them than a feature which slightly adjusts the color of a sidebar, so the former might get the number "13" while the latter might get the number "1".&lt;/p&gt;

&lt;p&gt;In the case of Tau Station, everyone is allowed to play the game for free, but as with many F2P games, there are monetization options in the game and things which increase the likelihood of &lt;strong&gt;monetization&lt;/strong&gt; gets a higher number. A "Happy Hour" feature, where the players get extra rewards when they buy stuff gets a higher monetization number than a feature to make forum posts "sticky." Again, you're using Fibonacci numbers here.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Important:&lt;/strong&gt; the three things you choose should reflect your business concerns and may very well be vastly different business requirements. You may choose "security" or "legal compliance" or something else entirely. Tau Station included these as part of the "usability" number. Think hard about these numbers because you don't want to get them wrong.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Once you have your three numbers, what next? Well, you need to assign weights to them. This will largely be a process of trial and error until it feels right. In our case, let's say that both monetization and complexity get a weight of 40 and usability gets a 20. Then you play around with formulae until you get a final score, with higher-scoring tasks having a greater priority. Here's the formula I used for Tau Station, though you will need to adjust this for your own needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pros  = monetization * 40 + usability * 20
cons  = complexity * 40
scale = 10
score = ( ( pros – cons ) + 500 ) / scale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's say a task has a monetization of 1, and a usability of 5, and a complexity of 7. How does that compare with a take of a monetization of 3, a usability of 3, but a complexity of 13? The first task might earn us less money and be less useful, but it's easy to implement. Decisions, decisions ...&lt;/p&gt;

&lt;p&gt;Well, monetization and usability are both "pros" (benefits), and complexity is a "con" (drawback), so we have task 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pros  = 1 * 40 + 5 * 20
cons  = 7 * 40
scale = 10
score = ( ( 140 – 280 ) + 500 ) / 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And task 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pros  = 3 * 40 + 3 * 40
cons  = 13 * 40
scale = 10
score = ( ( 240 – 520 ) + 500 ) / 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Task 1 has a final score of 36, while task 2 has a final score of 22. Thus, we do task 1 first.&lt;/p&gt;

&lt;p&gt;Once you learn to plug those three numbers into a spreadsheet, you can then sort the tasks by score and prioritize tasks at the top of the list.&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%2F22l0yk8xcx43hz39k6er.png" 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%2F22l0yk8xcx43hz39k6er.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Real World Usage
&lt;/h1&gt;

&lt;p&gt;When I was doing this, I found it made my work much easier and the team appreciated it because it was clear direction. Plus, since the team provides the complexity estimates, they know that they have a real impact on project direction, something that teams usually appreciate.&lt;/p&gt;

&lt;p&gt;I used an Excel spreadsheet, but also a small program that I wrote which fetched data from github and &lt;a href="https://www.zenhub.com/" rel="noopener noreferrer"&gt;ZenHub&lt;/a&gt; to automate the creation of my spreadsheet. Every month I'd repeat the process, fill in missing numbers, sort the rows and share it with the team via a Google Doc. It worked very well and figuring out the project tasks for the month was no longer a stressful affair..&lt;/p&gt;

&lt;p&gt;Note that sometimes you will find tasks which don't seem to be scored correctly with this technique. When that happens, investigate whether or not your three numbers seem reasonable. Maybe your weights are wrong, or your formula is off.&lt;/p&gt;

&lt;p&gt;Or, as sometimes happens, there are external factors. A task may be complex, offer low usability, and no monetization potential, but another team is demanding that XML feed now and hey, you have to play nice with others. When odd events like that happen, use your judgment instead of your spreadsheet.&lt;/p&gt;

&lt;p&gt;At the end of the day, this technique, like all others, isn't perfect. When used correctly, however, it's a great tool for building value and focusing on your real needs. Further, you prioritize greater value and, when asked to justify your decisions, you have numbers to back them up. Even if they're not perfect, quibbling over numbers and formulae instead of hunches is a much safer career move.&lt;/p&gt;

</description>
      <category>scrum</category>
      <category>management</category>
      <category>agile</category>
      <category>planning</category>
    </item>
    <item>
      <title>Fixing a 40-year-old Software Bug</title>
      <dc:creator>Ovid</dc:creator>
      <pubDate>Tue, 16 Mar 2021 09:44:59 +0000</pubDate>
      <link>https://dev.to/ovid/fixing-a-40-year-old-software-bug-jmm</link>
      <guid>https://dev.to/ovid/fixing-a-40-year-old-software-bug-jmm</guid>
      <description>&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I have exact numbers for this because I originally wrote this on the day I found the bug.&lt;/p&gt;




&lt;p&gt;I was working on an ETL system designed to reduce the cost of Phase III clinical trials. In doing so, I was reading some data and I processed 36,916 potential dates. Two of those 36,916 failed to validate. I wasn't concerned as these dates came from clients at big pharmaceutical companies handing us spreadsheets the often had only a vague match with our specification. When you work with clients much larger than you, you often just grin and bear it and die a little inside each day (which reminds me, I need to write up my hell with Yahoo's now-extinct IDIF format some day).&lt;/p&gt;

&lt;p&gt;On that day, however, the pharmaceuticals were blameless. When I inspected the raw data, the failed dates were January 1&lt;sup&gt;st&lt;/sup&gt;, 2011 and January 1&lt;sup&gt;st&lt;/sup&gt;, 2007. I knew those dates. This wasn't sloppy data from a client. I had a bug in software I had just written, but this bug was first released in 1983.&lt;/p&gt;

&lt;p&gt;For anyone who doesn't understand the software ecosystem, this may sound mystifying, but it makes sense. Because of a decision taken a long time ago to make another company money, my client lost money in paying me to fix a bug that one company accidentally introduced and another company deliberately introduced. But to explain it I need to talk about a third company that introduced a &lt;em&gt;feature&lt;/em&gt; that eventually became a bug, and a few other historical tidbits that nonetheless contributed to the obscure bug I fixed that day.&lt;/p&gt;

&lt;h1&gt;
  
  
  History
&lt;/h1&gt;

&lt;h2&gt;
  
  
  System Clocks
&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%2Fy19kul1s9vu4y2ead1c2.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%2Fy19kul1s9vu4y2ead1c2.jpg" title="Much of my early programming was on these bad boys." alt="Apple II computer with external modem"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the good ol' days, Apple computers would sometimes spontaneously reset their date to January 1&lt;sup&gt;st&lt;/sup&gt;, 1904. The reason for this is simple. Back then, &lt;a href="http://en.wikipedia.org/wiki/Apple_II_system_clocks" rel="noopener noreferrer"&gt;Apple computers used battery-powered "system clocks"&lt;/a&gt; to keep track of the date and time. What happened when the battery ran out? Apple computers tracked their dates as &lt;a href="http://lowendmac.com/tech/1-1-2k.shtml" rel="noopener noreferrer"&gt;the number of seconds since the epoch&lt;/a&gt;.  In this sense, an &lt;a href="http://en.wikipedia.org/wiki/Epoch_(reference_date)" rel="noopener noreferrer"&gt;epoch&lt;/a&gt; is merely a reference date from which we start counting and for Macintosh computers, that epoch was January 1&lt;sup&gt;st&lt;/sup&gt;, 1904 and when the system clock battery died, that was your new date and it caused a curious problem.&lt;/p&gt;

&lt;p&gt;Back then, Apple used 32 bits (ones and zeros) to store the number of seconds from their start date. One bit can hold one of  two values, 0 or 1. Two bits can hold one of four values, 00, 01, 10, 11. Three bits can hold one of eight values, 000, 001, 010, 011, 100, 101, 110, 111, and so on. How much can 32 bits hold? 32 bits can hold one of 2&lt;sup&gt;32&lt;/sup&gt;, or 4,294,967,296, values. 2&lt;sup&gt;32&lt;/sup&gt; seconds is just over 136 years, which is why &lt;a href="http://lowendmac.com/lab/04/0115.html" rel="noopener noreferrer"&gt;older Macs couldn't handle dates after 2040&lt;/a&gt; and if your system clock battery died, your date would reset to 0 seconds after the epoch and you'd have to keep manually resetting the date every time you turned on your computer (or until you bought a new battery for your system clock).&lt;/p&gt;

&lt;p&gt;However, the Apple solution of storing dates as the number of seconds &lt;em&gt;after&lt;/em&gt; the epoch means we couldn't handle dates &lt;em&gt;before&lt;/em&gt; the epoch and that had far-reaching implications, as we'll see. This was a feature, not a bug, that Apple introduced. It meant, amongst other things, that the Macintosh operating system was immune to the Y2K bug. (Ironically, many Mac apps weren't immune because they would introduce their own date system to work around the Mac limitations.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Lotus 1-2-3
&lt;/h2&gt;

&lt;p&gt;Moving along, we have &lt;a href="http://en.wikipedia.org/wiki/Lotus_1-2-3" rel="noopener noreferrer"&gt;Lotus 1-2-3&lt;/a&gt;, IBM's "killer app" that helped to launch the PC revolution, though it was &lt;a href="http://en.wikipedia.org/wiki/VisiCalc" rel="noopener noreferrer"&gt;VisiCalc&lt;/a&gt; on the Apple that really launched the personal computer. It's fair to say that if 1-2-3 hadn't come along, PCs would likely have not taken off as quickly as they had and computer technology would have turned out considerably differently. However, Lotus 1-2-3 incorrectly reported 1900 as a leap year. (In literary terms, that sentence is what we call "foreshadowing")&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%2Fd19k4jxmp4x08emjsv6t.png" 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%2Fd19k4jxmp4x08emjsv6t.png" title="The Glory Days of Lotus 1-2-3" alt="Text of a Lotus 1-2-3 spreadsheet"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When Microsoft released Multiplan, their first spreadsheet program, &lt;a href="http://www.memecentral.com/mylife.htm" rel="noopener noreferrer"&gt;it didn't have much market penetration&lt;/a&gt;. So when they conceived of Excel, they decided to not only copy 1-2-3's row/column naming scheme, they made it bug-for-bug compatible, including treating 1900 as a leap year, &lt;a href="http://support.microsoft.com/kb/214326" rel="noopener noreferrer"&gt;a problem that remains to this day&lt;/a&gt;. This wasn't to be sneaky; they needed Excel to be able to import Lotus 1-2-3 spreadsheets. So for 1-2-3, this was a bug, but for Excel, it was a feature, even if meant sometimes getting dates wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Epochs
&lt;/h2&gt;

&lt;p&gt;When Microsoft wanted to release Excel for Apple's Macintosh computers, they had a problem. As mentioned, Macintosh didn't recognize dates prior to January 1&lt;sup&gt;st&lt;/sup&gt;, 1904. However, Excel used January 1&lt;sup&gt;st&lt;/sup&gt;, 1900 as its epoch. So Excel was modified to recognize what the epoch was and internally stored dates relative to these respective epochs. &lt;a href="http://support.microsoft.com/kb/214330" rel="noopener noreferrer"&gt;This Microsoft support article explains the problem fairly clearly&lt;/a&gt;. And that leads to &lt;em&gt;my&lt;/em&gt; bug.&lt;/p&gt;

&lt;h1&gt;
  
  
  My Bug
&lt;/h1&gt;

&lt;p&gt;My client received spreadsheets from many customers. Those spreadsheets may have been produced on Windows, but they may have been produced on a Mac. As a result, the "epoch" date for the spreadsheets might be January 1&lt;sup&gt;st&lt;/sup&gt;, 1900 or January 1&lt;sup&gt;st&lt;/sup&gt;, 1904. How do you know which one? Well, the &lt;a href="http://msdn.microsoft.com/en-us/library/dd906747(v=office.12).aspx" rel="noopener noreferrer"&gt;Excel file format exposes this information&lt;/a&gt;, but the parser I was using did not and it expected you to know whether you have a 1900 or 1904-based spreadsheet. I suppose I could have spent a lot of time trying to figure out how to read the binary format of Excel and sent a patch to the maintainer of the parser, but I had many other things to do for my client and so I quickly wrote a heuristic to determine whether or not a given spreadsheet was 1900 or 1904. It was pretty simple.&lt;/p&gt;

&lt;p&gt;In Excel, you may have a date of July 5, 1998, but it might be formatted as "07-05-98" (the useless US system), "Jul 5, 98", "July 5, 1998", "5-Jul-98" or any of a number of other useless formats (ironically, the one format my version of Excel didn't offer was the standard ISO 8601 format). Internally, however, the &lt;em&gt;unformatted&lt;/em&gt; value is either "35981", for the 1900 date system, or "34519", for the 1904 system (these numbers represent the number of days after the epoch). So I used a robust date parser to extract the year from the formatted date, and then an Excel date parser to extract the year from the unformatted value. If they're four years apart, I know I'm using the 1904 date system.&lt;/p&gt;

&lt;p&gt;So why didn't I simply use the formatted date? Because July 5, 1998 &lt;em&gt;might&lt;/em&gt; be formatted as "July, 98", losing me the day of the month. We get our spreadsheets from so many companies and they create them in so many different ways that they expect &lt;em&gt;us&lt;/em&gt; (meaning me, in this case) to figure it out. After all, Excel gets it right, I should, too!&lt;/p&gt;

&lt;p&gt;That's when 39082 kicked me in the tail. Remember how Lotus 1-2-3 considered 1900 a leap year and how that was faithfully copied to Excel? Because it adds an extra day to 1900, many date calculation functions relying on this can easily be off by a day. That means that 39082 &lt;em&gt;might&lt;/em&gt; be January 1&lt;sup&gt;st&lt;/sup&gt;, 2011 (on Macs), or it &lt;em&gt;might&lt;/em&gt; be December 31&lt;sup&gt;st&lt;/sup&gt;, 2006 (on Windows). If my "year parser" extracts 2011 from the formatted value, well, that's great. But since the Excel parser doesn't know whether it's a 1900 or 1904 date system, it defaults to the common 1900 date system, returns 2006 as the year, my software saw that the years were &lt;em&gt;five&lt;/em&gt; years apart, assumed an error, logged it, and returned the unformatted value.&lt;/p&gt;

&lt;p&gt;To work around this, I wrote the following (pseudo-code):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;difference = formatted_year - parsed_year
if 0 == difference:
    assume 1900 date system
if 4 == difference:
    assume 1904 date system
if 5 == difference &amp;amp;&amp;amp; 12 == month &amp;amp;&amp;amp; 31 == day:
    assume 1904 date system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And all 36,916 dates parsed correctly.&lt;/p&gt;




&lt;p&gt;As an aside, &lt;a href="http://www.joelonsoftware.com/items/2006/06/16.html" rel="noopener noreferrer"&gt;according to an anecdote from Joel Spolsky&lt;/a&gt;, the Lotus 1-2-3 "bug" may have been a deliberate attempt to simplify the Lotus software.&lt;/p&gt;

</description>
      <category>bug</category>
      <category>excel</category>
      <category>history</category>
      <category>etl</category>
    </item>
  </channel>
</rss>
