The Hard Part

We all get there at some point; it is an inevitability that precedes your next victory in some cases, or perhaps it precedes The Hard Part II in others. Regardless of the the context, it’s coming for you. For me, it’s here!

The Hard Part involves brick walls. It involves trepidation. It involves self-doubt. I’ve spent a lot of time improving myself now and I’m already aware of the signs. I’ve come to learn a few things and have started to employ methods of working through The Hard Part.

Old me would have said forget this. I’m tired of this. Thankfully, new me is tired of being tired of stuff. Instead, I’m going to type out what’s making things The Hard Part and what I’m doing to work through it.

So what’s makes now the hard part?

Okay, so I’m still dealing with the real-life issues that sparked my post from a few weeks ago. After rereading the post again, I’m glad I wrote it and I feel better after rereading it. That said, I think my personal issues currently reside within the Annoying Chore category, rather than the Hard Part. That’s not to say personal issues are an afterthought. I just think my progress in that regard is not something to be worried about. I know I’ll get there.

That effectively just leaves the things I’ve been working on. The main thing I’m working on is my Python Django web application. For the interested, I posted about it here and here.

Since the last post, I recently made some unit tests. I also set up some Raspberry Pis on my local network to do some things. One of the Pis hosts an instance of Gitlab and the other Pi is hosting a runner that works with the Gitlab instance.

The problem I am currently running into is the dumb little box in the bottom left area called “Gitlab Runner.” I’m trying to run the gitlab-runner service in the background of the OS instead of running it manually in a shell in the foreground. It has proven annoyingly difficult. I tried to use crontab, nohup, and shell scripts to get it to run in the background. All of the approaches did not work and this consumed a lot of trial and error to no success.

Now I’ve spent all this time digging into the technicalities of building a working pipeline, and there’s still tests to be written. There’s still product features that that I set out to do. There’s still a million things to learn just to get this clunker-of-a-learning-experience software in a state that I think is acceptable.

One realization I’m coming to is there’s a reason why software companies hire multiple people. Yes, it’s obvious on the surface. Seeing it in front of you is life experience. It turns out that..

  • Making the project
  • Making the tests for the project
  • Setting up an infrastructure where pipelines can be run against the project
  • Setting up the code environment such that the project can be worked on and tested locally while still being compatible with a pipeline
  • The deployment process and integration into the pipeline
  • Using Docker in general in the process
  • Doing other stuff I definitely forgot about

… all at the same time, is in fact a herculean effort.

I’ve always been someone who’s been indecisive. It annoys people. Ask anyone I know. I have to write posts like this to understand what’s going on. I sometimes wonder how “10x” developers do what they do without going mad. As a result of the challenges I’m facing, my mind is going a mile a minute without any sort of productivity as a result.

In these dilemmas, what do you do? I’ll tell you what.

How to deal with The Hard Part

Deep Breath

“Okay.”

Take a step back. What’s the goal I’m trying to achieve in doing this project? Nobody has expectations of me right now. This is huge. Sometimes, it’s easy to get lost in the details and then throw my hands up in frustration when something doesn’t work. That’s fine. Here, look at this:

“What is that?” you may ask. I can tell you what it’s not. It’s not the big picture. Stare at it too long and you start to conjure things in your head of its meaning and its purpose. “Why is it like that? This doesn’t tell me enough. The colors are cool I guess but they don’t mean anything.” Thoughts about it are inconsistent and erratic at best, based on conjecture and frustration.

This is where you have to step back and look at the big picture.

Big picture time.

What am I trying to do with this project?

What I want to do with my Python Django project is understand the process of going from idea to production. Anything more than that is fluff. Yes, I have some features/tests planned out for the project and that’s great, but I will say that the majority of the remaining features/tests fall more into Annoying Chore territory rather than The Hard Part territory. If I let myself, I could keep adding features/tests to the project ad nauseam and never be done, while The Hard Part is lurking, waiting for me to revisit it.

Instead of dreading the Annoying Chores and The Hard Parts at the same time, I’m going to focus on The Hard Parts only. Yes this means some features won’t get done and test cases will not be finished. However, completing those things 100% will not be conducive to a learning experience. Diminishing returns.

Disclaimer: this approach does not work outside the context of learning. The real world unfortunately has its own plans in times like these that are best covered in another post.

I want to learn how to Dockerize my project, run tests against it using a pipeline (even if they are bare bones tests), and deploy it to some local server. Those are the things that matter. I don’t need to think about anything else in the meantime. I have a project and the means to test it. Now it’s time to deal with the operations that work on those means.

Yes, this will involve banging my head on the wall with the previous issue I mentioned. Yes, there will be other challenges to face alongside it. But now that I’ve looked at the big picture again, it can at least be underscored with a sense of purpose.

And anyone who has gained a sense of purpose, however fleeting, will know how powerful that is.

That’s all I’ve got for today. Stay safe friends.

My handy Rust CLI

A week or two ago I started on creating a rust program that would accomplish two things:

  1. Improve my understanding of the package management system for Rust. More specifically, I wanted to understand how to organize modules a little bit better
  2. Create something that I could use to create challenges for myself using an automated and randomized method

I worked on it for a while and then get hung up on the specifics for parsing command line arguments. The approaches I thought about either seemed way too complicated, or were way too hacky to really be proud of writing them. I let the project for a sit for a little bit.

After some time, I realized that I could probably just outsource the parsing logic. Given that I was, at the time, the furthest from the first person ever needing to parse command line arguments, there had to be a library for it.

I was indeed correct and found clap. This library turned my project from a headache into a neat little organized oasis. More on that later. First, I’m gonna describe how the program works.

How the program works

The utility will look at a set of text files that you give it and create a set of challenges. For example, you could have some text files that describe pixel art challenges you can try.

theme.txt

Draw a house
Draw a mountain
Draw a bear

size.txt

at 128x64 pixels
at 256x128 pixels
at 64x64 pixels

palette.txt

with Midnight Ablaze Palette
with Oil 6 Palette
with Apollo Palette
with Spanish sunset palette

With these files in the root directory, you can run something like this:

cargo run -- -f theme.txt size.txt palette.txt -c 5

This will…

  1. Look through the listed files indicated by -f
  2. Print out the challenges’ details in the order they were listed, randomly picking an entry in each file. This will be done five times, as indicated by the -c 5 argument. --count can also be used instead.

One example output could be the following:

Draw a house at 64x64 pixels with Midnight Ablaze Palette 
Draw a mountain at 256x128 pixels with Oil 6 Palette
Draw a house at 128x64 pixels with Apollo Palette
Draw a bear at 128x64 pixels with Midnight Ablaze Palette
Draw a mountain at 256x128 pixels with Midnight Ablaze Palette

Now I have 5 challenges for pixel art I can work with! This is great for generating ideas, especially if you fill a text file with a million different ideas, then also fill another text file with a million other ideas to combine with it.

This could also be good for quizzing yourself, learning a new language, inspiration, you name it.

Anyway, so on the topic of my code being a neat organized oasis, Here is the parser and how it is used by my main program:

Parser & friends

cli.rs

use clap::Parser;

#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
pub struct Args {
    #[arg(short, long, num_args = 1.., required = true)]
    pub files: Vec<String>,

    #[arg(short, long, default_value_t = String::from(" "))]
    pub separator: String,

    #[arg(short, long, default_value_t = 1)]
    pub count: i32
}

main.rs

use chalgen::{file_processor, cli};
use clap::Parser;

fn main() {
    let args = cli::Args::parse();
    file_processor::output_challenges(args);
}

The code above does the following:

  1. Define the command line arguments for the library to look for and what characteristics they should have (cli.rs)
  2. Pass configuration that was gathered from command line arguments to the program doing the actual work (main.rs)

To provide some context for cli.rs, the following keywords are important:

  1. short: the flag can be represented by the first letter of the property name. e.g. files can be referred to as -f when calling the command line utility
  2. long: Effectively the same sort of option as short, except the flag is able to be represented by its full name. e.g. count can be represented as --count when calling the command line utility
  3. default_value_t: Essentially, this is what the flag will be set to if it is not mentioned when calling the command line utility
  4. num_args: How many arguments that are expected from the flag. In this case, I use it once with files and the number of arguments is represented by 1.. which essentially means “at least 1.” I specify it like this to provide the user the ability to provide any number of files they want
  5. required: The flag must be used. In this case, the files flag is required

All of the restrictions and allowances defined above are automatically enforced by the clap library. This is a huge weight off my back and allowed me to focus on the actual program (AKA the file_processor::output_challenges(args) part of the code that I don’t show.

Anyway, that’s about it. Here is the repository for the curious! Stay safe and make good choices.