Hack in the Right Direction

March 12, 2014
Hack in the right direction!
For over a decade, I’ve been using the phrase “hacking in the right direction” and, while I’ve explained it several times over the years, I was surprised to discover that I never blogged about it. So, here’s an explanation…

As any experienced coder knows, there’s some code that they are proud of and other code that is just there to get the job done. The latter is often referred to as “a hack” or “hacky code.” While it’s fair to say that coders wish all of their code could be beautiful, the reality of implementation requirements, delivery timelines, and competing demands nearly always force some amount of compromise. This leads many developers to approach projects in one of two ways:

Conditions Approach
  • Sufficient timeline
  • Critical code
  • Shared or peer-reviewed code
Write good code
  • Short timelines
  • Not critical code
  • Short term need
  • Used by a single persion/group
Hack

While it’s not always this black & white, most developers are balancing these two approaches in their head and their code is an amalgam of these two approaches. In OpenMRS, we often contrast “Development” with “Implementation coding,” where the latter tends to involve more hacking to meet time constraints.

How can we leverage these two approaches? Do these approaches have to contradict each other or can we make them complimentary? This is where hacking in the right direction comes in:

Hack in the Right Direction

The next time life intervenes and prevents you from taking the time to generate beautiful code, before you just hack something out, take a moment to consider how you could hack in the right direction. This approach takes your ultimate goal(s) into consideration, but allows for guilt-free compromises.

The fundamental requirement of hacking in the right direction is understanding where you want to be. While a journey of a thousand miles may begin with a single step, it helps to take that step toward the destination.

patient selection widgetHow does this work in real life? I’ll give you an example that came up not too long ago. Imagine that you are asked to make a patient selection widget for an application. It would be pretty natural to have a method like this:

/* Get the selected patient */
public Patient getSelection();

But what if you took 20-30 minutes to review how the patient selection widget will be used in the application and learn that we want to use it for all patient selections, including for report generation where more than one patient may be selected? Then you would probably change your method to something like this:

/* Get the selected patient(s) */
public Patient[] getSelection();

It’s a subtle change, but it could have a dramatic effect on how you write and use your code. In this example, it’s a simple change to the method signature, so worth the effort. The trick is finding these easy wins without over-designing your hack. This approach will allow you to meet tight time constraints while at least building toward a longer-term vision.

A recipe for Hacking in the Right Direction

  1. Spend 20-30 minutes with one or more informed product owners to understand, as best as you can, their longterm vision.
  2. As you hack, look for inexpensive adjustments to your design that will support the longterm vision.

It’s not too hard and the investment is small, even when time constraints are tight. The next time you need to hack, take a moment, and try to hack in the right direction!

Faster resets for the OpenMRS Demo

March 11, 2014

I’ve long been bothered by how long it takes for our demo site (http://demo.openmrs.org) to be reset.  The site goes down for a few minutes each hour as the database is restored and the application restarted.  I was working on the demo site today in the middle of one of these resets and so I decided to look around for a better solution.

Fortunately, Akshay Suryawanshi from Percona (percona.com) recently shared a nifty rename_db script that can rename a MySQL database in a couple of seconds.  So, we put our credentials and host information into a file (don’t forget chmod 600 .my.cnf to protect those credentials):

.my.cnf

[client]
user=openmrs
password=secret
host=127.0.0.1
port=3306

Assuming the credentials and other settings are correct, then this script should show existing databases:

show-databases

#!/bin/bash
mysql --defaults-extra-file=.my.cnf -e "show databases"

Then we take Akshay’s rename_db script, define DEFAULTS=.my.cnf at the beginning, replace the “-h $1” references with --defaults-extra-file=$DEFAULTS, and remove the initial host parameter:

rename_db

Adapted from Akshay Suryawanshi’s blog post, thanks to the generous folks at Percona for sharing.

#!/bin/bash
# Copyright 2013 Percona LLC and/or its affiliates
# http://www.mysqlperformanceblog.com/2013/12/24/renaming-database-schema-mysql/
#
# Changes
# - Uses --defaults-extra-file for credentials, host, and port settings
set -e
DEFAULTS=.my.cnf
if [ -z "$2" ]; then
    echo "rename_db <database> <new_database>"
    echo "(assumes $DEFAULTS defines user, password, host, and port under [client] section)"
    exit 1
fi
db_exists=`mysql --defaults-extra-file=$DEFAULTS -e "show databases like '$2'" -sss`
if [ -n "$db_exists" ]; then
    echo "ERROR: New database already exists $2"
    exit 1
fi
TIMESTAMP=`date +%s`
character_set=`mysql --defaults-extra-file=$DEFAULTS -e "show create database $1\G" -sss | grep ^Create | awk -F'CHARACTER SET ' '{print $2}' | awk '{print $1}'`
TABLES=`mysql --defaults-extra-file=$DEFAULTS -e "select TABLE_NAME from information_schema.tables where table_schema='$1' and TABLE_TYPE='BASE TABLE'" -sss`
STATUS=$?
if [ "$STATUS" != 0 ] || [ -z "$TABLES" ]; then
    echo "Error retrieving tables from $1"
    exit 1
fi
echo "create database $2 DEFAULT CHARACTER SET $character_set"
mysql --defaults-extra-file=$DEFAULTS -e "create database $2 DEFAULT CHARACTER SET $character_set"
TRIGGERS=`mysql --defaults-extra-file=$DEFAULTS $1 -e "show triggers\G" | grep Trigger: | awk '{print $2}'`
VIEWS=`mysql --defaults-extra-file=$DEFAULTS -e "select TABLE_NAME from information_schema.tables where table_schema='$1' and TABLE_TYPE='VIEW'" -sss`
if [ -n "$VIEWS" ]; then
    mysqldump --defaults-extra-file=$DEFAULTS $1 $VIEWS > /tmp/${1}_views${TIMESTAMP}.dump
fi
mysqldump --defaults-extra-file=$DEFAULTS $1 -d -t -R -E > /tmp/${1}_triggers${TIMESTAMP}.dump
for TRIGGER in $TRIGGERS; do
    echo "drop trigger $TRIGGER"
    mysql --defaults-extra-file=$DEFAULTS $1 -e "drop trigger $TRIGGER"
done
for TABLE in $TABLES; do
    echo "rename table $1.$TABLE to $2.$TABLE"
    mysql --defaults-extra-file=$DEFAULTS $1 -e "SET FOREIGN_KEY_CHECKS=0; rename table $1.$TABLE to $2.$TABLE"
done
if [ -n "$VIEWS" ]; then
    echo "loading views"
    mysql --defaults-extra-file=$DEFAULTS $2 < /tmp/${1}_views${TIMESTAMP}.dump
fi
echo "loading triggers, routines and events"
mysql --defaults-extra-file=$DEFAULTS $2 < /tmp/${1}_triggers${TIMESTAMP}.dump
TABLES=`mysql --defaults-extra-file=$DEFAULTS -e "select TABLE_NAME from information_schema.tables where table_schema='$1' and TABLE_TYPE='BASE TABLE'" -sss`
if [ -z "$TABLES" ]; then
    echo "Dropping database $1"
    mysql --defaults-extra-file=$DEFAULTS $1 -e "drop database $1"
fi
if [ `mysql --defaults-extra-file=$DEFAULTS -e "select count(*) from mysql.columns_priv where db='$1'" -sss` -gt 0 ]; then
    COLUMNS_PRIV="    UPDATE mysql.columns_priv set db='$2' WHERE db='$1';"
fi
if [ `mysql --defaults-extra-file=$DEFAULTS -e "select count(*) from mysql.procs_priv where db='$1'" -sss` -gt 0 ]; then
    PROCS_PRIV="    UPDATE mysql.procs_priv set db='$2' WHERE db='$1';"
fi
if [ `mysql --defaults-extra-file=$DEFAULTS -e "select count(*) from mysql.tables_priv where db='$1'" -sss` -gt 0 ]; then
    TABLES_PRIV="    UPDATE mysql.tables_priv set db='$2' WHERE db='$1';"
fi
if [ `mysql --defaults-extra-file=$DEFAULTS -e "select count(*) from mysql.db where db='$1'" -sss` -gt 0 ]; then
    DB_PRIV="    UPDATE mysql.db set db='$2' WHERE db='$1';"
fi
if [ -n "$COLUMNS_PRIV" ] || [ -n "$PROCS_PRIV" ] || [ -n "$TABLES_PRIV" ] || [ -n "$DB_PRIV" ]; then
    echo "IF YOU WANT TO RENAME the GRANTS YOU NEED TO RUN ALL OUTPUT BELOW:"
    if [ -n "$COLUMNS_PRIV" ]; then echo "$COLUMNS_PRIV"; fi
    if [ -n "$PROCS_PRIV" ]; then echo "$PROCS_PRIV"; fi
    if [ -n "$TABLES_PRIV" ]; then echo "$TABLES_PRIV"; fi
    if [ -n "$DB_PRIV" ]; then echo "$DB_PRIV"; fi
    echo "    flush privileges;"
fi

We use the following script to backup the OpenMRS database:

backup-openmrs

#!/bin/bash
mysqldump --defaults-extra-file=.my.cnf --add-drop-database --extended-insert \
    --single-transaction openmrs > openmrs.sql

Then we create the following scripts:

drop-openmrs

#!/bin/bash
mysql --defaults-extra-file=.my.cnf -e "drop database openmrs"

restore-openmrs1

#!/bin/bash
mysql --defaults-extra-file=.my.cnf \
    -e "create database openmrs1 DEFAULT CHARACTER SET utf8"
mysql --defaults-extra-file=.my.cnf openmrs1 < openmrs.sql

replace-openmrs

#!/bin/bash
./drop-openmrs
./rename_db openmrs1 openmrs

Now, when we are preparing to reset the openmrs database, we can execute the restore-openmrs1 script, which will place a fresh copy of the default OpenMRS data into the openmrs1 database (restoring the database can take several seconds, but since we are doing this into openmrs1 and the demo is using openmrs, we can perform this step before involving the demo site). Then, when we are ready to reset the database, simply executing replace-openmrs will reset the openmrs database in 2-3 seconds or less. Given that the demo data is a non-production system, we could even perform this data replacement without restarting the demo application.

Now, instead of taking 30-90 seconds or more, the OpenMRS Demo can be reset in 2-3 seconds or less.

Thank you Akshay Suryawanshi and the folks at Percona for sharing!

Fun with HTTP

January 9, 2014

Playing around with cURL and learning about some handy online tools…

requestbin

 

 

Visit requestb.in and you’ve instantly got a link against which you can post data and see the results.

When posting a text file with curl, I usually remember the -H “Content-type: text/plain”, but I try -d @filename.txt and get frustrated before I rediscover –data-binary @filename.txt.

CLBIN

clbin.com is a nifty tool for directing command line output straight to a short url.

IRCCloud Last Read Bookmarklet

January 3, 2014

IRCCloud Last Read Bookmarklet

If you are like me and participate in several IRC channels but don’t get to be in them all the time and sometime are away from them for several days, then IRCCloud is the perfect solution (a web-based IRC client that rivals any other and awesome mobile clients).  While IRCCloud makes it very easy to load a chunk of history (simply scrolling up or clicking on a bar), loading the history of a busy IRC channel for several days or weeks that can span multiple chunks of history is cumbersome.  I’ve bugged James a few times to ask for a click-once-to-load-all-history-back-to-last-read-message link.  When I recently suggested it again in the #feedback channel, I got some javascript tips from James that allowed me to do the next best thing: make a bookmarklet. Simply drag this link to your bookmark bar:

When you are using IRCCloud and enter a channel that you haven’t visited in a while (your last read message is several hours or days ago), click the bookmarklet and it will load history until it reaches your last read message.  In the rare case that you’ve been away for weeks or months and it takes more than a minute to load all the history you’ve missed, the bookmarklet will stop after a minute; just click it again to keep loading.

OpenMRS SDK

October 24, 2013

I’m very excited about the OpenMRS SDK and what it can mean for the OpenMRS Developer Community.

Chris Niesel (h3llborn) brought this beautiful creature to life during Google Summer of Code 2013 under the mentorship of Rafał Korytkowski.  Strong work, guys!  Looking forward to watching the SDK grow into both the first thing a new OpenMRS Developer touches and an invaluable tool for daily development of our most experienced developers!

Open Source: Focus on the Source, not the Code

September 19, 2013

I Am The SourceAs I’ve mentioned previously, if there’s one thing I’ve learned working with OpenMRS, it’s that the value is not in the code; it’s in the people.

In a recent branding discussion at Regenstrief, someone suggested we adopt the tag line
“The Source for Biomedical Informatics.”
I didn’t care for the tag line at all for a few reasons: (1) it’s imperious, (2) it focuses on the code, and (3) it’s seems counter to our mission to be a leader in open collaboration.  Later on in the discussion, the tag line was placed on a t-shirt and changed to “I am the source for biomedical informatics.”  While we ended up going with a different tag line for Regenstrief, I found the t-shirt provocative.  First of all, I liked the twist of saying “I am the source for biomedical informatics,” since it was less high & mighty;  but more importantly, those words caught me: “I am the source.”

“I am the source.”

There’s something hidden in that phrase.

Aha!  That was what I had been missing and it was there – right in front of me – the whole time.  All of this time that I’ve been struggling with the term “Open Source” focusing too much on the code, I didn’t realize that the “Source” wasn’t referring to the code at all; rather, the person creating the code.  Code is code.  The “Source” is the developer.

Code is code.
The “Source” is
the developer.

Now I look at the term “Open Source” differently.  No longer do I think “Open Source (Code).”  Now, I think “Open (Person who creates code).”  When I see the term “Source Code,” I no longer think “code that is the source for the application;” I think “code from a source – i.e., code from a developer.” This solves two problems for me.  First, it helps put the emphasis on getting people to behave openly  – obviously, an open coder is someone who is willing to share her code.  Secondly, it underscores that the real value is in the source – the people – and not the code.  Code can be thrown away, refactored, and easily replaced.  People cannot.

Love your coders.  Make them behave openly.

They are the source.

Open Behavior

Too often in open source, people focus on the code instead of being open. In a recent discussion with Paul, trying to clarify our thoughts on “open source” and realized that we now see open source as more of an approach than about simply sharing code. When we chose the topic for an “open-focused” Regenstrief conference, we chose “Open methodologies.”  That term is closer to what we value.

When we reviewed our values, they included:

These ideas lead to some thoughts about which behaviors or consistent with these values vs. behaviors that work against them:

 

Open Behaviors

  • Going out of your way to be transparent – e.g., when someone sends an e-mail that belongs on a community mailing list to the mailing list, replying to the list.
  • Committing code first, making it “perfect” later.
  • Thinking long-term by planning for and investing the additional (often nine times) effort needed to collaborate & re-use code.
Closed Behaviors

  • “Reply All” to an off-list e-mail that belongs on a community mailing list (instead of replying on list).
  • “My code isn’t ready to be shared yet.”
  • The engineer’s creed: “I can do it better.”

What would happen if all of your cells became self-aware?

June 6, 2013

What would happen if all of your cells became self-aware? Would each movement of your arms be made by committee? Would you fall into an anarchic pool of slush on the floor? Would there be a funeral for every skin & intestinal cell that perished… and, if so, which cells would attend? Would your kidneys go on strike? Would your liver invade your gallbladder?

Now think of yourself as a cell and the entire human race as the organism.

What is your role in the body human?

Spamming for the Lord

April 3, 2013

The spammers are getting more creative. Just got this from Teresa Albert (teresa.albert503@yahoo.com)…

Hello dear,

With due respect and humility am writing you. Though i know this email will come to you as a surprise and i simply ask that you follow your heart before you respond to what i am about to say.

I have been informed by my Oncologist that I would not Last for the next two week due to an internal illness that has been going on for seven years. I have been down with Ovarian Cancer but what bothers me is my stroke. Due to my medical problem, i have decided to donate my life savings to the service of humanity, hence i am looking for someone of integrity who will help carry out my last wishes the way i will advise herein. I took this decision to contact you because I don’t want my husband’s wish to be wasted by unbelievers or someone not to be trusted.

I am not afraid of death hence I know where I am going. I know that I am going to be in the bosom of the Lord. Exodus 14 VS 14 says that the lord will fight my case and I shall hold my peace. Please let me know you can handle this evangelism so that i will provide you more information. Whoever that wants to serve the Lord must serve him in spirit and truth. Please always be prayerful all through your life. If you can handle this, get back to me immediately with your following contacts and detail informations.

Full name:
Address:
Phone Number:
Occupation:

Hope to hear from you.
Remain blessed in the name of the Lord,
From: Mrs. Teresa Albert

In short, “I’m about to die and don’t want to leave this world without first sending out some spam.”

Alfred Workflows

If you own a Mac and haven’t discovered Alfred 2 yet, well I’m sorry.  Combined with its PowerPack, it’s a formidable tool for doing things quickly on the Mac.  I’ve used QuickSilver and LaunchBar.  I held out for a long time as a staunch LaunchBar user, but recently made the switch to Alfred 2 and I’ve got it doing anything everything I need (and boy do I need a lot… I’m lazy and I’ll love keyboard shortcuts).  The only shortcoming with Alfred is its clipboard history, which is limited to text (no images… bummer).  LaunchBar does the clipboard history just right, so I map Alt+Cmd+K to LaunchBar’s clipboard history and I’m good to go.  Sure, LaunchBar is an expensive tool just for clipboard history, but I already owned it and why use Alfred’s crappy clipboard history when I have access to LaunchBar? 🙂

Anyway, I thought I’d share a few of my (now ~24) Alfred 2 workflows for anyone interested:

icon Arrows
Easily type arrow characters (↑, ↓, ←, →) using HTML entity names: uarr, darr, larr, and rarr.
IRC Cloud IRC Cloud
Open IRC Cloud in Chrome with irc.  If you’ve already got IRC Cloud in a tab, it doesn’t open it in a second tab.
icon OpenMRS
A workflow for OpenMRS developers with a few handy shortcuts.

  • op — launches the OpenMRS wiki
  • ot [ticket] — launches OpenMRS JIRA. Naming a ticket goes straight to that ticket; giving just a number will open the corresponding TRUNK ticket; leaving off the ticket just takes you to JIRA.
  • dev — opens the OpenMRS Developers Forum page
  • raf — types Rafał’s name properly into your editor (and into your clipboard in case you need it again)
  • paul – pastes “Paul is a dork” into your editor
icon Volume Controls
Provides some shortcuts for controlling your volume. You thought you were lazy? I know there are dedicated keys for this, but they’re way over there. Alfred shortcuts can be habituated (once learned, you just think and it happens) and don’t require your hands to leave the “home row” of the keyboard.

  • vl — volume low (about one “dot” of volume on the Mac)
  • vh — volume high (crank it up!)
  • mute ‐ guess what this does. 🙂