Done is better than perfect

Published on Monday 29 July 2019 | 4 minute read

In today's 100 Days of Code I experienced one of those time sinkholes that projects sometimes throw up. It was part due to my decision to implement something that was kind of an unnecessary eye-candy, and part because my production environment threw an error that my development environment didn't - Shoot!

What happened

So what happened? Well, the background is that inspired by this tweet from Laravel's Mohammed Said, I decided to implement a mini feature that would display the published dates of my articles differently, depending on when a reader like you accesses it. If you were to visit this on the day I published this article, it would say 'Published today'. If the day after, then 'Published yesterday' ... etc.

The challenge came with me wanting to use difference phrasing depending on the day of the week that someone is visiting. For example, if you visit on a Monday but the article was published two days ago on Saturday, it would say 'Published on Saturday'. However, if you visit on a Monday but the article was published three days ago, on the Friday, it would say 'Published last Friday' I like it this way because the most recent weekend and current week feel recent, but anything before that feels like in a previous week (FYI - anything article more than five days old just gets the date written out nicely).

It took my a little time to get my head round how to best achieve this. In the end, this is the final working code:

    public function getPublishedAttribute()
    {
        $diffInDays = $this->created_at->startOfDay()->diffInDays(now()->startOfDay());

        if($diffInDays > 5) {

            return "Published on " . $this->created_at->format('l d F Y');
        }

        if($diffInDays > 1) {

            $postIso = $this->created_at->dayOfWeekIso;
            $todayIso = now()->dayOfWeekIso;

            // says that today should be Mon or Tues and post must be Thurs or Fri to use 'last', else use 'on'
            $determiner = ( $todayIso < 3 && $postIso > 2 && $postIso < 6 ) ? 'last' : 'on';

            return "Published {$determiner} {$this->created_at->englishDayOfWeek}";
        }

        if ($this->created_at->isYesterday()) {

            return 'Published yesterday';
        }

        if ($this->created_at->isToday()) {

            return 'Published today';
        }

        //default
        return "Published on " . $this->created_at->format('l d F Y');
    }

All in all, it took about an hour and ten minutes to write the tests and then get my head around the logic. This was part due to needing to dig into the Carbon docs to know what methods I could use, but really the time went in bringing it all together. The above code isn't though the first code that I wrote.

First code written, "Great!" I thought, and pushed and deployed it. But when I visited the list of posts on my live site it was failing! I had no idea why. So I then spent time looking in the log. That told me what had gone wrong and where, but it didn't make sense as to why. So I spent time adding Bugsnag because that's more useful than the log. But that didn't tell me exactly was was going on either.

How I dealt with it

Long story short, it turned out that an article I had written on on Saturday was, for some reason, having a $diffInDays of 1, rather than the expected 2, but was return false to isYesterday(). Originally, I had one return statement at the end that returned among other things $published. But due to the afore mentioned values, it was missing all the code were a value for $published was being set, and so throwing an error there.

The solution was to add in the use of startOfDay() right at the top. The date was slipping through problematically probably because of something to do with the times of day being taken into consideration. startOfDay() strips these out, so it is very clear which day is being referenced. I also added multiple return statements and a default return statement, so that even if it didn't display exactly as I wanted in another fringe case like this, it still wouldn't throw and error.

Learning

So what did I learn? First, I didn't need to do this. With is article write up now, I have spent close to three hours on something that as meant to take one. It has not added three hours of value to my side; it is such a small feature. My goal at the moment is to rapidly prototype features that will add long term value - this was just a nice to have. This taught me priority.

Second, I had at one state earlier thought about adding in the default, but my logic looked water-tight, I thought. This taught me that unexpected behaviours can always, and will always, crop up. The extra time taken needs to be allowed for. If not, then we'll put excessive and unnecessary pressure on ourselves.

--

Today was still valuable for the practice of TDD, and for learning that even tests won't show us all possible errors. It is also valuable because I showed up for day 6 of my 100 days of code, and did it, even it it wasn't a huge success. A less that peak performance day is still better than a no-show. My code and writing this article took me three hours today. Tomorrow, add the ability to show nicely formatted code.

#100DaysOfCode #Day6

Made by Ed in Somerset. 2019.