One plus one

12.08.2019

A little funny quiz I've came up with. Guess programming language:

  1. '1' + 1 = 11
  2. '1' + 1 = 2
  3. '1' + 1 = 50
  4. '1' + 1 = TypeError

Answers below More...

One of common tasks in software development is building a client library for some service API. Over the years I've come up with a way of structuring these libraries, which has served me well. The evolution of my approach went all the way from 2 layer (Business - Client) to now 4 layers. In some specific cases you might need more than these 4, but in usual case it enough. So the layers are:

  1. Business/Domain Layer
  2. Service Layer
  3. Client Layer
  4. Transport Layer

Business/Domain layer

Business layer doesn't really belong to the library itself, it is application specific. This layer contains business logic, which operates in business terms. For example, your use case requires you to create a Google Analystics visitor statistics widget for your backend panel. It is important to note, that use case does not define which protocol do you need to use or which request you need to make. All because it's irrelevant, the use case is still valid regardless of technical details. We can use some pseudocode to describe usage of the client library in the use case: gaService.getVisitorStatistics(). Actually, the real code shouldn't look much different. As we see, business layer makes use of a service class, which belongs to a service layer.

$statistics
$statistics
Service
Service
$service->getStatistics()
$service->getStatistics()
Domain
Domain
More...

Some time ago I wrote a post on my hate towards static methods and helper classes. It was more of an emotional rant than anything else and didn't serve the educational purpose. So this is a rewrite of the original post, which I hope can provide more insight.

To begin with, a static method is a method, that can be invoked without instantiating an object of that class. The most common usage of static methods I've seen are helper classes and singleton pattern. A helper class is a collection of static methods, which hopefully has some common topic. So it may look like:

// /helpers/FormatHelper.php
class FormatHelper
{
    public static function formatXml($xmlString) {...}
    public static function formatJson($jsonString) {...}
}

and the usage would be:

// /lib/ServiceClient.php
$this->getLogger()->debug(FormatHelper::formatXml($response));

I hope you get the idea. So here is where I see the problem. More...

Forget about DRY

13.08.2017

I've once had a heated discussion with my colleagues, which eventually motivated me to write this blog post. This goes about DRY programming principle, being oh so loved by many developers. I'm convinced, that many do not really understand what does this principle really promote. DRY suffers from what I would call an "imprinting fallacy" (trying to mimic senior colleagues, while taking barely understood concepts to extreme), the same way the Singleton design pattern does.

An inexperienced developer, lets say Joe, starts working in a team, where words like "design patterns" are frequently used. Joe looks through some online articles and sees a lot of scary words and complex schemes. But then a simple enough passage pops up - a single instance of some class. So Joe starts to use Singleton extensively, as he thinks it makes him a more senior developer, because he now uses a design pattern. He does not know about all the hidden flaws he's building into the project. Same goes for DRY. It's deceptively easy to understand, while the devil is in the details. DRY - Don't repeat yourself, don't duplicate programming code, what's more to it? The same Joe, is happy to use a programming principle he has heard about. He vigorously finds out all repeated lines of code and extracts them into separate (usually into static methods) classes. By doing that, he is most likely damaging the project once again. More...

For my job I needed to write a more efficient function to merge multiple sets of segments. Segments were date ranges where some special offers are applicable. So let's say a 10% discount is valid from 1st Jan to 10th Jan and another 20% discount is valid from 5th Jan to 15th Jan. This means both of them are valid between 5th and 10th. Those are just two ranges, there potentially be two lists of any amount of segments.

Now, I've said more efficient, because the code I inherited has been creating an list of all the dates within given ranges and calling array_merge on them. This has worked until we've started getting dates ranges like "0001-01-01 to 9999-12-31". I've came up with an algorithm, which might not be the most optimal, but I'm pretty happy with it. Especially because I've developed it without stealing parts from Google or StackOverflow. For funsies I'll reproduce the algorithm in Golang (it was in PHP originally).

Funnily enough, while writing this article I've come up with even easier solution. It seems much more obvious and looks like it doesn't deserve an article, but I've already wrote it. So there. Update 2: A day after posting I've simplified the algorithm even more by removing the flattening. It's now about 3 times shorter and 100 times easier to comprehend, than the first PHP version I wrote.

Prerequisites are: segments within a set are not overlapping with each other and are sorted. Let's start with a couple of definitions. I'll use ints for segment begin and end, but generally any comparable type will do (like in PHP I just use date strings "2017-01-25", which are perfectly comparable). You can also view the gist with the source + test here.

type segment struct {
    from, to int64
}

type segments []segment

A couple of functions I'm going to use are flatten, min and max. Flatten removes "borders" between ranges flattening them to an int array, min and max are pretty self-explanatory.

func min(a, b int64) int64 {
    if a < b { return a }
    return b
}

func max(a, b int64) int64 {
    if a > b { return a }
    return b
}

So here it goes:

func Merge(a, b segments) segments {

    result := segments{}

    // While there are entries left
    for len(a) > 0 && len(b) > 0 {

        // Set A will be the one, starting with the earliest date (smallest entry)
        if a[0].from > b[0].from {
            a, b = b, a
        }

        // While A has segments before the B starts, remove those
        if a[0].to < b[0].from {
            a = a[1:]
            continue
        }

        // We've thrown away all the non-intersecting segments at the beginning
        // So here we will have an intersection between a and b
        result = append(result, segment{
            max(a[0].from, b[0].from),
            min(a[0].to, b[0].to),
        })

        // Remove the segment of these two, which ends first
        if a[0].to < b[0].to {
            a = a[1:]
        } else {
            b = b[1:]
        }
    }

    return result
}