Synthesis : Scott Becker

Weekend Update 9/13/2020

This has been a rough week.

Fires – A rare weather event in Oregon combined dry conditions, heat, and very strong winds to become a perfect storm. This fueled explosive growth to wild fires brewing throughout the state, both natural and human caused. More than a million acres burned in a single week. More than double the previous record for the entire year. The fires grew shockingly fast. Conspiracy theories abound, but whatever the original cause of each of the fires, wind is why they grew so huge. The wind began to subside on Wednesday, allowing humanity to regain something resembling control over the situation. The fires caused immense devastation, took lives and livelihoods, and completely destroyed many smaller towns like Gates, Phoenix and Talent. It came within a half mile of Estacada and other larger towns very close to Portland and Salem. As of this moment, the march towards Oregon’s major population centers in the north west part of the state appears to have stopped, thanks to the milder weather. I’m really hoping for rain. If the forecast holds, we may get a bit as soon as Tuesday.

Bad Air Quality – After the fire came the smoke. Around Wednesday the air quality started getting bad, and by this weekend it was downright hazardous (AQI – Air Quality Index – readings of around 450-500 in Portland – current worst in the world). The visibility is low and the sky has been a grayish orange. This morning it was just gray and fog-like, but so thick we could barely see the house across the street.

Stuck Indoors – As of this moment on Sunday afternoon, because of the bad air, we’ve remained almost 100% indoors since Thursday, or 4 straight days. I’m anxiously awaiting a dip in the AQI readings so we can get out of the house. Some of our friends opted to drive to the coast today or book a hotel. We were tempted, but we are lucky to have two air purifiers in the house, and they’re running 24/7. I think staying here where we have all of our personal creature comforts is the best option for us right now.

It feels strange to write like this. These weekend review posts are usually much more mundane musings on my personal life, family, and work. This week we have been living through a literal firestorm.

We are some of the lucky ones – we are all alive and healthy. Our house, our neighborhood, and our city is still standing. For all of that I’m grateful, and I can live with being stuck inside for a bit. We’ll get through it.

Leave a comment

Weekend Update – 9/6/2020

Fall is coming. It’s not officially here yet; Tuesday September 22nd is the official start of Fall, but you can feel it and see it. It’s starting to feel a little cooler in the mornings and evenings, leaves are beginning to turn brown and fall off the trees, and the sun is setting a few minutes earlier each day. We even have pumpkins in our backyard this year, and they are getting bigger and more orange each day.

Father/Son Overnight – O went camping for one night with a couple of her girlfriends mid week, so A and I had a solo day. Nothing extreme, we just had fun hanging out in the neighborhood. I got Thai food delivered for dinner. A likes Pad See Ew noodles with tofu. Doordash is great for solo dad night. Then we went on one of our long solo bike/walks for the evening. The next morning we both biked together, to a part of the neighborhood school field we like to call “the canyon.” It’s sort of a dry drainage ditch that runs along one side the field. “The canyon” is much more fun to say. A also likes to say “I want to go on a road trip” which means he wants to ride from one end of the canyon to the other. We watched neighbors toss balls for their dogs, biked around and had snacks – a big component of a fun outing. It was nice to just be on our own, felt like no big deal, and something we should do more.

Cathedral Park Company Outing – Finally had a company outing since the start of coronavirus, meeting up in the park under the St. John’s Bridge. It was refreshing to see everyone in person, and be able to see and hear the people I work with every day in real time living technicolor, not just little boxes on video calls. I brought pizza for everyone, and we played croquet. It was the first time I’ve played that game since the year of my wedding when we bought sets for people to play in the park by our house. Turns out we have still have a cool vintage set at home, so we got it out for A to play around with in the back yard.

Swan Island Beach – We got out to the Willamette river via the small “beach” on Swan Island again this week. Our friends just got the same type of folding kayak as us, as well as an inflatable SUP. We’ve all been enjoying this new novel experience of paddling around on the water. We take turns paddling around and taking the kiddos out, then they spend the rest of the time playing in the sand and having snacks, until the sun sets. This is a really nice way to end a weekday after work.

Khan Academy Kids – I’ve been itching to try this app with A, and finally did this week. So far he seems to like it. I’ve kept it to about ten minutes each time so it stays interesting, then we go do some other non-phone activity. It’s neat to see what he understands (big vs small, more vs less, color distinctions, associating pictures with letters). I like most of the activities and characters I’ve seen so far. Some of the activities seem arbitrary free play with no real goal/reward, like: draw X and tap the green button when you’re done! It doesn’t matter what you draw, it doesn’t check – but this is probably similar to any kind of drawing activity in pre-k school. I think it’s to give them a fun break, get them thinking creatively, and possibly lay groundwork for later things. A couple small frustrating things – the character in the corner (Cody I think) constantly says “tap me if you ever need help!” so A tried it a couple times and it either didn’t do anything, or it just repeated what it just said again. Some non-app specific things are – taps sometimes don’t register with the app (little fingers and the subtle skill of tapping a phone with the part of your finger the phone will recognize), tapping/dragging near the edge of the screen can make the phone actually switch to another app, and random phone notifications popping up on top of the app. There may be a way to disable some of that using “Guided Access.” We’ll try that next time.

Leave a comment

Weekend Update – 8/23/2020

Things I’ve been into this week:

A course – Video Editing with Final Cut Pro X – From Beginner to YouTuber with Ali Abdaal – I recently discovered Ali on YouTube. His videos on productivity are pretty engaging thanks to his quick cut, polished editing style. Ali made a class on Skillshare giving away a lot of his video editing tips and tricks for faster workflow. I’ve made a video course once before, so I appreciate the difficulty in making a polished video of the same high quality we are accustomed to watching as viewers. I’ve been thinking of starting my own YouTube channel lately, so this is good prep.

Writing – I’ve been working on a long form blog post on how to get better sleep – a departure from the mostly technical topics I’ve posted about in the past. Branching out! I’ve now got a ton of content, but I have more work to do to edit it all into a nice flowing article. Ironically, while putting together the post, I was thinking – I’ve now got a good system going and I’m ready to speak to it with authority. Then I fell off the wagon this week and slipped into a pattern of staying up late again. I’m re-committing myself and hitting the reset button this weekend.

In other writing work, I published a new post on my software development company’s blog on saving money and time when building a mobile app. I’m working on a follow up post with more technical suggestions for faster and more efficient development.

Adventures – O and A are doing excellent at getting outdoors into nature, going on hikes and getting out on the water. We got an Oru Inlet folding kayak about a month ago. They are a bit pricey but super lightweight and convenient. It fits inside the car and is only 20 lbs, which makes it no big deal. This has opened up a new area of exploration for us – actually on the water and not just land locked on the shore at rivers and lakes.

Parenthood – The current work-from-home situation means I get to spend a lot more time together with A. I get to be a part of a lot more of his day than I would have otherwise. He has started to ask “why” about various things, and I love attempting to answer.

Leave a comment

Enumerated Types in TypeScript

Enumerated Types, also known as enums, are a feature common to many programming languages, that represent a finite set of possible values for a given type.

For example, in a typical 52 card deck of cards, there is four suits: Clubs, Diamonds, Hearts and Spades. All of the 52 cards are one of these suites.

In a typed programming language, we could have a “Card” type, with a “suit” property. In the most basic form, the type could be defined where the value of “suit” must be a string. We could then assign the value “Clubs”, “Diamonds”, “Hearts”, or “Spades” for each card.

Here’s how that might be represented in TypeScript, and for simplicity, we’re going to make every card have a number value for now and not worry so much about face cards.

interface Card {
  value: number // 2, 3, 4, etc...
  suit: string // either "Clubs", "Diamonds", "Hearts", or "Spades"
}

With this setup, we could assign suit to be “Diamonds”, “Hearts”, etc, but we could also assign it any other string, like “Squares” or “Harts”, and the compiler would not complain, because we said the value can be any string, and “Squares” is a string.

It would be nice if the computer could help us avoid errors like these. That’s one of the things enums can do, along with documenting what the valid values are.

So to lock things down a bit tighter, we can define an enum for Suit, with all possible expected values defined. Here’s how that looks in TypeScript:

enum Suit {
  Clubs,
  Diamonds,
  Hearts,
  Spades,
}

Unless you say otherwise, the value of each item in an Enum is numeric, and starts at 0. Clubs == 0, Diamonds == 1, etc. It can start at a different number, or you can specifically set the value of each item.

You can also make the values be strings, which is handy if you’re dealing with something like a REST API returning JSON data where the value of suit is a string, not a number.

// GET https://awesome.api/cards/32

// JSON Response:
{
  "value": 5,
  "suit": "Diamonds"
}

Here’s a string enum for Suit:

enum Suit {
  Clubs = "Clubs",
  Diamonds = "Diamonds",
  Hearts = "Hearts",
  Spades = "Spades",
}

Slightly redundant since we have to repeat each word twice, but it’s more convenient, and the values map better to the data.

Now you can define a card interface with the more exact Suit enum value defined.

interface Card {
  value: number
  suit: Suit
}

Then we can define a card:

const card1: Card = {
  value: 5,
  suit: Suit.Diamonds
}

This works, because we use the enum when setting the value. If we try to use a random string, it does not work:

const card2: Card = {
  value: 5,
  suit: "x" // compiler error 
}

// error details:
// Type '"x"' is not assignable to type 'Suit'.
// The expected type comes from property 'suit' which is declared here on type 'Card'

It also doesn’t work if we use the same string values as the enum.

const card3: Card = {
  value: 5,
  suit: "Diamonds" // compiler error 
}

// error details:
// Type '"Diamonds"' is not assignable to type 'Suit'.
// The expected type comes from property 'suit' which is declared here on type 'Card'

If value type is an enum, and you manually define a card at compile time, you must use the enum to set the value.

Sometimes numeric enums are desirable. A single integer takes up less space than a string. Sometimes the numeric values are meaningful – it could represent known id values, sort order, or ranking. One possible example is access roles. If all roles are ranked and one always supercedes the other, you could use a numeric enum where the values represent the ranking order:

enum Role {
  SuperAdmin = 0,
  CompanyAdmin = 1,
  Manager = 2,
  Employee = 3,
  Guest = 4,
}

Then let’s say a user can have multiple roles, like ["Employee", "Manager"] and we wanted to get that user’s highest privileged role.

First, if the data looks like that instead of numeric values, we can define a typed union with all possible value of a role string, with this handy keyof typeof syntax:

/**
 * This is equivalent to:
 * type RoleStrings = 'SuperAdmin' | 'TenantAdmin' | 'Provider' | 'Guest' | 'NoAccess';
 */
type RoleStrings = keyof typeof Role;

Then we can define a function that compares the values of each role and find the one with the lowest value:

function getHighestRole(roles: RoleStrings[]) {
  return roles.reduce((acc, role) => {
    // if role's number is lower than previous accumulator role's number, it now wins, return it
    if (Role[acc] > Role[role]) {
      return role

      // otherwise return existing accumulator role
    } else {
      return acc
    }
  }, "Guest") // default to lowest privileged "Guest" role string
}

Now we can define some data and call the function

const myRoles: RoleStrings[] = ["Manager", "Employee"]

getHighestRole(myRoles) // "Manager"

So, enums provide a way to concretely define and document the set of possible values that a specific variable or object property can be, help programmers avoid mistakes while writing code, and enable compilers to catch errors.

Alright, that’s probably enough about enums for now. By this point it’s either totally clear or you’re totally confused. 🙂

Leave a comment

Google Sheets Macros

A macro in Google Sheets is a recording of a series of actions that can be repeated via the Macro menu or with a hotkey.

To start making a macro, click Tools > Macros > Record Macros.

A dialog box pops up showing the macro is currently recording, and lets you choose between absolute and relative references.

Once recording, you can perform some actions (such as formatting a header row), and click the “Save” button in the dialog box, give it a name and an optional hotkey.

The first time you try to use a recorded macro, you will be prompted to authorize it. Authorization cancels the action, so you’ll have to re-run it after you’ve authorized it for it to actually run.

Macros as Code

The really cool thing – is that your recorded actions are automatically translated into statements in a JavaScript function using the Apps Script APIs. After saving a macro, you can go to Tools > Script Editor to view the recorded macro as a script.

Here you’ll see the script editor with a function of the same name as your macro, containing the code statements to reproduce the actions. This macro “Header” changes the colors of the first header row and freezes it.

Here’s that code, with comments for each statement:

/** @OnlyCurrentDoc */
// ^ limit script permissions to current doc

function Header() {
  
  // get the active spreadsheet (the entire document)
  var spreadsheet = SpreadsheetApp.getActive();

  // get the currently selected sheet
  var sheet = spreadsheet.getActiveSheet();

  // select the entire row, relative to cursor starting point (equivalent of clicking number to left of a row)
  sheet.getRange(spreadsheet.getCurrentCell().getRow(), 1, 1, 
  sheet.getMaxColumns()).activate();

  // set background color, set foreground color, and bold it
  spreadsheet.getActiveRangeList()
    .setBackground('#4c1130')
    .setFontColor('#ffffff')
    .setFontWeight('bold');

  // freeze the first row
  spreadsheet.getActiveSheet().setFrozenRows(1);
}

Leave a comment

Creating G Suite Add-ons with Apps Script.

I’ve been working on a project to build a G Suite Add-on. This category of software is written in JavaScript and runs within Google’s infrastructure. It’s essentially serverless – you write your scripts, Google runs them, and you don’t think about the compute.

It has it’s own flavor of JavaScript execution environment and a set of APIs for communicating with and extending G-Suite, collectively called Apps Script.

A couple interesting things about it:

CLASP (Command Line Apps Script Projects) is useful and recommended when you’re ready to “get serious” and not just write simple scripts. It lets you:

Interesting things about the Apps Script APIs and execution environment:

Maintaining State

In most business applications, you want to maintain some sort of state, such as a user’s auth token to a 3rd party service, their preferences, etc. Any add-on that talks to a 3rd party service might want this. Until today, I was using an additional “Meta” sheet within a Google Sheet to maintain some state. But I thought there must be a better way and found that there is – the Properties service. This lets you store key/value pairs in a few different contexts:

Leave a comment

Forcing HTTPS on WordPress with Apache

This site lives on a long-lived Dreamhost account, and until a couple months ago, like many older sites, had been happily humming along without SSL setup.

That’s not really a big deal for a public-facing blog site, or so the old thinking went. But since I do login to update content once in a while, it makes sense to get it up to date with current security best practices and not send passwords in the clear, so…

There’s many options on how to do this, and I don’t reach for Dreamhost these days for new web projects, but since it ain’t broke… I went hunting on how to do that on Dreamhost. A couple months ago I did the first half of this project, and figured out how to set up Let’s Encrypt for a website in the Dreamhost admin panel – too easy. Worked right away… But one problem, no redirect of HTTP to HTTPS, so the old insecure site continued to work just fine. Also my custom web font wasn’t rendering correctly.

Then I just forgot all about it, until today. I’m planning to blog here again, so I took a look and fixed the issues.

First, my web font wasn’t rendering because I had a “http://” url referring to the web font, and the browser was refusing to load insecure content. A quick edit of the header php file for my theme to make that a “https://” url fixed that up.

Second, forcing HTTPS. From doing this many times, I know this means redirecting from HTTP to HTTPS. My WordPress site is hosted on Dreamhost and runs within Apache, so I was searching around (much of professional software development is having good internet search skills). First, I searched “wordpress redirect to https” and found “How to Redirect HTTP to HTTPS in WordPress“, which mentions how to do it on Kinsta (a different hosting provider, not applicable), Nginx (not applicable), Apache (applicable!), and via a WordPress plugin (applicable!). Since Apache is lower level than WordPress itself, this is more desirable (can’t get blown away by an update or conflict with other WordPress plugins.) Apache it is, which involves editing a .htaccess file. Then I searched “wordpress dreamhost .htaccess” and found “Force your site to load securely with an .htaccess file” on the Dreamhost knowledge base, which show how to find where on the server file system to find this file for a particular Dreamhost site, and what to put in it – same code as the Kinsta article.

The magical .htaccess incantation?

RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]

Looks like it turns the rewrite engine on, runs a rule if the HTTPS condition is not on, and creates a rule to return a 301 (Moved Permanently) redirect for any request to the https:// equivalent.

Then I remembered I used to have SSH access to Dreamhost set up. Did it still work? Yes, it did! Was the file where the help article said it woud be? It was. Edited it. Tested it with curl:

$ curl -i http://synthesis.sbecker.net/

HTTP/1.1 301 Moved Permanently
Date: Tue, 14 Jul 2020 05:31:58 GMT
Server: Apache
Location: https://synthesis.sbecker.net/
Cache-Control: max-age=600
Expires: Tue, 14 Jul 2020 05:41:58 GMT
Content-Length: 238
Content-Type: text/html; charset=iso-8859-1

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>301 Moved Permanently</title>
</head><body>
<h1>Moved Permanently</h1>
<p>The document has moved <a href="https://synthesis.sbecker.net/">here</a>.</p>
</body></html>

Works!


Sidenote – The above is a first go at a new format I’m going to try for a bit. I needed a place to put this writing, and this old dusty blog already existed, so I’m reviving it.

I plan to write about / document something I learned once a day or so (with exceptions for trips, vacations, needed breaks, etc.). It can be the simplest, stupidest thing, as long as I learned anything at all.

I’m going to commit to this for at least one week.

Leave a comment

Exploring graph databases and Neo4j

Recently I started learning about graph databases and Neo4j specifically.

In a graph database, you have two main things:

Nodes and relationships can each have properties – pieces of data about them – what type of node or relationship it is, and any other data about it – like it’s name, when it was created, etc. In a relational SQL database, to store relationships you need to add columns in your tables to track foreign keys, indexes, etc. In other words, you need to pre-think things out and get your data into a shape that fits in tables of columns and rows, and define what kinds of things belong to or have many other things. In a graph database, you just start adding nodes, then add relationships from one thing to another. Just interconnected things.

I decided to play around with Neo4j and get a feel for how it works.

I used the instructions on Digital Ocean’s site for installing Neo4j on Ubuntu.

 wget -O - http://debian.neo4j.org/neotechnology.gpg.key | apt-key add -
 echo 'deb http://debian.neo4j.org/repo stable/' > /etc/apt/sources.list.d/neo4j.list
 apt-get update
 apt-get install neo4j

Now Neo4j is running. The HTTP api is available on port 7474.

We can send some requests to the api and see how it responds.

Some facts I’ve gathered:

Every request, when using curl, looks basically like this:

$ curl \
  -H "Accept: application/json; charset=UTF-8" \
  -H "Content-Type: application/json" \
  -X POST \
  http://localhost:7474/db/data/cypher \
  -d '{...some json...}'

That JSON generally follows this format:

{
  "query": "...SOME CYPHER HERE...",
  "params": {
    "key1": "value1",
    "key2": "value2",
  }
}

The values in the params key/value hash get substituted for names in the query. Name substitution is a nice built in feature.

The exception to this JSON format, atleast in the tutorial I followed, was when creating relationships. For that, you post to a different URL and use a slightly different syntax.

POST http://localhost:7474/db/data/node/0/relationships (url of first node’s relationships resource)

Request Body JSON:

{
  "to": "http://localhost:7474/db/data/node/1", // url to another node
  "type": "Comes Before" // name you've given this type of relationship
}

You can also create relationships with the Cypher query language.

Creating a node of type Person with a “name” attribute:

Cypher:

CREATE (n:Person { name : {name} }) RETURN n

Params:

name: "John"

Full on JSON:

{
  "query" : "CREATE (n:Person { name : {name} }) RETURN n",
  "params" : {
    "name" : "John"
  }
}

The API gives you a lot of data back:

{
  "columns": [
    "n"
  ],
  "data": [
    [
      {
        "outgoing_relationships": "http://localhost:7474/db/data/node/0/relationships/out",
        "labels": "http://localhost:7474/db/data/node/0/labels",
        "data": {
          "name": "John"
        },
        "traverse": "http://localhost:7474/db/data/node/0/traverse/{returnType}",
        "all_typed_relationships": "http://localhost:7474/db/data/node/0/relationships/all/{-list|&|types}",
        "self": "http://localhost:7474/db/data/node/0",
        "property": "http://localhost:7474/db/data/node/0/properties/{key}",
        "properties": "http://localhost:7474/db/data/node/0/properties",
        "outgoing_typed_relationships": "http://localhost:7474/db/data/node/0/relationships/out/{-list|&|types}",
        "incoming_relationships": "http://localhost:7474/db/data/node/0/relationships/in",
        "extensions": {},
        "create_relationship": "http://localhost:7474/db/data/node/0/relationships",
        "paged_traverse": "http://localhost:7474/db/data/node/0/paged/traverse/{returnType}{?pageSize,leaseTime}",
        "all_relationships": "http://localhost:7474/db/data/node/0/relationships/all",
        "incoming_typed_relationships": "http://localhost:7474/db/data/node/0/relationships/in/{-list|&|types}",
        "metadata": {
          "id": 0,
          "labels": [
            "Person"
          ]
        }
      }
    ]
  ]
}

Creating a second person:

{
  "query" : "CREATE (n:Person { name : {name} }) RETURN n",
  "params" : {
    "name" : "Susan"
  }
}

Create a relationship between them:

Assume the John node has a url of

http://localhost:7474/db/data/node/0

And the Susan node has a URL of

http://localhost:7474/db/data/node/1

We want to create a relationship between John and Susan. John loves Susan. That’s the relationship. In Neo4j, relationships have a direction. They go from one node to another. They also have a type. In this relationship, the from node is John, the to node is Susan, and the type is “Loves”. Each node has a set of relationships. This set or collection of relationships is mapped to a rest API.

http://{url-to-node}/relationships

For John’s node, which is node 0, this would be:
http://localhost:7474/db/data/node/0/relationships

Then, to create this relationship:

POST http://localhost:7474/db/data/node/0/relationships

{
  "to": "http://localhost:7474/db/data/node/1",
  "type": "Loves"
}

Neo4j returns a lot more data back:

{
  "extensions" : {
  },
  "start" : "http://localhost:7474/db/data/node/0",
  "property" : "http://localhost:7474/db/data/relationship/1/properties/{key}",
  "self" : "http://localhost:7474/db/data/relationship/1",
  "properties" : "http://localhost:7474/db/data/relationship/1/properties",
  "type" : "Loves",
  "end" : "http://localhost:7474/db/data/node/1",
  "metadata" : {
    "id" : 1,
    "type" : "Loves"
  },
  "data" : {
  }
}

It tells us where the relationship starts, where it ends, any metadata, any extensions, properties, and any other data.

Now, lets do a simple query to figure out who loves who (JSON enclosure left out, just the Cypher query):

MATCH (n)-[r:Loves]->(m) 
RETURN n.name AS from, type(r) AS `->`, m.name AS to

And we get back:

{
  "columns" : [ "FROM", "->", "to" ],
  "data" : [ [ "John", "Loves", "Susan" ] ]
}

Once you’ve got a lot of relationships set up, you can use Cypher to quickly query these. An immediate use case for graph databases is social networks. If you have all this data regarding who is friends with who, and you want to get some stats on that, like how many friends of my friends friends like pizza and live in Spain, and so forth, with a SQL database, this can be a real pain to write – loads of joins, nested queries, and very slow as the size of the network grows. A graph database is optimized for this, so it’s very fast to perform queries that traverse through many levels of relationships – at least it’s purported to be.

Although social networks are the first data structures to jump to mind, graph databases seem well suited to many other types of data as well, so long as the items are interrelated.

Another nice thing – graph databases seem to be more able to easily represent real life scenarios, where data doesn’t easily fit in a two dimensional grid. Have more data? Add nodes. Does that data relate? Add relationships. This includes one off items, special cases, and things which don’t fit into a traditional two-dimensional grid, or a hierarchical tree, or a flat set of key value pairs. Everything can be inter connected in a giant messy hairball of strange and wonderful relationships, just as it is in real life, and that’s ok – a graph database can easily represent that.

Comments Off on Exploring graph databases and Neo4j

Getting Started with Freelance Web Development

A friend of mine recently let me know his current web development job is ending because the company is shutting down. While he is interviewing at other companies, if at all possible, he’d like to start freelancing, but has no idea where to start.

Since I have a fair amount of experience doing freelance and contract web development over the past 8 years, I thought I’d put together some ideas for the currently non-freelancing, yet marketable-skill possessing web developer. If you don’t yet have technical or design chops, this doesn’t apply to you yet. Go back and get those. If you do have the chops, soldier on.

What do you NEED to get started?

Are you ready? All you need to get started is your first client. That, and a few more obvious things like: technical skills, communication skills, the ability to think of creative solutions, manage your own time, and follow through on commitments in a timely manner. No big deal. You have all of those, right? What you might not have is that first client.

Notice what I didn’t say you needed. The following is a list of nice-to-haves that you can work on later once you’re generating a modest amount of cash flow: a website, business cards, a corporate entity. Your initial focus should be on getting client #1. Sure, you can work on the other business admin things, but they shouldn’t be your first priority.

To get started, you should be spending more than 50% of your time finding that first client.

How do you get your first client?

To get a client, you need some leads. A lead in this case, is a person or company that is interested in getting some work done – which could be a brand new website or application, or additional work on an existing project. Start building a list of leads.

Are you already known to friends and colleagues as a skilled web developer? If so, you may have already been contacted at some point by someone asking for help. If so, congrats, you’ve got a lead! Follow up and engage with this person.

What if no one is currently requesting your services? Then, my friend, you need to get proactive. You need to hustle.

How do you hustle?

Let’s look at some definitions of hustle. “To move or act energetically and rapidly.” “To sell, promote, or publicize aggressively or vigorously”. This is the hustle I’m referring to. “Hustle” also has many other definitions, some with illicit connotations, like to sell drugs or stolen goods, or to swindle and deceive people. Just to be clear, that’s not the kind of hustlin’ we’re talking about. This is all about getting more assertive with communicating, making connections, and asking for business. In other words, anything but wasting time.

If you’ve been comfortably working away in a bubble on whatever you’ve been asked to work on for your previous employer, you might not yet have experience with this world. It’s time to get out of the bubble and start talking to people.

Clarify your offering

You need to let people know what services you’re offering, and spread the word that you’re available for work. Before you start initiating contact with people, you should put together some talking points. Start by putting together a list of the kinds of things you can do. Bear in mind your audience when you build this list. Are you talking to technical people, business people, creative people? Know your audience and tailor the list to them. If you plan on going after multiple kinds of people, build multiple lists.

For the technical list – do you have a LinkedIn profile with a list of technical skills and endorsements? If so, you can start there. Work on any open source projects? That’s even better. Flesh it out, but emphasize the things you’re best at. No one is awesome at everything, so don’t depict yourself as a grab bag of every possible technical skill. Pick the things you’re best at and that you actually like doing, and emphasize those. Balance that with what’s actually in demand. Don’t just stop at the keyword. You should have examples you can talk about. “I do HTML5” is not a good example. “I’ve used HTML5 to build responsive designs that look just as great on PCs as they do on tablets and mobile phones” is better. Having numbers is best. How many people use your open source project? How many more visitors (absolutes and percentages) from mobile devices are you getting on your website now that you’ve implemented that new whiz-bang responsive HTML5 design?

For the business list – think of the past projects you worked on. What did you contribute? Did you improve the project? How did you move the needle for the business? Improve sales or conversions in any way? Do any A/B testing that quantifiably improved something? That kind of stuff is extra-awesome. Start collecting some metrics that you can tout.

For the creative list – what kind of unique, innovative projects have you worked on? How did they do things differently? Any unique ways of navigating the site? New paradigms for conveying information? Simplify something previously complex? Did you work with other creative people and help realize their vision? Have visual examples.

Finding people

Now that you’ve got a list of services you can offer, who do you offer it to? Where do you find these people? Many places. Web development is in demand. Here’s some ideas:

  1. Previous Colleagues

    These are some of your best initial connections. They already know you and your abilities. Let them know you’re available for work, should they know of any. They might not right now, but next week a new project may appear on their horizon and you might come up as a potential do-er of said project. They’re also good people to ask to write LinkedIn testimonials for you.

  2. Local Meetup Social Hour

    Do you live in a city? Then there are probably some meetups you can and should attend. I live in Portland, Oregon, where there are technical, business, and creative meetups happening on a daily basis. You can find these on sites like meetup.com, and local sites like calagator.org, which is specific to Portland. Not every group is on meetup.com though. There’s probably a website or three with a calendar of professional events happening in your town. Don’t have a local group that discusses technical/business/creative topic X? Start one! I’ve done this three times. It’s great, and gets everyone coming to you.

    Many times these group meetings are split into two halves. The speaker presentation(s) for the month, and the social mixer component. If you’re looking for work, the social component is your priority. It might be beforehand or afterwards in the lobby, or it might take place at a bar nearby. Many times this is actually more interesting and fun than the scheduled presentation. What you want to do here is simply walk up to or sit by people you don’t already know, say hello and introduce yourself, and ask what they do. Let them know what you do. If there’s any common interest, or it seems like they might be valuable to know in the future, ask for their contact info, like a twitter handle or an email address. When the conversation wanes, say “it was great to meet you,” move on and repeat!

    You don’t want to be wasting time, so don’t just find the person you already know and hang out with them the entire time. Split up and meet new people. And since you spent the time to come here, set a quantifiable goal, such as exchanging contact info with at least five people. If you’re new to doing this, you can start slow and build up to it, but it’s hustle time. Get out there and chat it up. Remember there’s no pressure here. You’re not out to find that one person who’s going to lead to some work, you’re just practicing meeting people, and building your network. Have nice casual conversations and move on. Then follow up with them later with a short email, or an @ reply on Twitter. Get used to doing it regularly so it becomes automatic, and no big deal.

  3. Local Meetup Presentations

    This is a step up from social hour. Do you have some topic you can talk about for 5-15 minutes that would be interesting to the members of some group? It’s a great way to make yourself the central topic of conversation, and get people coming to you afterwards to ask follow up questions. You can also post your slides online, and the video if one was recorded. (Hint: use screencast software to record one yourself.) Posting presentation decks and videos online gets you a lot of mileage over time, and it makes you look like an expert on that topic.

  4. Online Forums

    Google groups is a good one. Meetup.com also. Again I recommend starting local. Find where local people are having discussions online and hang out there. It’s easier to start an initial business relationship if you have the ability to meet up face to face, at least once or twice to get started. So find groups related to what you do, preferably local to your area. Technical groups frequently have job postings from companies or individuals looking for skilled people. Each of these is a lead. If they match up with what you’re offering, email them.

  5. Twitter

    You should have a twitter account where people can find you. Something short that you can tell people while talking, and write on a name tag. You should post there regularly, about what your working on. You can also share links you find interesting. You should follow interesting people and engage with them as well – replying and retweeting. BUT remember, you need to primarily be working. Don’t let Twitter take over your mind. There are some good strategies for this. You can get a BufferApp account and queue it up with tweets to go out at measured, regular time intervals. Timebox 15 minutes a day or so to review what others have said, and retweet interesting things. But limit your time here. You need to be producing, not consuming.

  6. Blog

    It would be good to get one of these if you don’t yet have one. As a technical person, you might view this as a technical project that you need to build from scratch in the new cool language and framework du jour. Nope. Don’t do that! You’re a business person now, who makes smart decisions about the value of their time. Unless you’re in the business of building a better blog engine, or someone is paying you to do it (and even then), working on building a blog engine when you need to get client work is like not working at all.

    Just get a standard WordPress blog started. They’ve already solved the blog problem, and there’s a huge thriving ecosystem behind it. So many useful plugins and themes, and constant security updates. Host it somewhere you don’t even have to think about the hosting. I use Dreamhost. They keep it updated with the latest version automatically. Timebox this. It should take you two hours max to get setup and online. You can tweak it later.

    I also recommend getting your own domain, vs being on the domain of the latest hosted blog platform out there. You’re building your own brand and creating your own content now, not bolstering someone else’s.

    Once it’s setup, link it to your Twitter profile, LinkedIn, etc. Then set a goal to post about one interesting, useful thing a week to start. Make a content plan: a list of potential blog post ideas, and start outlining them. Set aside an hour or two per week to bang it out. Get a friend or two to review it and use the feedback to make adjustments before you show it to the entire world. Schedule the post to go live in the morning of the next day. Schedule a tweet to go out at the same time. When you feel you’ve really knocked a post out of the park, submit it to forums like Hacker News and Reddit, or others more specific to what you’re talking about. Start building an audience.

Have some savings

When you’re first getting started, you won’t have any momentum yet. You need to spend some time getting the ball rolling, and learning how to make things work. There might not be any immediate action, aka money coming in. It’s ideal to have some money saved before you take the plunge into freelance. Like three to six months worth, because it takes time to get started and build momentum. If you don’t have any savings, stay gainfully employed and start doing this on the side so you don’t go broke. Understandably, you might not always have a choice, if for example, your current company is going under. In that case, use your best judgement to decide if now is the right time to go freelance. You want to be thinking rationally, not in a panic about paying the bills.

Get a peer group

If you’re used to being around co-workers and heading out on your own, it can get lonely. You’ll still need a group to talk shop with. Find some people that are doing the same thing. Being around other motivated, successful people in pursuit of similar goals goes a long way towards helping you succeed. If everyone in your current professional peer group is employed by a company, they’re good connections for work, but not necessarily going to be the best motivators. Keep them, but also find some other people who are already freelancing, and go to lunch or out for drinks. Find or start a mastermind group.

Practice

All of the things I’ve listed here are things to do regularly and repeatedly. Your overall goal here is to boost your professional profile and visibility.

A single burst of energy is not going to fill your inbox with potential clients. Instead, pick a few simple things to do on a regular basis, and keep it up.

Make a list for yourself, and set daily and weekly reminders so you don’t forget. Make the list short and simple to start. Like two or three key things to do regularly. Here’s an example list you can use:

  1. Attend one meetup per week. Exchange contact info with 3-5 new people.
     
  2. Write one blog entry, and a few tweets per week. Preferably something meaningful and useful to others.
     
  3. Make personal contact with one to three people per week. Meet someone for lunch, send them an email, reply to them on Twitter, etc.

Don’t give up

Starting freelancing is like starting any other new job. The first six months are a time of intense learning. There’s a lot to learn. It takes time and patience. It doesn’t happen over night. The secret is to just keep at it. Take one day at a time and set short achievable milestones for yourself. There are ton of other topics that can be covered about freelancing, such as pricing and creating proposals. But that comes later. Before you can focus on those things, you have to focus on finding the people who need work done. Network and meet people. Business requires two parties. You are one. Find the other. Have fun!

Comments Off on Getting Started with Freelance Web Development

Checklists and Habits

Lately I’ve become obsessed with checklists – a simple list of everything to complete a certain task. The book “The Checklist Manifesto” by Atul Gawande is a great introduction to the topic, on how checklists reduce defects in medicine, aviation, and architecture, with some dramatic medical stories thrown in for excitement. The basic idea is that humans are fallible, and a simple checklist can help us do things the right way every time, without overlooking something simple or skipping a step. This can be especially helpful in a stressful situation. It’s challenging to remember every step of a simple, mundane process when we don’t have a lot of time to think, if we’re distracted, or if we’re forced to multitask.

Although we’re drawn to stories of the brilliant creative genius, or the hero who reacted quickly under pressure, many times what really lies beneath these stories are established routines and engrained habits.

How Companies Learn Your Secrets“, an article by Charles Duhigg is a fascinating look at the analytics and marketing groups inside Target, and how, using existing data of their customer base, they determine within a degree of certainty, whether a female customer is likely to be pregnant. The statistical analysis behind that is interesting, but there’s another half of the same article which explores habit formation and how existing habits can be changed or manipulated. That’s because when a woman is pregnant, it is one of the main times in their lives when they are thrown into unfamiliar territory, and their shopping habits can dramatically change. If Target is there at this critical juncture, the thought is that they can be the new favored store for all kinds of things the expectant mother must buy.

This knowledge of habit manipulation is surely great for companies to use on their customer base in order to generate more sales, but it can also be useful to all of us at the individual level, in order to improve ourselves.

For most of our lives, when we’re not making major changes and just going about our day to day lives, we are operating mostly out of habit. When we operate out of habit, our brains go on autopilot. We’ve done this before, we’ve got it down, and we can do it blindfolded. This is true for everything from our morning shower routine to our commute to work. We don’t have to think much. This is our brain’s way of conserving energy.

A habit has a structure. The structure includes a “cue” – the thing that prompts us to begin the habit, a “routine” – the things we do to complete the habit, and a “reward” – the thing we get for completing the routine. Duhigg calls this “The Habit Loop”.

For example, take washing the dishes. The cue – seeing a pile of dirty dishes. This won’t do. The routine – clear the sink so you can work, start the hot water, put in some dish soap, soak the dirty dishes. Then clean something, rinse, repeat until all are done. The reward – a clean, empty sink, a strainer full of clean dishes. The second reward – if you do it daily, not that many dishes build up, so it gets done fairly quickly. All done until next time.

While washing dishes isn’t my favorite way to spend my time, at this point I do it out of habit, and I find it somewhat calming and relaxing. My brain gets to disengage from the task at hand and think about other things.

It can be very hard to change habits. By their nature, we do them without really thinking about it, and sometimes without even realizing it. One of the best ways the marketers have found to change a habit is to piggy back on to an existing habit.

In the article the example was Febreze. Initially, marketers originally sought to sell the product as an odor eliminator. The problem is, people become used to the smell of their own house and don’t realize it stinks. Ever been to a friends house and smelled an intense, overwhelming smell of dogs or cats and wonder how they stand it?

So, most people saw no need for a product like Febreze. Their houses were clean (or so they appeared) and of course they didn’t want to think of themselves as people whose houses stink.

The marketers found the solution by observing customers who actually used the product. A woman who loved it used it after cleaning every room in her house, as part of the reward – a little spritz of Febreze as the finishing touch. This was the key. Instead of trying to create a new behavior in customers – going around eliminating the odors in their stinky houses, the answer was to pitch Febreze as a product to incorporate into your existing cleaning routine as part of the reward.

How can we use this information to change and improve our personal habits and routines? Maybe there is something you want to start doing regularly, like writing, or exercising. Start by thinking about what existing habits you have, that you like to do or just do without thinking. What causes you to start? What is your reward for completing them? Here lies the answer. You can hijack your existing habits by planning to do a different routine or give yourself a different reward for an existing habit by figuring out the cue that triggers it.

For more info, see “How Habits Work” by the author of the article mentioned above, and the book “The Power of Habit“.

Comments Off on Checklists and Habits