From Chaos to Clarity: A Simple Scoring Formula for Prioritizing Projects

published:2024-10-27

updated:2024-10-27

tag:#Golang #Sqlite

category:Tool

You may have that moment when ideas flood into your mind, and you have no control over it. This happens to me all the time. I think of a project and get excited about it. So, quickly, I will write it down before I forget it. The next day or so, the new idea comes to my mind and the cycle starts all over again. Writing down ideas with details is a good way of organizing and prioritizing projects to get them done one by one. But this isn’t the case for me.

As a software engineer who works for companies and has some side hustles(aka. freelance), I end up having very limited spare time. When I get the opportunity to sit down and start one of those projects that was written a long time ago, my mind starts to ask so many questions about the project that I wanna start. “Why you wanna pick this project over the other?”, “Does this project help you financially?”, “Maybe you should start the other project which can be done faster…”, and the questions never end. This obviously will waste my limited time to only think of the project selection. By the time, sometimes never, I’m done answering all those questions, either my time is up or my mind is exhausted.

My limited time is gone, and I didn’t manage to start any of my projects. Usually, this cycle happens every time that I wanna work on my projects. This is a blocker for me, and I’m frustrated about it. So I decided to write a program to tell me what project need to start. But it can’t be as easy as suggesting a project randomly or without any rational validation. The suggestion must have some logic behind it. Something like weight and score it based on their weight. The heavier the weight is, the higher priority the project has.

DECISION MAKING

My first thought was to find some criteria for the projects and give them some values. But then I was thinking, project managers might have some solution to this problem. I never worked as a project manager, nor did I hear what is their approach to prioritizing their projects. After a quick research, I found that they call it Weighted Scoring Model. They find some criteria to the projects, such as “budget”, “deadline”, “manpower”, “impact”, “completion time”, etc. and give each of them a weight. After that each criterion will receive a value from 1 to 5.

Personal projects rarely have the criteria that apply to the company’s projects. So, I must brainstorm for some criteria for my projects.

TIME How long does a project take to complete the first version or POC at least? Answering this question is the trickiest task ever (for me). When we write software that we wrote similar concepts a million times, still we find ourselves stuck in some new scenarios that we never encountered in the past. Software is full of surprises and each project has its requirements that we must deliver. So this adds complexity and unexpected problems, hence the dynamic/unknown completion time. So I can’t have “TIME” as the project’s criteria.

Budget Since the software only needs a computer and a good internet connection, the budget will be out of those criteria. Unless I wanna prototype an IoT or electronic project in which I already have most of the equipment and parts, if I need a new part, the cost is almost negligible. I can’t use “BUDGET” as the criteria, either.

CRITERIA

Impact The impact of completing a project is something that I can decide based on the situation or opportunities at the moment. Some projects do not have an impact on future career opportunities, such as writing this program. For that reason, the criteria should be Impact/Importance. I can think of the impact of a project or how important the nature of the project is. Since this is one of the most important criteria, I would add a weight of 5 to it.

Urgency Even though as a single programmer, I don’t have any deadline for my personal projects, always is a good idea to think ahead and see which project is needed earlier than others. The urgency also has a weight of 5.

Growth When I was younger I used to set new year resolutions and always chose a goal based on personal growth. For me, it’s important to know that the project I’m doing in my limited time, how much can gain my knowledge. So the Growth must be good criteria. This criterion doesn’t directly impact the project, rather it comes as a bonus. The weight can be 2.

Excitement I tried so hard to keep the emotion out of this equation. But at the end of the day, emotion plays a big role in our will. One of the things that can boost my energy is the excitement toward the project. As I mentioned this has a big influence on the process of getting the project done so this is going to have a weight of 4.

Knowledge/Resources I can’t decide on the project time duration, but I can tell how much of my gained knowledge through the past decade can be used in the project. However, some of the projects are fairly new to me and I never had a chance to work on such projects before. For this kind of project, I can use this criterion as “Available Resources”, how much information can be found out there (the internet). The Knowledge and Available Resources work the same, the lower the value the more penalty to the score, and vice-versa. That’s the reason I put these criteria together.

SCORING FORMULA

The Weighted Scoring Model formula suggested the summation of each criterion’s value multiplied by its weight value. That means that all criteria help the score to increase.

score = (Impact's value * 5) + (Urgency's value * 5) + (Growth's value *2) + ...

That’s not what I want. The Knowledge/Resource criterion is a penalty. That means the lower the value, the more penalty on the score. Therefore, projects with maximum criteria value and low Knowledge/Resource value must have a lesser score than projects with maximum criteria value and higher Knowledge/Resource value.

I knew that the Knowledge/Resource criterion must have a negative effect on the score. I can’t deduct the Knowledge/Resources value from the equation because it will influence the equation too much. One way to achieve this is to use an inverse function for the penalty. Instead of having:

score = (Impact's value * 5) + (Urgency's value * 5) + (Growth's value *2) + (Knowledge's value * Criterion's weight)

We need to have:

score = (Impact's value * 5) + (Urgency's value * 5) + (Growth's value *2) - (Constant / Knowledge's value)

Where the “Constant” is a value that determines how significantly the penalty impacts the score.

THE PROGRAM

Now is the perfect time to start writing some code. I spend most of my time on my computer, mainly using the terminal for everything from coding (in Go/Golang) to chatting to taking notes. So the requirement is:

  1. Runs on a terminal (CLI)
  2. Displays projects in a table
  3. Add, edit and delete project
  4. Calculate the scores

Recently, the BubbleTea TUI framework got my attention. TUI stands for Terminal User Interface, and BubbleTea is a framework with a bunch of features that make the CLI program look pretty. TUI programs are pretty compared to line after line in a single color on the terminal.

I started reading its documentation to become familiar with the framework and eventually use it for this project. Soon after that, I found myself pretty confused about the framework’s architecture, it uses “Elm Architecture” which is used in the React framework. Since I hate the JS ecosystem with its gazillion frameworks, I’ve got a bad feeling about it. But that didn’t stop me from trying this framework. The unnecessary complexity of the framework for such a simple application.

TUI

I compared the “BubbleTea” framework to other packages such as “Tview”, “Tcell”, “TermUI”, etc. These packages were very straightforward and I could start to make my CLI app very quickly but I stuck with “Tview” until got to the limitation of not being able to customize the InputField. After spending a few days testing, trying, comparing and writing code with these packages/frameworks, I realized that the time is wasted and not worth making such a program look so pretty. The expectation from this program is the functionality not how it looks.

I decided to go back to the boring CLI looking app design but with a little difference. A long time ago when the GUI (Graphical User Interface) didn’t exist, everything was displayed in a terminal. Some CLI programs drew tables, boxes, headers, etc., using special characters such as | or # as well as “Box-drawing characters”. They look easy enough to implement and not so boring as line by line plain texts. So I ended up making my own interface - obviously not something like BubbleTea.

BUILD

The program needs to be interactive, which means it should listen to the user’s command and after execution, listen to the user’s next command again. For that, I can think of using the “prompt” approach, where the program has a main loop and the first line of the loop should be bufio.NewReader(os.Stdin).

Next is the set of understandable commands based on our needs. Something like “new” to add new project details, “edit” to update the existing project, and “delete” when we don’t need that project to be on the list anymore. So the next thing in the loop should be the switch-case statement to execute the commands accordingly. For the TUI, I decided to draw tables using “Box-drawing characters” and other special characters.

+----+-------+------------------+---------------------------+
| ID | SCORE | TITLE            | DESCRIPTION               |
+----+-------+------------------+---------------------------+
|  1 |  85   | Project A        | Task tracker              |
|  2 |  92   | Project D        | Photo manipulation        |
|  3 |  78   | Project C        | eCommerce API             |
|  4 |  88   | Project B        | Voice over IP - VOIP      |
+----+-------+-----------------+----------------------------+

DATABASE

This program is intended to run on the user’s computer and is designed for only one user. I’ve considered the option of reading/writing into a text file, but that isn’t an elegant solution. The data must be organized and stored properly. Considering that this project is for one user and only involves dataset for projects, using PSQL, MySQL or even MongoDB would be overkill. The only suitable choice for this project is SQLITE, a relational database that stores all its data into a single file. There is much more to it but the key point is its simplicity. Additionally, I can backup or store the database anywhere I choose. Speaking of storing the data, I’ve been considering uploading it to cloud drives such as Google Drive.

SECURITY

Stored data on the computer’s storage or stored on the cloud must be protected against unauthorized users. Even though the personal projects do not contain any sensitive data, it’s always a good idea to protect the data. To protect the data we can encrypt the database. If we encrypt the entire database, we have to decrypt it on program launch and encrypt it again at exit. This approach has some drawbacks:

  • The program can only encrypt the database when gracefully exits. If the program is exited by SIGINT (Interrupt signal) or SIGTERM (Terminate signal - AKA. kill signal), the database will remain unprotected.
  • Encrypting and decrypting the entire database can be expensive as the database holds more and more data.

So I left with only one option: to encrypt the data that we want to keep safe, particularly the value of some columns. This approach has its pros and cons:

  • The overhead of implementing encryption/decryption in the code.
  • Encrypting/decrypting the data on every insert or read can reduce the execution speed.

However, the trade-off is that we can ensure the database won’t be left unencrypted at any stage. On the other hand, the data in personal projects won’t be so big that we notice a significant slowdown due to decryption.

To encrypt the data I like to use AES (Advanced Encryption Standard) which is a symmetric block cipher algorithm. It converts these individual blocks using keys of 128, 192, and 256 bits. The AES takes the data and mixes it up using a secret key, so that only someone with the right key can read it. It does this through several rounds of scrambling, swapping, and mixing the data, making it very hard to reverse without the key. The key (a plain text) size can be 128, 192, or 256 bits. The higher the size, the stronger the key is.

KEY DESIGN

The encryption needs a key and the key can’t be known or a fixed set of characters. It should be set by the user. For that reason, we should let the user set a password for themselves and use the password as the encryption key. Right off the bat, we are not allowed to hard-code it into the program or on a .env file which may leak to online repositories. The password needs to be hashed and stored in the database, but not in the same database as the data as SQLITE stores everything in a single file. We should separate the password from the data.

set-pass

Hashing the password should take a different approach to ensure its secrecy. We do have two types of hashing: one-way hashes which cannot be reversed to the original data, and two-way hashing which can be reversed to the original data. For the passwords we don’t need to reverse them back to the original data to validate them. Instead, we hash the input and compare the hashes together.

There are few hashing algorithms that can be used such as MD5, SHA-1, SHA256, bcrypt, scrypt, and Argon2.

  • MD5 & SHA-1 aren’t secure because they are vulnerable to collision attacks, where different inputs can produce the same hash. They’re also fast, making it easier for attackers to use brute force/precomputed (AKA. Rainbow tables) to crack passwords.

  • SHA-256 is strong but more suitable for general data, not passwords.

  • bcrypt is slow by design which protects against brute force attacks, and it uses salt. A salt is random data fed as an additional input to a password. I choose bcrypt because it is well supported and can be found in Golang ecosystem.

  bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
  • scrypt is resistant to brute force attack but it is memory-intensive.

  • Argon2 is the strongest and designed specifically for password hashing.

PBKDF

When the user sets a password, the password is hashed + salted and stored in the database. Since we are unable to reverse the password to its original text, for the encryption/decryption key, we must prompt the user to enter their password when the program is launched. Just like any website’s login page but we only ask for the password.

The strongest AES key we can use is 256-bit, which will be 32 characters in length. But what if the user’s password is less or more than 32 characters? The answer is PBKDF, which stands for “Password-Based Key Derivation Function”.

enter-pass

The PBKDF is a cryptographic algorithm used to derive a secure cryptographic key from a password. Once we validate the user’s input as ther password, we use the PBKDF package (golang.org/x/crypto/pbkdf2) to generate the key.

pbkdf2.Key([]byte(pass), nil, 4096, 32, sha256.New)

We don’t need to set a salt value to the PBKDF.Key() if we want to use it to decrypt the encrypted data.

COMPILE & INSTALL

This is a tool for organizing your projects based on the calculated score. This tool will help you set priorities for the next project. So it must be accessible like any other tools on the terminal. I’m using Linux as my primary OS and the it is Linux compatible (It may run on MacOS, but not tested).

This CLI program must have a name. I called it “FOCUS”. If we want to use the name as the program name on the terminal, we should place the main.go file into ./cmd/<program-name> directory. To install we can use:

go install cmd/focus

The install flag will first download the dependencies, compile and build the package and then place the binary in $GOPATH/bin directory, making it executable from the terminal. For more detail, you can run:

go help install

HOW IT WORK

If you’re running this program for the very first time, the program will prompt you to “Set Password”. After setting a password, you will enter the main loop. In this loop we can enter commands such as “new”, “edit”, etc.

new-command

By selecting “new” we will move to the create project page which will prompt us for “Project title”, “Description”, and “Criteria”. The “title” & “description” are expected to receive alphanumeric characters, including spaces and special characters. The “criteria” can only accept numeric value from one to five.

create-project

After answering the questions, we will go back to the main page. This time we will see the table of projects above the command prompt.

show-table

To update a project detail, you need to enter edit followed by the project’s ID. This will display similar prompts as when creating a project, but all of them are optional. Therefore, you can only update the data that you entered and keep the rest as it is.

FUTURE UPDATES

I may add an auto backup system where the program will make a backup and automatically push to the cloud drive. It would be nice to have a time tracking feature where we can see how long a project took to complete. The features and updates are limitless but for now it is good enough for the program’s initial intended purpose.

CONCLUSION

To wrap things up, I’ve created a program to prioritize my projects based on Impact, Urgency, Excitement, Personal Growth and available resources criteria. Each criterion will receive a number from one to five. At the end each project will have a score. The higher the score the more urgent the project is, and it needs to be started right away.

Also, from a security perspective, I designed the program in a way that allows the user to set a password and store it securely in the database. The password set by the user will be used as the AES encryption key to encrypt the data when storing it and decrypt it when fetching data. The encryption used to protect the data both on the personal computer and cloud drives.