GunplaHub Update #4 - Refactor and Document Often

Eight months since my last update! I started working again on GunplaHub in April 2018. Like always, it was mostly an on-off relationship on weekday nights and some weekends.

I achieved what I set out to do at the end of my previous post fairly quickly thanks to VueJS. I finished the first iteration of the frontend web client before my hiatus.

This post covers what happens when you haven't looked at your code in a while: seeing new opportunities to refactor and appreciating good documentation to help you get started.

Table of Contents

1) Intro, Technical Decisions, What's Next

2) Slugs, Obfuscated Primary Keys

3) Deployment and Serving Images

4) (Current) Refactor and Document Often

5) Automation and SEO

Encoded Keys to Saner Human Readable External Facing Keys

You might remember in Update #2 that I went with encoded keys that don't exist in my table schema to ID toys, etc.

Well I still think it was an interesting 'good' choice, however..

Human readable primary keys let me easily share pages by typing it or dictating it. A random string of alphanumeric characters of length 8, like 1p634xr5, is random enough for my use case.

Another win: I don't need another dependency just to build a page's URL. All I'm using is a script similar to the below:

pool = 'ABCabc123...'
def generateHash:
    res = ""
    for i in range(0, char_count):
        res+=pool[random.randrange(0,len(pool))]
    return res

Yet another win: it makes development and debugging easier. For example: In my assets folder, I can refer to images as {key}.png. No need to guess and check which image relates to which. If needed, I could also build links to images from the frontend, without relying on my backend.

Say I have a thumbnails/ and full/ folder. A toy with ID ABCD will have a corresponding entry in both folders, named thumbnails/ABCD.png and images/ABCD.png.

BTW, this new ID field isn't really the primary key. That stuff is still hidden from the user :)

'Meta' Endpoints

In GunplaHub, we want users to be able to filter and query toys easily by size and the show they appeared on.

gunplahub-filter-by-show

The catch: there's always a new show. We want to display toys from the new shows too.

Should the frontend keep track of all the shows (ids, endpoints, or titles)? This means we need to update the frontend each time. I did this for a while for GunplaHub. It made sense the time I wrote it. After all, I wanted to get GunplaHub out and not over engineer everything!

However.. doing this process forever is a waste of developer time.

A new show also doesn't mean we have to update our code. That's not right! A new show means we have a new row of data under the Show table. We can off load this work to our database layer, which we interface with using a Meta endpoint, and query for a list of Shows and return it to the frontend for display. This Meta endpoint would look like GET /meta/shows/ and only to the official client.

My simple query looks like this:

SELECT DISTINCT title FROM SHOW_TABLE

With Django Rest Framework, that looks like:

class MetaShowSerializer(serializers.BaseSerializer):
    def to_representation(self, obj):
        return obj['title']]

Hitting the backend just to fill a dropdown list is more upfront 'work' for the frontend, but you don't have to think about updating the app each time you update your data.

The Show Meta endpoint is one of several Meta endpoints I created. One is for fetching the URLs of all toys (for static site generation in VueJS), list of all toy sizes, etc.

READMEs and Scripts for Common Tasks

I thank my past self for writing rich documentation, with instructions on how to run the app after a fresh git clone. My README tells me how to install, run tests, run the app, and what to expect from the app. It definitely felt like overkill the time I wrote it.

If you think thorough READMEs are a waste of effort, you could just include an install script, a run script, and a script to run tests. This approach works well if you commit to using these scripts exclusively and name your scripts similarly across projects.

Most of my serious projects have run-script.sh and run-tests.sh, so my instinct upon cloning my projects is look for them. A run script that just includes manage.py runserver sounds pointless, but it keeps the awkward 'how do I run this again' phase away. Also if your app needs uncommon option flags to properly run, this is the place to put them!

For installation, I have install scripts for: 1) Setting up Postgres 2) Setting up Django/Python 3) Installing initial data using fixtures. My scripts are simply called 1) db-setup.psql 2) setup.py and 3) load-data.py. I also have a run script aptly named run-script.sh and a test script named run-tests.sh.

Thanks to my scripts and my README, I was able to be productive within 15 minutes without fumbling around.

What's Next

The GunplaHub looks good right now. I quickly created a beautiful, responsive website thanks to VueJS and bootstrap-vue. Here are the latest screenshots:

On Desktop: gunplahub-gunpla-page-desktop

On Mobile: gunplahub-gunpla-page-mobile

In the next post I'll talk about automating the boring processes related to GunplaHub and SEO. Thanks for reading up to this far!

tags: gunplahub