Adam – Procrastinating Developer https://procrastinatingdev.com Tue, 26 May 2020 20:52:45 +0000 en-US hourly 1 https://wordpress.org/?v=5.5.9 https://i2.wp.com/procrastinatingdev.com/wp-content/uploads/2020/04/cropped-android-chrome-512x512-1.png?fit=32%2C32&ssl=1 Adam – Procrastinating Developer https://procrastinatingdev.com 32 32 26453327 Four Things I Wish I Knew When I Started My Career https://procrastinatingdev.com/four-things-i-wish-i-knew-when-i-started-my-career/ https://procrastinatingdev.com/four-things-i-wish-i-knew-when-i-started-my-career/#respond Tue, 26 May 2020 20:50:54 +0000 https://procrastinatingdev.com/?p=3839 When I was getting started early on in my career I made a lot of mistakes. I was unhappy, working for people who didn’t respect me and I was lost. I was unsure as to whether or not I wanted to continue being a developer and for a while, I was unsure if I’d ever be able to make it. I was working my butt off and burning myself out for a company I didn’t believe in.

These are the four things I wish I had known when I was just starting out.

The post Four Things I Wish I Knew When I Started My Career appeared first on Procrastinating Developer.

]]>
When I was getting started early on in my career I made a lot of mistakes. I was unhappy, working for people who didn’t respect me and I was lost. I was unsure as to whether or not I wanted to continue being a developer and for a while, I was unsure if I’d ever be able to make it. I was working my butt off and burning myself out for a company I didn’t believe in.

These are the four things I wish I had known when I was just starting out.

Don’t chase money

I realize that everyone needs a certain amount of money to survive and I’m very privileged to be able to work at a place that I enjoy and pays me enough to cover all of my bills.

The advice I wish I knew when I started was to not pick the job purely because it offered $1k more a year. Find a place where you enjoy working at and then work your ass off. The difference in salaries will generally correct itself when you’re motivated and enjoy working at a place.

Jessica Pryce-Jones, author of Happiness at Work and Founder of iOpener, agrees.

“Happiness at work is closely correlated with greater performance and productivity as well as greater energy, better reviews, faster promotion, higher income, better health and increased happiness with life. So it’s good for organizations and individuals, too.”

By enjoying the place you work at those promotions that close the gap generally come faster.

Find a mentor and ask for help

Finding a mentor early on in your career can be immensely beneficial. Mentors are someone you can ask for advice, someone who can help you get through tough situations and someone who can help you get to where you need to be.

Mentors can come from anywhere. Don’t be afraid to talk to the senior leadership team at your organization. It’s easy to get into the mindset that they’re too busy or too important to talk to you but some of the best mentors I’ve had have come from the VP level.

You don’t always have to look upwards to find a mentor. Your colleagues, friends and acquaintances can also be mentors. Everyone has different experiences. People who work at the same level as you will have had different opportunities that you can learn from. These relationships can often be more collaborative since both sides can learn a lot.

It’s ok to fail

I wish I had taken more chances when I was starting my career. I had the mindset that failure was bad and should be avoided at all costs. Failure can be a great thing, as long as you learn from it. If you’re continually failing at the same thing, over and over, you’re only going to get frustrated. If you can take that failure and learn from it, you’ll be better prepared in the future.

It’s easier to fail when you’re just starting out since you’re likely to have less at risk. Once you have a family, mortgage and other responsibilities it’s harder to take risks since failure is more costly.

Family > Job

At the end of the day, your job is just a means to make money (and hopefully friends) to be able to spend more time with your family. There will be times where focusing on your career, spending long hours working, is putting your family first. You’re spending the time to learn, to further your career which directly benefits your family. The advice I’d have for my younger self is to always circle back and make sure the work I’m doing is putting my family first.

Is that 80 hour work week going to put me ahead so I can spend more time with my family in the future? Is this new position going to unlock things for me or give me more time with my family? If the answers no then you need to look really hard to see if it’s worth it.

The post Four Things I Wish I Knew When I Started My Career appeared first on Procrastinating Developer.

]]>
https://procrastinatingdev.com/four-things-i-wish-i-knew-when-i-started-my-career/feed/ 0 3839
7 Musings on Minion Management https://procrastinatingdev.com/7-musings-on-minion-management/ https://procrastinatingdev.com/7-musings-on-minion-management/#respond Tue, 19 May 2020 13:35:57 +0000 https://procrastinatingdev.com/?p=3829 So, you’ve just been promoted to the dark side of management but you don’t know how to lead your minions? Here are a few tips on keeping your plebeians in line. Make Your Minions Do the Dirty Work You shouldn’t worry yourself about work that’s underneath you, you get paid way to much. You’ve hired [...]

The post 7 Musings on Minion Management appeared first on Procrastinating Developer.

]]>
So, you’ve just been promoted to the dark side of management but you don’t know how to lead your minions? Here are a few tips on keeping your plebeians in line.

Make Your Minions Do the Dirty Work

You shouldn’t worry yourself about work that’s underneath you, you get paid way to much. You’ve hired coder monkeys to handle everything from downed servers at 3 am to documentation to those pesky clients. All you have to do is sit back and make sure they don’t lose focus.

Don’t Give Any Praise

Your minions just put in 100 hour weeks to get the feature launched on time? They get paid to do that work so there’s really no need to tell them they’re doing their job. Work horses don’t need to be told they’re doing a good job every time so why should your employees? As soon as you’re done you should give them more work since that’s what they need.

Belittle Your Minions

You’re the manager so that makes you better than everyone else. If they were as good as you, they’d be managing people themselves. This means you’ve earned the right to make the others know that they aren’t as good as you.

Always Leave Before Your Minions

Your time is way more valuable and you don’t want to wear yourself out. This means you should come to work later and leave earlier than your team. Besides, as long as you’re doing a good job guiding your newbies they should be able to carry on their work when you’re gone.

Avoid All Responsibility

If your staff messes something up it’s their fault, not yours. Why should you be held responsible for something they’ve done. When the top brass asks you what happened make sure they know exactly who screwed up. This also secures your position because you’ll never do anything wrong.

Believe That Your Job Is More Valuable

Obviously the people underneath you wouldn’t be able to do their job without you. Who would lead them? You get paid more so that means you’re way more valuable than them. Make sure that they know you’re more valuable to the company.

Always Think You Are Right

Again, you’ve probably been there longer and you get paid more so that means you’re always right. When someone tries to tell you otherwise make sure you make them look bad to their co-workers; this will show them who’s boss.


If you’ve gotten to this point and haven’t realized you shouldn’t do any of these things you probably shouldn’t be managing people. If you realized that you should be doing exactly the opposite than go forth and manage!

The post 7 Musings on Minion Management appeared first on Procrastinating Developer.

]]>
https://procrastinatingdev.com/7-musings-on-minion-management/feed/ 0 3829
Properly Handling Failures https://procrastinatingdev.com/properly-handling-failures/ https://procrastinatingdev.com/properly-handling-failures/#respond Tue, 12 May 2020 10:58:00 +0000 http://procrastinatingdev.com/?p=3750 Every developer has written a bug at least once in their career. It’s almost a rite of passage to debug faulty code and turn it into something that works. Most of the time these bugs are caught before they ever reach the eyes of your customers but every so often a bug gets through to [...]

The post Properly Handling Failures appeared first on Procrastinating Developer.

]]>
Every developer has written a bug at least once in their career. It’s almost a rite of passage to debug faulty code and turn it into something that works. Most of the time these bugs are caught before they ever reach the eyes of your customers but every so often a bug gets through to production. How you handle these bugs can be the difference between a successful program or business and a failed one.

Remember the two benefits of failure. First, if you do fail, you learn what doesn’t work; and second, the failure gives you the opportunity to try a new approach 

Roger Von Oech

Be Transparent

One of the most important things you can do when you screw up is to be transparent. Let your users know what happened, why it happened, what you’re doing to fix it and how you’re going to prevent it from happening in the future. While this doesn’t fully restore your user’s faith in your development team and the product it goes a long way to repairing the broken bridges.

Constant updates help your users know the progress that you’re making to recover from failure and can give them an idea of when everything will be back to normal (if the problem is more than a simple fix).

Apologize

You made the mistake, you should apologize. If you broke someone’s phone you would apologize and this is no different. By apologizing you’re admitting that you’ve done something wrong and are sorry about breaking something that people use. Apologizing isn’t hard, a simple sorry is enough, but make sure you actually apologize and not just pseudo-apologize.

Fix it Quickly

This one is pretty straightforward. The longer an issue exists the more likely you’re going to lose customers. If the problem exists for more than a couple of hours you should look into compensating your users with either a discount or something else of value. This, in combination with an apology and transparency, should help you keep your users through your failures.

Learn from it

The best thing you can do after failing is to learn from it.

The only real mistake is the one from which we learn nothing

Henry Ford

This is where postmortems come in. By documenting your failures you make it easier to learn from and easier to prevent in the future. You’re building out a repository of information that new developers can lean on when deploying new code. This also helps you prevent similar issues from happening in the future.

The post Properly Handling Failures appeared first on Procrastinating Developer.

]]>
https://procrastinatingdev.com/properly-handling-failures/feed/ 0 3750
Using Python to generate over 10,000 unique 8-bit lightsabers https://procrastinatingdev.com/using-python-to-generate-over-10000-unique-8-bit-lightsabers/ https://procrastinatingdev.com/using-python-to-generate-over-10000-unique-8-bit-lightsabers/#comments Mon, 04 May 2020 10:44:00 +0000 http://procrastinatingdev.com/?p=3757 Python is great for a number of things. It powers 1.4% of the internet, Nasa uses it a lot! and you can use it to create art. In honour of Star Wars Day, I wanted to create a program that dynamically generated lightsabers and tweeted them out once a day. TL;DR I created a computer [...]

The post Using Python to generate over 10,000 unique 8-bit lightsabers appeared first on Procrastinating Developer.

]]>
Python is great for a number of things. It powers 1.4% of the internet, Nasa uses it a lot! and you can use it to create art. In honour of Star Wars Day, I wanted to create a program that dynamically generated lightsabers and tweeted them out once a day.

TL;DR

I created a computer program that generates a unique lightsaber made out of four different parts (blade, hilt, pommel and button) and tweets it out once a day along with some statistics about the lightsaber.

Luke briefly had a red lightsaber

Getting Started

The overall premise of this script is fairly simple. It randomly selects four pieces of a lightsaber and then pieces them together. To achieve this I used an online tool called Piskel to generate all of the 8-bit parts. While Piskel is normally used to create animated sprites for websites it’s one of the better 8-bit art editors. My favourite feature from it is the ability to mirror lines. This simplifies the process of making symmetrical lightsabers.

Once I created a base set of parts put them in an images directory. This directory has 5 folders, one for each part and an output folder (lightsabers). This allows me to keep everything organized as well as helps later on when generating lightsabers.

images/
    blades/
        b1.png
        b2.png
    hilts/
        h1.png
        h2.png
    buttons/
        u1.png
    pommels/
        p1.png
    lightsabers/
        h1b2u1p1.png

To do all of this I installed three python packages. I use Pillow (version 7.1.1) for all of the image processing, Tweepy (version 3.8.0) to send out the tweets and numpy (version 1.18.3) to do some colour processing that I’ll discuss later on. Overall it’s a very simple requirements.txt

# requirements.txt
numpy==1.18.3
Pillow==7.1.1
tweepy==3.8.0

The first version of this script was really this simple. Call generate_lightsaber and that’s it. Up next I’ll talk about how I generated the initial lightsaber (just the blade and hilt), then how I added buttons and pommels and finally how I tweeted the final image.

# lightsaber.py
import os, random
from pathlib import Path, PurePath
# This is actually Pillow but it's API is backwards compatible with the older PIL
from PIL import Image

def generate_lightsaber():
    # Will talk about this more next

if __name__ == "__main__":
    generate_lightsaber()

Adding Blades and Hilts

In my initial design, I only had blades and hilts so that’s where I started. My first goal was to get a single blade and a single hilt merged and properly lined up on a single image. I first added code to fetch a unique lightsaber part based on the directory layout above.

# lightsaber.py
# Set some constants to keep the code clean and consistent
IMAGE_PATH = Path('../images')
BLADE_PATH = IMAGE_PATH / 'blades'
HILT_PATH = IMAGE_PATH / 'hilts'
OUTPUT_PATH = IMAGE_PATH / 'lightsabers'

def fetch_lightsaber_parts():
    hilt = Path(f"{HILT_PATH}/{random.choice(os.listdir(HILT_PATH))}")
    blade = Path(f"{BLADE_PATH}/{random.choice(os.listdir(BLADE_PATH))}")

    return (hilt, blade)

First, we have os.listdir(), which returns a list of filenames in the directory given. The list is in arbitrary order and it does not include the special entries ‘.’ and ‘..’ even if they are present in the directory. It does, however, include any folders or special files (like .DS_store in OS X) so make sure your folder only includes the image files. We pass that filename into random.choice which will choose a random filename. Finally, we create the full file path and return that for both the hilt and the blade.

Once we’ve gotten the image paths we can start piecing them together into a final image.

# lightsaber.py
def generate_lightsaber():
    blade_path, hilt_path = fetch_lightsaber_parts()

    # Open the images using Pillow
    blade = Image.open(blade_path, 'r')
    hilt = Image.open(hilt_path, 'r')

    # Open the output image. Twitter displays the entire image if it's 1024x512
    output = Image.new("RGB", (1024, 512), (255, 255, 255))

    # Paste the blade and hilt onto the output image. 
    output.paste(blade, blade_offset, mask=blade)
    output.paste(hilt, hilt_offset, mask=hilt)

    # Save the output image to disk
    img.save("{}/{}.png".format(OUTPUT_PATH, output_filename))

First, we’ll fetch the paths as described above. After that, we’ll use Pillow to open the images as well as create a new output image with the width and height specified by Twitter to allow us to show the entire image as a preview. Finally we paste the two original images and save the output.

When pasting the images onto the output you’ll see two arguments that I haven’t shown yet, <part>_offset and mask. The offset is used to determine where the parts are placed on the output image. When you get this wrong, funny things can happen.

The tiniest of blades

To calculate the offset, where the image gets placed, I need to get and store all of the dimensions of the images. Then we calculate the middle position for the width (output_w - blade_w) // 2 this aligns it horizontally on the image. This is true for both the blade and the hilt.

To position the blade and hilt vertically it requires a little more math. For the hilt, we take the height of the output image (512px) and subtract the height of the hilt image (between 70 and 120px depending on the design). This makes the hilt start at the bottom of the image.

# lightsaber.py
output_w, output_h = output.size
blade_w, blade_h = blade.size
hilt_w, hilt_h = hilt.size

blade_offset = ((output_h - blade_h - hilt_h + hilt_offset), (output_w - blade_w) // 2)
hilt_offset = (output_h - hilt_h, (output_w - hilt_w) // 2)
You can think of the image like an array

Using the array above as an example, if we had a lightsaber 1px wide and 2px (1px hilt, 1px blade) tall the math would be.


blade_offset = ((4 - 1 - 1), (5 - 1) // 2 ) # (2, 2)
hilt_offset = ((4 - 1), (5 - 1) // 2) # (3, 2)

Finally, we have to pass in the original image as a mask, otherwise, Pillow will default the transparent backgrounds into black. By using a mask Pillow only writes data to the output image for the pixels that have colour. The image below on the left has a mask applied, the one on the right does not.

Adding Hilt Offsets and Extra Information

When calculating the blade offset you may have noticed a variable hilt_offset. In some designs, the part where the blade comes out of the hilt isn’t the start of the lightsaber. In these cases I needed the blade to start “below” where the hilt starts. Knowing that I needed to do this but also store metadata on hilts, blades, buttons and pommels down the road I created a manifest.py file to store these defaults.

# manifest.py
MANIFEST = {
    "hilt": {
        "h1": {
            "offsets": {
                "blade": 5
            }
        }
    }
}
# lightsaber.py
from manifest import MANIFEST

def get_hilt_offset(hilt):
    return MANIFEST['hilt'][hilt]['offsets']['blade']

def generate_lightsaber():
    ...
    hilt_offset = get_hilt_offset(hilt)
    blade_offset = ((output_h - blade_h - hilt_h + hilt_offset), (output_w - blade_w) // 2)
    

Without this offset, I was originally getting a floating blade for certain designs. You can see below that the bottom of the blade lines up with the top of the rightmost side of the lightsaber but it still looks funny.

The force must be keeping this thing together

This manifest file also holds information on button offsets, colours and tweet information but I’ll go more in-depth into that later on.

Adding Buttons

After getting the blades and hilts lined up I realized that to hit 10,000 unique lightsabers with only blades and hilts I would need to design 100 of each (200 total). My 8-bit art skills just aren’t up to that. By adding buttons I could cut down the unique designs needed only 22 each (66 total). Buttons were added to the final image in exactly the same way as hilts and blades.

# lightsaber.py
# Set some constants to keep the code clean and consistent
IMAGE_PATH = Path('../images')
BLADE_PATH = IMAGE_PATH / 'blades'
HILT_PATH = IMAGE_PATH / 'hilts'
BUTTON_PATH = IMAGE_PATH / 'buttons'
OUTPUT_PATH = IMAGE_PATH / 'lightsabers'

def fetch_lightsaber_parts():
    hilt = Path(f"{HILT_PATH}/{random.choice(os.listdir(HILT_PATH))}")
    blade = Path(f"{BLADE_PATH}/{random.choice(os.listdir(BLADE_PATH))}")
    button = Path(f"{BUTTON_PATH}/{random.choice(os.listdir(BUTTON_PATH))}")

    return (hilt, blade, button)

def generate_lightsaber():
    blade_path, hilt_path, button_path = fetch_lightsaber_parts()

    # Open the images using Pillow
    blade = Image.open(blade_path, 'r')
    hilt = Image.open(hilt_path, 'r')
    button = Image.open(button_path, 'r')

    # Open the output image. Twitter displays the entire image if it's 1024x512
    output = Image.new("RGB", (1024, 512), (255, 255, 255))

    output_w, output_h = output.size
    blade_w, blade_h = blade.size
    hilt_w, hilt_h = hilt.size
    button_w, button_h = button.size

    hilt_offset = get_hilt_offset(hilt)
    blade_offset = ((output_h - blade_h - hilt_h + hilt_offset), (output_w - blade_w) // 2)
    hilt_offset = (output_h - hilt_h, (output_w - hilt_w) // 2)

    # Paste the blade and hilt onto the output image. 
    output.paste(blade, blade_offset, mask=blade)
    output.paste(hilt, hilt_offset, mask=hilt)
    output.paste(button, get_button_offset(hilt), mask=button)

    # Save the output image to disk
    img.save("{}/{}.png".format(OUTPUT_PATH, output_filename))

The main complication with buttons is that they can be placed in a square on the hilt and this square is different for each hilt. There are a few different ways we could do this automatically (detect edges, detect width and height of the blade, etc…) but for simplicity’s sake, I just decided to manually calculate it for each hilt and store it in the manifest file.

# manifest.py
"hilt": {
    "h1": {
        "offsets": {
            "blade": 0,
            "button": {
                "x": (8, 9),
                "y": (110, 111)
            },
        },
    },
}

# lightsaber.py
def get_button_offset(hilt):
    between_x = MANIFEST['hilt'][hilt]['offsets']['button']['x']
    between_y = MANIFEST['hilt'][hilt]['offsets']['button']['y']

    return (random.randint(between_x[0], between_x[1]), random.randint(between_y[0], between_y[1]))

Adding Pommels

Once again, pommels were added to the image in much the same way as the others only this time I needed to update the height of both the blade and the hilt to accommodate the pommel. This didn’t go well at first.

This is what happens when you get your +’s and -‘s mixed up
# lightsaber.py
# Set some constants to keep the code clean and consistent
IMAGE_PATH = Path('../images')
BLADE_PATH = IMAGE_PATH / 'blades'
HILT_PATH = IMAGE_PATH / 'hilts'
BUTTON_PATH = IMAGE_PATH / 'buttons'
POMMEL_PATH = IMAGE_PATH / 'pommels'
OUTPUT_PATH = IMAGE_PATH / 'lightsabers'

def fetch_lightsaber_parts():
    hilt = Path(f"{HILT_PATH}/{random.choice(os.listdir(HILT_PATH))}")
    blade = Path(f"{BLADE_PATH}/{random.choice(os.listdir(BLADE_PATH))}")
    button = Path(f"{BUTTON_PATH}/{random.choice(os.listdir(BUTTON_PATH))}")
    pommel = Path(f"{POMMEL_PATH}/{random.choice(os.listdir(POMMEL_PATH))}")

    return (hilt, blade, button, pommel)

def generate_lightsaber():
    blade_path, hilt_path, button_path, pommel_path = fetch_lightsaber_parts()

    # Open the images using Pillow
    blade = Image.open(blade_path, 'r')
    hilt = Image.open(hilt_path, 'r')
    button = Image.open(button_path, 'r')
    pommel = Image.open(pommel_path, 'r')

    # Open the output image. Twitter displays the entire image if it's 1024x512
    output = Image.new("RGB", (1024, 512), (255, 255, 255))

    output_w, output_h = output.size
    blade_w, blade_h = blade.size
    hilt_w, hilt_h = hilt.size
    button_w, button_h = button.size
    pommel_w, pommel_h = pommel.size

    pommel_offset = pommel_h
    hilt_offset = get_hilt_offset(hilt_name) - pommel_offset
    button_offset = get_button_offset(hilt_name)  

    blade_offset = ((output_h - blade_h - hilt_h + hilt_offset), (output_w - blade_w) // 2)
    hilt_offset = (output_h - hilt_h - pommel_offset, (output_w - hilt_w) // 2)
    pommel_offset = (output_h - pommel_h, (output_w - pommel_w) // 2)

    # Paste the blade and hilt onto the output image. 
    output.paste(blade, blade_offset, mask=blade)
    output.paste(hilt, hilt_offset, mask=hilt)
    output.paste(button, get_button_offset(hilt), mask=button)

    # Save the output image to disk
    img.save("{}/{}.png".format(OUTPUT_PATH, output_filename))

The next challenge I encountered with pommels was designing them so they looked nice across all hilts. When I first designed the lightsaber I included both the hilt and the pommel. When I realized I needed to split them out to allow for more unique combinations I simply cut the old 8-bit images in two. The problem with this is that the colour schemes between hilts and pommels just didn’t match.

After a lot of thinking I decided that I could do something similar to how green screens work. If I designed the images with specific colours (in this case Red = Primary, Blue = Secondary and Green = Tertiary) I could pull them out using Pillow and Numpy and substitute them for the colours I want.

# manifest.py
"hilt": {
    "h1": {
        "offsets": {
            "blade": 0,
            "button": {
                "x": (8, 9),
                "y": (110, 111)
            },
        },
        "colours": {
            "primary": (216,216,216), #d8d8d8
            "secondary": (141,141,141), #8d8d8d
            "tertiary": (180, 97, 19), #b46113
        },
    },
}

# lightsaber.py
import numpy as np

def convert_colours(img, hilt):
    img = img.convert('RGBA')
    data = np.array(img)

    # Grab the pixels which are 100% red, 100% blue and 100% green
    red, green, blue, alpha = data.T
    primary = (red == 255) & (blue == 0) & (green == 0)
    secondary = (red == 0) & (blue == 255) & (green == 0)
    tertiary = (red == 0) & (blue == 0) & (green == 255)

    # Substitute out the colours for the hilt colour scheme
    data[..., :-1][primary.T] = MANIFEST['hilt'][hilt_name]['colours']['primary']
    data[..., :-1][secondary.T] = MANIFEST['hilt'][hilt_name]['colours']['secondary']
    data[..., :-1][tertiary.T] = MANIFEST['hilt'][hilt_name]['colours']['tertiary']

    return Image.fromarray(data)

def generate_lightsaber():
    ... 
    pommel = Image.open(pommel_path, 'r')
    pommel = convert_colours(pommel, hilt_name)
    ...

Once I did that everything was all set. The pommels now have the correct colour scheme as the lightsaber.

Generating Random Tweet Text

The final part of all of this is generating the actual tweet. I added a number of fields to the manifest file including the hilt length and material, the blade colour, crystal and who used it and finally the pommel length. After that a new function, generate_tweet_text, that pulls all of this new information together and generates the text to tweet out.

# manifest.py
"hilt": {
    "h1": {
        "offsets": {
            "blade": 0,
            "button": {
                "x": (8, 9),
                "y": (110, 111)
            },
        },
        "colours": {
            "primary": (216,216,216), #d8d8d8
            "secondary": (141,141,141), #8d8d8d
            "tertiary": (180, 97, 19), #b46113
        },
        "length": 24,
        "materials": "Alloy metal/Salvaged materials"
    },
},
"blade": {
    "b1": {
        "colour": "Red",
        "crystal": ["Ilum crystal", "Ultima Pearl"],
        "type": "Sith"
    },
},
"pommel": {
    "p1": {
        "length": 5,
    },
}

# lightsaber.py
AVERAGE_HILT_LENGTH = 25
AVERAGE_POMMEL_LENGTH = 3
AVERAGE_BLADE_LENGTH = 90

NAMES = ['List', 'of', 'generated', 'names']

def generate_tweet_text(hilt, blade, pommel):
    hilt_details = MANIFEST['hilt'][hilt]
    blade_details = MANIFEST['blade'][blade]
    pommel_details = MANIFEST['pommel'][pommel]

    hilt_length = hilt_details['length']
    pommel_length = pommel_details['length']

    total_length = hilt_length + pommel_length
    average_length = AVERAGE_HILT_LENGTH + AVERAGE_POMMEL_LENGTH
    blade_length = int(AVERAGE_BLADE_LENGTH * (total_length / average_length))

    title = blade_details['type']
    if type(title) is list:
        title = random.choice(title)

    crystal = MANIFEST['blade'][blade]['crystal']
    if type(crystal) is list:
        crystal = random.choice(crystal)

    name = f"{title} {random.choice(NAMES)}"

    tweet = f'''Owner: {name}
Hilt Length: {total_length} cm
Blade Length: {blade_length} cm
Blade Colour: {MANIFEST['blade'][blade]['colour']}
Kyber Crystal: {crystal}

#StarWars
'''

    return tweet

To generate the unique names I used this Star Wars name generator to generate a number of names. I then randomly choose one to use.

To tweet out the final tweet I use the python library Tweepy to do all of the heavy lifting for me. Since I already have the image saved on file all I need to do is grab the credentials from an environment variable, upload the media and then post the tweet.

consumer_key = os.getenv('CONSUMER_KEY')
consumer_secret = os.getenv('CONSUMER_SECRET')

access_token = os.getenv('ACCESS_TOKEN')
access_token_secret = os.getenv('ACCESS_TOKEN_SECRET')

auth = tweepy.OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
api = tweepy.API(auth)

tweet_text = generate_tweet_text(hilt, blade, pommel)

media = api.media_upload(path)
api.update_status(status=tweet_text, media_ids=[media.media_id,])

If this is your first time setting up a twitter bot you’ll need to create an application first, grab your credentials and then store them in an environment variable.

Wrapping it all up

Now that everything’s running correctly, I’ve got it generating a unique lightsaber and posting it to Twitter I just have to add it to a Cron to run every day.

12 21 * * * python3 lightsaber.py >> lightsaber.log

This will run the program, every day at 9:12 pm. And with that, we’re done! We’ve got a Python program that generates a unique lightsaber, every single day, and tweets it out. Which Jedi/Sith owns your favourite lightsaber?

The post Using Python to generate over 10,000 unique 8-bit lightsabers appeared first on Procrastinating Developer.

]]>
https://procrastinatingdev.com/using-python-to-generate-over-10000-unique-8-bit-lightsabers/feed/ 4 3757
Adding Cron to a Digital Ocean Droplet https://procrastinatingdev.com/adding-cron-to-a-digital-ocean-droplet/ https://procrastinatingdev.com/adding-cron-to-a-digital-ocean-droplet/#respond Tue, 28 Apr 2020 13:18:00 +0000 http://procrastinatingdev.com/?p=3784 I love Digital Ocean. It’s incredibly easy to set up, it’s cheap to get started and you can scale up easily as your website grows. ProcrastinatingDev is hosted on there and when I needed a host for my latest project, Daily Lightsaber, Digital Ocean was the obvious choice. The main feature of Daily Lightsaber is [...]

The post Adding Cron to a Digital Ocean Droplet appeared first on Procrastinating Developer.

]]>
I love Digital Ocean. It’s incredibly easy to set up, it’s cheap to get started and you can scale up easily as your website grows. ProcrastinatingDev is hosted on there and when I needed a host for my latest project, Daily Lightsaber, Digital Ocean was the obvious choice.

The main feature of Daily Lightsaber is the scheduled post of a new lightsaber every day. To do this I needed to add Cron to my droplet. This guide assumes you have a droplet running with Ubuntu 18.04.

Installing Cron

The first thing you’ll want to do is make sure your droplet’s package manager is updated to the latest version.

$ sudo apt update

Then you can simply install cron

$ sudo apt-get install cron

Editing your Crontab

Once you’ve installed cron onto your droplet you can edit your crontab and start adding entries to run automatically. A crontab is a special file that holds the schedule of jobs cron will run. To edit your crontab you can type:

$ crontab -e

The first time you run this command you’ll be given the option to choose a text editor to edit it with. If you don’t have a preference, nano is probably the most user friendly.

no crontab for <user_name> - using an empty one

Select an editor.  To change later, run 'select-editor'.
  1. /bin/nano        <---- easiest
  2. /usr/bin/vim.basic
  3. /usr/bin/vim.tiny
  4. /bin/ed

Choose 1-4 [1]: 

Once you’ve chosen you’ll see the default crontab text. You can remove all of the comments if you’d like or simply add a new line at the bottom.

# Edit this file to introduce tasks to be run by cron.
# 
# Each task to run has to be defined through a single line
# indicating with different fields when the task will be run
# and what command to run for the task
# 
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').# 
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
# 
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
# 
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
# 
# For more information see the manual pages of crontab(5) and cron(8)
# 
# m h  dom mon dow   command

Each line in the crontab will execute a single command. Below I execute a python script everyday at 9:12pm and pipe any output to a log file.

# ┌───────────── minute (0 - 59)
# │ ┌───────────── hour (0 - 23)
# │ │ ┌───────────── day of the month (1 - 31)
# │ │ │ ┌───────────── month (1 - 12)
# │ │ │ │ ┌───────────── day of the week (0 - 6) (Sunday to Saturday;
# │ │ │ │ │                                   7 is also Sunday on some systems)
# │ │ │ │ │
# │ │ │ │ │
# * * * * * command to execute
12 21 * * * python3 lightsaber.py >> lightsaber.log

Now that you have cron installed and you’ve edited your crontab you need to make sure it’s enabled. This is the final step to get everything working.

$ sudo systemctl enable cron

Now you have a fully functioning cron on your droplet. This will allow you to run commands at a set schedule. If you’re interested in getting set up with Digital Ocean you can use this link to get $100 in free credits.

The post Adding Cron to a Digital Ocean Droplet appeared first on Procrastinating Developer.

]]>
https://procrastinatingdev.com/adding-cron-to-a-digital-ocean-droplet/feed/ 0 3784
The Five Traits I Look For When Hiring https://procrastinatingdev.com/the-five-traits-i-look-for-when-hiring/ https://procrastinatingdev.com/the-five-traits-i-look-for-when-hiring/#respond Tue, 21 Apr 2020 17:53:00 +0000 http://procrastinatingdev.com/?p=3708 Hiring is hard. It’s a stressful process where you’re trying to find a good fit, both personally and technically, all in a few short hours. Over the last five years, I’ve hired numerous developers and while they haven’t all worked out, I’ve been very fortunate to have been able to work with some excellent people. [...]

The post The Five Traits I Look For When Hiring appeared first on Procrastinating Developer.

]]>
Hiring is hard. It’s a stressful process where you’re trying to find a good fit, both personally and technically, all in a few short hours. Over the last five years, I’ve hired numerous developers and while they haven’t all worked out, I’ve been very fortunate to have been able to work with some excellent people.

In every case, as I’m sitting across from them in the interview, I focus on trying to see if the candidate exhibits the following five traits.

Happiness

Research shows that happy people are more productive at work. Happiness is also infectious, spreading to your coworkers, staff and customers. One of our core values at G Adventures is ‘Create Happiness and Community’ and it’d be hard to do this if the candidate can’t even fake happiness in the interview.

Happy people are also less likely to leave. Employees leaving are extremely expensive to the company with one study showing that the cost can be from 16% to 213% of their salary. When an employee leaves it can also be hard on their manager. They’ve spent 40+ hours a week together, often more time than they’ve spent with their families. This bond can be difficult for the manager and lead to burnout or worse.

I like to ask questions like “What are things that your current company does that you like?” and “What makes you happy?” to judge their happiness.

Curiosity

Curious people find better solutions to problems they’re working on, learn things faster and weed out inefficiencies with current processes. They ask more questions, getting to the bottom of issues or new requests faster than less curious people.

I believe that everyone is born curious. This curiosity is taught out of a lot of us via school and work where the correct answer is valued but finding out the why behind it isn’t.

I like to ask questions like “Can you explain a problem you encountered in the past and how you solved it”, “How do you go about learning new things?” and “What’s the next thing you’re interested in learning?” to see how curious they are.

Communication

Communication skills are extremely important. You could be the best engineer but if you can’t communicate why you’re doing something in a certain way to your coworkers your impact on the team will be limited.

The only question I ask that touches on communication is “Explain a time where you disagreed with a co-worker and how you came to a solution.” This will give me insight into how they communicate with others, especially when there’s conflict. For the most part, the entire interview process gives me an idea of how they communicate. I’d worry about hiring someone if they’re struggling with eye contact, fumbling with responses or giving short answers. One thing I’m conscious about here is how nervous they are. Some people shut down more than others when they’re really nervous.

Creativity

This is the hardest of the five traits to look for since creativity means something different to everyone. Creativity to me is the act of turning an idea into a reality. This can involve problem-solving, organization, planning and judgement which are all incredibly useful things for employees to have.

This question is one that I ask rather bluntly “Give me an example of your creativity.” I’m looking for examples from their past jobs or schooling as well as how they define creativity. If the person starts talking about their latest painting or poetry I’ll often nudge them back to how they showcased it at work to keep things focused.

Politeness

I’m not interested in hiring brilliant jerks. I know there’s some controversy over that term but at the end of the day if an employee can’t treat their coworkers with respect I don’t want them on the team. This conflict leads to discouragement and distrust with the hiring process and ultimately leads to other staff leaving.

As I mentioned above, I like to have the candidate talk about a time when they had a conflict with a co-worker. This shows me how they handle conflict, how they try to get their points across as well as how polite they are. I’m looking for the candidate to describe a time where they collaboratively improved a difficult situation through positive discussion, coaching, and problem-solving.

A big red flag for me is when the candidate speaks negatively about their past company or coworkers. I get it, there’s a reason you’re leaving your company but airing your dislikes in an interview is not the right time.

Obviously showcasing these traits doesn’t excuse a candidate from lacking a required skill but if it comes down to two comparable candidates I’ll select the one that’s stronger in one of the above traits every single time.

The post The Five Traits I Look For When Hiring appeared first on Procrastinating Developer.

]]>
https://procrastinatingdev.com/the-five-traits-i-look-for-when-hiring/feed/ 0 3708
From Developer to Manager https://procrastinatingdev.com/from-developer-to-manager/ https://procrastinatingdev.com/from-developer-to-manager/#respond Tue, 14 Apr 2020 13:56:00 +0000 http://procrastinatingdev.com/?p=3675 In early 2013 I transitioned from a developer to a manager of a team of 3 when my manager left suddenly. It took me over two years (and doubling the team size) until I felt like I really understood what it meant to be a manager and thought I was adding value. Here are a [...]

The post From Developer to Manager appeared first on Procrastinating Developer.

]]>
In early 2013 I transitioned from a developer to a manager of a team of 3 when my manager left suddenly. It took me over two years (and doubling the team size) until I felt like I really understood what it meant to be a manager and thought I was adding value. Here are a few things I’ve learnt and tips others gave to me.

Less Coding

By far the hardest thing for me to realize was that I needed to cut back the amount of coding that I did. There are only so many hours in a day and I was finding that I could either code or do other, more managerial things, without working a lot of overtime. This is something my boss kept mentioning to me and it was something I was extremely resistant to in the beginning. Since I’d been a developer for my entire career, and programming since I was 15, I thought the way I added value to the company was by writing more code.

It wasn’t until 18 months after I became a manager that I finally started realizing that I could provide value to the company by doing things other than coding. Things like investing my time in understanding my developers better, better planning out project timelines and figuring out where I wanted my team to head. These were all things I knew intellectually provided value but found very hard to feel accomplished with.

I still code every once in a while at work, especially when I need something out fast and all of my developers are focused on their own projects but it’s definitely something that I’ve had to cut back on. To make up for this I’ve found myself coding a lot more in the evenings for personal projects.

Focus on your People

Your developers are the most important people for your success. Without them, you can only accomplish what you could when you were a developer. With them, you can accomplish so much more.

As I got busier as a manager and more and more things started competing for my time I always made sure to make time for my developers. This included carving out time for one-on-ones, being on time for meetings and in general just being available whenever they needed to talk. This may seem like a no brainer but it can really improve your relationship with your developers when you show them basic things like this.

Each person requires a different type of leadership. Some will need more of a micromanagement style. Working closely with these developers to plan out what they’re working on will help them stay on track. I’ve found that more inexperienced developers generally fall under this style though I’ve also seen seasoned developers who required a more hands approach.

The reason you want to focus on your developers is that it’s extremely expensive when someone leaves. Not only do you lose all of the experience that the developer has accumulated you can also be in a position where you have to pay significantly more for the same experience if the market has been increasing lately. The best thing you can do to keep your developers is to respect them and pay them fairly.

Your Time is Precious

A concept that I struggled with at the beginning was that my time is worth a lot of money, both to myself and my company. While it’s a fairly simple concept, it just wasn’t something I thought about at the beginning. This means that things like meetings, events and other things that take up your time cost money. I finally understood this when I started saying no to meeting requests that didn’t make sense for me to be there. Most people like to include as many people in meetings as possible, regardless of whether or not everyone is required or not. Say no to meetings religiously to keep yourself on track.

The other thing I started doing was to schedule time for myself. When my days started getting busy I would schedule in my lunches, planning sessions and time to learn new things so that it didn’t get eaten up by meetings and other events. I remember one week early on where I had a meeting scheduled for every minute of the week and I barely ate anything. This leads very quickly to burn out. Schedule in an hour here and 30 minutes there for yourself.

Tooling

There’s a number of tools that I use every day to keep track of what my developers are working on. I’ll discuss three that I use every day.

Pen & Paper: One of the biggest changes I made over the past couple of years was switching from Evernote and/or Google Docs to a pen and paper for my note-taking. I found when I was using my computer my attention was never fully there. Using a pen and paper allows me to stay connected to the person I’m speaking with while still being able to take notes.

Trello (Referral Link): I spend a large portion of my time in Trello. Whenever there’s a feature request it goes straight into a card. When I want to see what people are currently working on I go straight to Trello. When I want to discuss with other managers a larger project, those comments go in Trello. It’s a fantastic tool for keeping track of features and bugs.

Github: Github is the best tool for storing code and also a great tool for conducting code reviews. I spend my time in Github going over various pull requests, ensuring that there’s a common style of code that’s carried across all of my developers. We use Trello Cards instead of Github Issues but that’s just a personal preference.

Grammarly: Since becoming a manager I’ve spent a significant amount of time writing. Writing emails, documentation, PR reviews, performance reviews and more. My writing, as you may have noticed here, isn’t always the best but Grammarly helps me out by suggesting fixes and improvements.

Rule of Five

I was recently reading Trello’s Blog where they discussed the “Rule of Five”. This concept is fairly simple: two tasks you’re currently working on, two tasks that are planned to work on next and one task that people might expect you to be working on, but you aren’t actually planning on doing.

This rule helps you from getting overwhelmed by the number of tasks on your plate by allowing you to focus on only a few things at a time. I’ve also found that this rule works wonders for your developers, allowing them to organize what they’re working on and making it easier for you to keep track of the progress of your team.

I hope everything I’ve written above can help developers turned managers from falling into the pitfalls I had. Let me know what tips and tricks you use in your transition.

The post From Developer to Manager appeared first on Procrastinating Developer.

]]>
https://procrastinatingdev.com/from-developer-to-manager/feed/ 0 3675
Why I Became a Developer https://procrastinatingdev.com/why-i-became-a-developer/ https://procrastinatingdev.com/why-i-became-a-developer/#respond Tue, 07 Apr 2020 12:11:00 +0000 http://procrastinatingdev.com/?p=3673 When I was in grade 9 I wanted to be a lawyer. I didn’t really know what that entailed and I had no idea how many more years of schooling I’d have to complete to realize my dream. In grade 10 I took my first law class and absolutely hated my teacher. From that moment [...]

The post Why I Became a Developer appeared first on Procrastinating Developer.

]]>
When I was in grade 9 I wanted to be a lawyer. I didn’t really know what that entailed and I had no idea how many more years of schooling I’d have to complete to realize my dream. In grade 10 I took my first law class and absolutely hated my teacher. From that moment on I knew I would never become a lawyer. I dropped out of law class and took computer science and fell in love. Over the years I’ve learnt more and more and I thought I’d take a few minutes to talk about “Why I Became a Developer”.

Problem Solving

For as long as I can remember I’ve loved solving puzzles. When I would get a new puzzle I’d try to break it down to smaller logical chunks to try to see how the puzzle worked. That’s exactly what I get to do at work every day. I first start with a large overarching problem that’s given to me by a customer and then I have to break it down into smaller logical chunks. Sometimes these problems aren’t exciting or challenging. I’ve written enough CRUD websites that they no longer give challenges but sometimes these problems are unique and fun.

Building Stuff

While I love solving problems and it’s fun to have a challenging career I really enjoy building things. As a kid I was always taking stuff apart, finding out how it worked and then building it back. Writing software provides me with quick gratification because I can see the immediate results of my work. I can quickly put together a new app and once deployed see it being used instantly on the Internet.

Being a developer also lets me build a large range of things. I can build a website one day, an iPhone app another and configure a server on the weekend. This diversity allows a developer to feel new and exciting even if you’ve done it for a few decades.

Continuous Learning

As a developer, if you’re not learning the latest technology you get left behind very quickly. There’s always a new language coming out or a new framework to read about. While this can become overwhelming at first I often find trying a new language out to be a fun experiment on a day off. I’ve also found that this constant learning makes programming stay fresh and fun.

I’ve since been promoted to Manager and now a Director but I think these concepts still ring true, just at a different scale. While I don’t directly build applications and websites anymore I get to build more things by building an amazing team. I’d love to hear why you became a developer. Let me know in the comments below.

The post Why I Became a Developer appeared first on Procrastinating Developer.

]]>
https://procrastinatingdev.com/why-i-became-a-developer/feed/ 0 3673
Welcome to Procrastinating Dev https://procrastinatingdev.com/welcome-to-procrastinating-dev/ https://procrastinatingdev.com/welcome-to-procrastinating-dev/#respond Tue, 31 Mar 2020 11:43:00 +0000 http://procrastinatingdev.com/?p=3639 Welcome to ProcrastinatingDev.com. I started this blog almost 10 years ago to help me become a better developer. Over the years a number of things have changed. I’ve gone from a developer to a manager and then a manager to a director. I’ve gotten married and had two kids. I’ve travelled the world (6 of [...]

The post Welcome to Procrastinating Dev appeared first on Procrastinating Developer.

]]>
Welcome to ProcrastinatingDev.com. I started this blog almost 10 years ago to help me become a better developer. Over the years a number of things have changed. I’ve gone from a developer to a manager and then a manager to a director. I’ve gotten married and had two kids. I’ve travelled the world (6 of 7 continents) and I’ve learned a lot over the years.

Why restart this blog?

This year the world was hit with a devastating pandemic, COVID-19. While under lockdown I began to think about financial stability and how dangerous it is to have a single source of income. I began thinking about the kind of activities I could start doing to better protect myself and my family in case I ever lost my job. I’ve thought of a number of ideas, ranging from building out a side project, writing, dividend investing and more, and I wanted to start chronicling them here. I have no idea whether any of these ideas will ever come to fruition but I thought it’d be an interesting experiment to try.

I’m going to set a goal for myself to hit $5000/month in passive income. This is slightly more than the average Canadian income and I thought it’d be an interesting milestone to reach for. In an upcoming article, I’ll speak more on why I set this goal and how I plan on hitting it.

What to Expect

So what can you expect from procrastinatingdev.com? Well, I’ve got a number of articles already written and plan to release them once a week, every Tuesday, at least for the foreseeable future. These posts will generally fall under one of these categories.

Technical Articles

I’ve got a number of articles planned where I deep dive into a technical topic. My background is in Python so a number of these will be focused on that but there’s also the possibility for articles on Postgres (or databases in general), Javascript and more.

Management Posts

I’ve been a manager for over 7 years now and I’ve spent a tonne of time reading, researching, experimenting and failing at how to be a better manager. I’ve mentored junior developers to senior, senior devs to managers. I’ve hired dozens of people and fired a few too. I’ve managed budgets, negotiated software licenses and presented 5-year strategic roadmaps to C-level executives. I plan on writing about all of these things here.

Side Hustle Updates

I mentioned that one of my goals was to hit $5000/month to protect myself against having a single income. One of the ideas on getting there is to start a side hustle. I’ve got a few ideas brewing (and a couple of domains purchased) so I’ll be speaking about how to build a side hustle from scratch.

Financial Updates

Every month I’ll have an extra post going through the financial update for that month as I strive to hit my goal. I’ll break it down by channel and give insight into how I went about making it and how you could too.

So if any of this sounds interesting and you’d like to follow along please feel free to signup below to stay up to date. See you around!

The post Welcome to Procrastinating Dev appeared first on Procrastinating Developer.

]]>
https://procrastinatingdev.com/welcome-to-procrastinating-dev/feed/ 0 3639
Here are the books I read in 2019 https://procrastinatingdev.com/here-are-the-books-i-read-in-2019/ https://procrastinatingdev.com/here-are-the-books-i-read-in-2019/#respond Thu, 02 Jan 2020 12:59:00 +0000 http://procrastinatingdev.com/?p=3740 In 2019 I read 26 books. This was 9 less than the goal I set in 2018, this was due to a busy personal life (I had a second kid) and changing roles at my company. I also think I got a little burnt out from reading after a fairly quick start. Favourite Books Reset: My [...]

The post Here are the books I read in 2019 appeared first on Procrastinating Developer.

]]>
In 2019 I read 26 books. This was 9 less than the goal I set in 2018, this was due to a busy personal life (I had a second kid) and changing roles at my company. I also think I got a little burnt out from reading after a fairly quick start.

Favourite Books

Reset: My Fight for Inclusion and Lasting Change — Ellen Pao

I followed along as Ellen’s lawsuit was happening against Kleiner Perkins so reading about it from her point of view and all of the events that led up to it was very interesting. The subjects are important and extremely relevant since I’m also in tech. The writing still is simple and easy to get behind and I finished this book extremely quickly.

Meltdown: Why Our Systems Fail and What We Can Do About It — Chris Clearfield

I really liked this book for a number of reasons. The stories were interesting and I was able to tie each one back into a failure I’ve experienced throughout my career. I was expecting this book to be a little dry when I bought it but it was hard to put down once I started.

The First 90 Days: Critical Success Strategies for New Leaders at All Levels — Michael D. Watkins

In September I switched roles and picked up this book to help with the transition. The diagrams and worksheets were invaluable and the examples throughout were incredibly useful even though I’ve been managing for the past 8 years.

I’d definitely recommend this book to anyone switching jobs or becoming a manager for the first time.

The Martian — Andy Weir

This is one of my favourite science fiction novels of all time. I love the story, the humour and the actual science that was thoroughly researched. This is also one of the few books where I thought the movie didn’t totally butcher.

Statistics

Last year I read/listened to 26 books. You can find the full list here. I listened to 17 audiobooks (at 1.5x speed) for a total of 122 hours of listening time (5.1 days). This is one of the reasons why I was able to get through so many books. On average it took me 4.3 days to get through an audiobook vs 12.14 days to get through a hard copy. Of the 7 physical books, I read a total of 1246 pages.

Of the 26 books, only 9 were written by women (35%). This was a significant increase over 2018 where 14% of the books I read were written by women. I plan on continuing this trend into 2020.

The trend continues where I rated books slightly more positive than the rating on Good Reads. In 14 cases I rated the book more favourably than the community, though my average rating was a 4.15/5 vs 4.09/5 so the difference is very minimal. I rated books pretty evenly regardless of the gender of the author but I did notice that I rated physical books much higher than audiobooks in general (4.3 vs 4.05). This makes sense since I tend to only buy books that I think I’ll be interested in.

This year I was much more consistent in the genres of books I read. Last year 23% of the books I read fell under the business category. This year Children, Business, Science Fiction, Politics, Self Help and Fantasy all tied for 11.5%

Onwards

I already have a couple of goals for 2020. I want to continue being mindful of the books I’m reading and who’s writing them. I want to make sure that I’m reading books from a diverse set of authors.

The second goal is to read at least 30 books. This is less than my goal for 2019 but more than I actually read last year. I want to try to be more consistent in the number of books I read in a month. In 2018 there was only a single month where I didn’t read any books, in 2019 there was 4.

My final goal is to read more physical books. I love audiobooks because I can listen to them while I’m walking and driving but I’d really like to get to more physical books. There’s something about flipping through the pages and finally getting to the end that is so much more satisfying than finishing an audiobook.

Here’s to 2020 and more hopefully many more books!

The post Here are the books I read in 2019 appeared first on Procrastinating Developer.

]]>
https://procrastinatingdev.com/here-are-the-books-i-read-in-2019/feed/ 0 3740