Mark's Blog http://wiscospike.posterous.com This site is just a mirror of my actual blog: markliu.me posterous.com Tue, 08 Jan 2013 13:53:12 -0800 How to find a good freelance developer http://wiscospike.posterous.com/how-to-find-a-good-freelance-developer http://wiscospike.posterous.com/how-to-find-a-good-freelance-developer A bunch of people have asked me recently how to find good contract developers. I’ve done this quite a few times now and feel like I now have a pretty good system for selecting quality freelancers at good fixed prices.

I can tell that this system works because on my last two contract projects I received an average of 100 bids per posting (spread across 3 different sites) and both hires ended up blowing me away with the quality, speed, and price of their work. I have viewed many friends’ postings that have received 10 or fewer bids, many of which were low quality. I’ll take you through what I do to give myself better developer options.

Step 1

Write a great specification of the work you want done. There are a lot of reasons why this is important.

First, the best freelance developers are more likely to place bids if they know the scope of the work. When they don’t know the scope of the work it becomes dangerous for them to place a fixed price bid because they might end up doing far more work than they had hoped. These developers care a lot about their aggregate ratings on these freelance sites and will make sure they do everything in their power to make their customer happy and get a top rating. If you lay out a detailed specification, they know exactly what it will take and are no longer scared that you have a list of features up your sleeve that you want them to implement as well.

Basically a great specification will tell the potential hire that you know what you’re doing and that you will be pleasant to work with.

Second, you will need to define all of these features at some point anyway so you might as well do it right up front in order to gain the benefit of finding the best possible developer. It’s a good exercise to fully define your product as you will find tricky parts you hadn’t thought of while simply thinking about your product at a high level. The earlier you think about and address these tricky bits, the less costly it will be to implement well.

Here’s an example of a spec I wrote recently that received 110 bids, at least 8 of which were very high quality: https://docs.google.com/document/d/15c6n6HAUoa5SDP7TlGU_sljpdIA_8doe3h2Kbi9VcoM/edit

Step 2

Post your spec to elance.com without setting a price. They allow you to not specify your budget or expected price. By posting it without a price you can wait for the bids to roll in and you’ll start to get a good idea of how much this job should cost.

Step 3

Take the expected cost determined in step two and post the same spec to odesk and freelancer.com. There are plenty of other freelance sites out there so you are free to post to those as well. I have had great luck with those three sites (elance, odesk, and freelancer) and can confidently say there are some very high quality developers on each of these sites who will come out of the woodwork if your spec is good.

Step 4

Wait. It’s tempting to hire the first competent person who submits a good bid, but you will get plenty more if you wait. Be patient and wait for the bids to start slowing down. 2-3 days should be enough.

Step 5

Narrow down the bids to find the right developer. This can seem overwhelming at first but if you do it systematically you can do it in about 2-4 hours. Here’s what I do:
  • Narrow down the field based on their bid price, their quantitative ratings, and the amount of work they’ve done in the past. After keeping only people with 4.9+ ratings, a significant amount of work, and a very good bid price the field usually narrows to about 10.
  • Briefly glance at each of the top 10 applicants’ portfolios and take a look at their communication skills in their messages with you or on their profile page. Eliminate all but the very best. Hopefully you’ll be down to your last 2-4 people now.
  • Choosing from these last few is the toughest step and for this I tend to choose the person I think would make the best long term hire. English speaking ability and the general vibe I get from the person play a big role here. Of course this is just a contract project, but while working with this person you will build up a relationship and if they produce high quality work the chances are you will want to hire them for work in the future as well.

Final Note

If your project is big you may want to break it up into small pieces. For instance you can write the spec for just a small part of your application so if you end up hiring someone who produces low quality work, you won't lose much because you haven't invested much. I view the first job I award someone as an interview to some extent. If you like that person you can always hire him/her for the rest of your project.

Good luck!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Thu, 01 Nov 2012 15:16:16 -0700 Why are you doing this? http://wiscospike.posterous.com/why-are-you-doing-this http://wiscospike.posterous.com/why-are-you-doing-this One of the mentors at The Iron Yard this summer asked the question "Why are you doing this?". I love this question because without a really good reason for doing this, building a company becomes infinitely harder. Recently I had one of my most grueling days since I started Leaguevine when I spent the entire day on the phone making sales to people who mostly didn't give a crap. I don't like sales. But I could force myself to work hard and get stuff done because I have a very clear end goal.

I want to have a huge, positive impact on the world through international development and to do so requires a large amount of money. This is at the top of my mind every day and has been sitting there for about two years.

When I was devoting a vast majority of my energy to Engineers Without Borders from mid 2008 through 2010, my view on the world changed completely. Before EWB, I would have been content with a comfortable life where I was able to travel a lot and have some fun. I could work for a great employer, produce at a high level, get paid a lot, and do the things I wanted on nights and weekends. 

EWB killed this former dream life for me. I am certain that I would no longer be content with such a life. 

After my first trip to Orongo, Kenya and seeing all of the challenges people have to go through every single day, I returned home grateful for running water. For a full year after that first trip I would think about how lucky I am to have running water every time I turned on a faucet. 

On my second trip I was given a grand tour of Kibera by one of its residents. Kibera is one of the largest slums in the world and I was walking through it shortly after it had rained. Walking through this town was excruciatingly difficult because all roads and pathways were dirt and because many of these pathways were steep and it had rained, it was really tough walking around. I remember Cartoon (my friend in Kibera) reaching down and extending me a hand to help me climb various pathways. Not surprisingly, I thought about Kibera at least once a day for the entire calendar year of 2009.

By the fourth time I traveled to Orongo, Kenya I realized how much I loved doing this International Development work. Every day I was using my knowledge, connections, and hustle to make the most change I could in this charming little community. Despite loving this work, I realized how many parts of this system were broken. I don't mean to say EWB or other NGOs are bad, but the entire state of International Development could use a heap of improvement. Because of this, I would not be content being a cog in this big International Development machine where I would likely have very little meaningful impact over a lifetime of hard work.

At this point, because I was no longer content with a simple software developer's life and I would not be content working for an NGO in this International Development space, the path to reaching my dreams started to become somewhat clear. I would have to do something that gave me a chance to make a huge amount of money which I could then use to instill my own vision into the International Development scene. 

I like to think of this as the Bill Gates model of doing good in the world. Step 1 is to make a lot of money, and step 2 is to use the money to do something incredible. Gates is doing far more than just donating his money to charity. He is aggressively attacking the world's problems with the same gusto that he used to build Microsoft by defining his own well-rounded initiatives and finding the right partners to carry out his vision. He's crushing it. He is not conforming to standard NGO models, and is willing to disrupt this space and do whatever it takes to improve the world. And for that he's my idol.

While it's unrealistic to expect to make an amount of money and impact on the same order of magnitude that Gates did, this general model for living my life is well within the realm of possibility. This is what gets me fired up every morning. And more importantly, it's what keeps me going at full speed when things look bleak.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Wed, 15 Aug 2012 14:49:07 -0700 Fixing tech startup incorporation documents http://wiscospike.posterous.com/fixing-tech-startup-incorporation-documents http://wiscospike.posterous.com/fixing-tech-startup-incorporation-documents A year ago I incorporated Leaguevine myself without using any lawyers. I wrote about it here and heavily relied upon the Orrick company document templates. I realized back then that there was a good chance I would screw some stuff up and decided it would be better for Leaguevine to first get some funding or free access to lawyers and at that point revise anything I messed up.

Turns out this approach worked great.

I am currently attending The Iron Yard accelerator program in Greenville, SC and this program gave us not only funding and connections, but also free legal services from a quality law firm called Wyche. Our lawyer there took care of all the amendments where necessary for free and now we have a great set of documents. He told us that the templates that we used were excellent and far better than a lot of the templates he sees self-incorporators use. So I'm glad we used the Orrick templates and saved him a lot of work.

I'm really happy I decided not to pay for legal services at the very start of this company.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Sun, 05 Feb 2012 13:14:57 -0800 Python syntax highlighting in Django templates using Pygments http://wiscospike.posterous.com/python-syntax-highlighting-in-django-template http://wiscospike.posterous.com/python-syntax-highlighting-in-django-template
Highlighting code to be placed on your site is a pretty common need that has been addressed nicely by a number of open source projects. Pygments is great, but on it's own would take you a while to get set up. This guide is meant to get you set up with using pygments in your django templates in under 5 minutes.

Choose your syntax highlighting style

First, decide which pygments style you want to use by trying them out on the pygments site.

Install django-pygments

There's a nice project on github that gives you a couple template tags for using pygments.

Create a pygments css file

Using whichever  style you liked from the pygments site, you can use the pygmentize command in your terminal to generate a CSS file for marking up your code.

pygmentize -S vs -f html > vs.css

Mark up your django templates

You're now ready to make the changes to your templates. Just import your css file, load the template tags, wrap your pre-formatted code with the pygment template tag, and then label the code as whatever language you use.

<link href="/media/css/pygments/vs.css" media="screen" rel="stylesheet" type="text/css" />

{% load pygmentify %}
{% pygment %}
<pre lang="python">
import simplejson
import urllib2
</pre>
{% endpygment %}

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Fri, 25 Nov 2011 12:40:18 -0800 Almost 4 months in http://wiscospike.posterous.com/almost-4-months-in http://wiscospike.posterous.com/almost-4-months-in I've been working full time on Leaguevine for almost 4 months now, and things are finally beginning to click for me. It's a completely different lifestyle, and I'm finally accepting the fact that it takes a while to adapt to it. Things like self-motivation, prioritization, setting deadlines, and staying organized are all things that I never had to deal with before since school basically took care of all that for me. These tasks are becoming easier every day, and I wouldn't trade this experience for anything at this point!

A couple nights ago I realized just how much I love what I'm doing. I was hanging out with friends who were back in town for Thanksgiving until 2:30am, and despite having a blast catching up with those guys, all I could think about on my drive home was how pumped I was to finish implementing our oauth2 server for our API. Indeed, I went right to work when I got home (don't worry - I hadn't been drinking with my friends) and worked until I was just too exhausted. I went to bed really happy.

We have yet to release our core product to the world, and won't do so for another few months, but I imagine once all our cool stuff goes live and we have more users, this experience will only get better. 

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Fri, 04 Nov 2011 13:48:35 -0700 Reducing CrashPlan's Disk Space Usage on OSX http://wiscospike.posterous.com/reducing-crashplans-disk-space-usage-on-osx http://wiscospike.posterous.com/reducing-crashplans-disk-space-usage-on-osx I have CrashPlan backing up almost every file on my Hackintosh, and when CrashPlan runs, it caches a ton of data so it can run faster. However, they don't let you configure where that data is stored. In my case, I'm backing up around 1TB of data, and thus the cache is a whopping 8GB. 8GB might not seem like that much these days, but when it's sitting on your small SSD, it's quite annoying. Fixing this problem was pretty easy, and since plenty of people were complaining about this on their forums in different threads, I thought I'd post my simple solution here.

First, you need to have a second drive connected to your system that you'd rather have hold the cache. If you have this, then this solution should work for you:

1. Close CrashPlan, as you normally would.
2. Stop CrashPlan's background service by typing: 

sudo launchctl unload /Library/LaunchDaemons/com.crashplan.engine.plist 
 
3. Remove the Cache:

rm -r /Library/Caches/CrashPlan

4. Create a simlink: 

ln -s /Volumes/OtherDrive/path/to/new/cache/ /Library/Caches/CrashPlan

5. Start CrashPlan's background service: 

sudo launchctl load /Library/LaunchDaemons/com.crashplan.engine.plist

6. Restart CrashPlan

This should free up a whole bunch of space on your startup disk. Enjoy!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Thu, 29 Sep 2011 10:15:23 -0700 Django-Celery on Webfaction using RabbitMQ http://wiscospike.posterous.com/django-celery-on-webfaction-using-rabbitmq http://wiscospike.posterous.com/django-celery-on-webfaction-using-rabbitmq
This tutorial is meant to get you up and running from scratch with django-celery on Webfaction. Each of the steps is a bit of a hassle since you typically need to find different install steps for each individual part, so I just lumped up the whole experience in this guide.

Install Erlang

Erlang is needed for installing RabbitMQ which is the preferred message broker for Celery. Webfaction doesn't come with this installed, so you'll need to do it manually:
  • Go to the webfaction control panel and create a new app -> Custom App, Listening on Port
  • Download the latest version of Erlang. You can just use the command: wget http://www.erlang.org/download/otp_src_R14B03.tar.gz
  • Unzip it: gunzip -c otp_src_R14B03.tar.gz | tar xf -
  • cd into the directory
  • Configure the build: ./configure --prefix=/home/your_webfaction_username/
  • Make it: make
  • Install it: make install
  • Run it on the port given to you when you created the new Erlang app: epmd -port 12345 -daemon
Install RabbitMQ
Next, you need to change the file ~/lib/rabbitmq/rabbitmq-server. I found some information about this on the webfaction community forums. Open your text editor and change three lines to:

CONFIG_FILE=~/src/rabbitmq_server-2.6.1/sbin/
LOG_BASE=~/logs/user/rabbitmq
MNESIA_BASE=~/src/rabbitmq_server-2.6.1/sbin/

Added these lines to the rabbitmq-env file and use the ports you reserved for epmd and rabbitmq in your earlier steps:

export ERL_EPMD_PORT=12708
export RABBITMQ_NODE_PORT=35478
export ERL_INETRC=$HOME/.erl_inetrc

Added the file $HOME/hosts which looks like:

127.0.0.1 localhost.localdomain localhost
::1      localhost6.localdomain6 localhost6
127.0.0.1 web160 web160.webfaction.com

Added the file $HOME/.erl_inetrc which looks like:
 
{hosts_file, "/home/<your_user_name>/hosts"}.
{lookup, [file,native]}.

Run Rabbitmq and check that it is working:

./rabbitmq-server -detached
./rabbitmqctl status

Finally, add a new user and vhost, and configure it so only your app will have access to it.

./rabbitmqctl add_user <username> <password>
./rabbitmqctl set_user_tags <username> administrator
./rabbitmqctl add_vhost <vhostpath>
./rabbitmqctl set_permissions -p <vhostpath> <username> ".*" ".*" ".*"
./rabbitmqctl clear_permissions -p <vhostpath> guest

Install Celery and Django-Celery

pip install django-celery

In your settings file, you will then need to add the lines:

BROKER_HOST = "localhost"
BROKER_PORT = 36784
BROKER_USER = "username"
BROKER_PASSWORD = "password"
BROKER_VHOST = "vhostpath"
CELERYD_CONCURRENCY = 1
CELERYD_NODES="w1"
CELERY_RESULT_BACKEND="amqp"

The reason we set the concurrency so low is because Celery takes up a good amount of memory, and you are likely limited with your memory consumption on webfaction. The minimum amount of memory Celery can take will be however much it needs to run the main process (consuming messages, sending tasks to workers, etc), and a worker tasks that actually does stuff. Each of these will take up about 20-30MB of memory depending on the size of your Django app.

Add 'djcelery' to your installed apps.

Follow any other steps listed in their installation guide that are relevant to your app.  If you are using mod_wsgi, add the following to your .wsgi module:

import os
os.environ["CELERY_LOADER"] = "django"

Install a tool to create a Daemon

Celery does not daemonize itself, and thus you need to do this yourself. Creating a daemon is not exactly the same as simply running it in the background, so you should install a tool that can help you do this. Celery recommends a couple options. One of the easiest ways is to use a simple tool called django-supervisor. To install this, just type: 

pip install django-supervisor

Add the file supervisord.conf in the same directory as manage.py, and add the content:

[program:celeryd]
command={{ PYTHON }} {{ PROJECT_DIR }}/manage.py celeryd -l info

[program:autoreload]
exclude=true

[program:runserver]
exclude=true

[program:celerybeat]
exclude=true

Every time you restart your webserver, you can restart celery by issuing the following commands:

python manage.py supervisor --daemonize
python manage.py supervisor stop all
python manage.py supervisor start all

However, there is a downside with using django-supervisor in that it will run in the background and take up another 20-30MB of memory. A more memory efficient way would be to install a tool called daemonize. This page has very easy installation instructions. Once you install it, just add an alias to it in your .bashrc or .profile and then run:

daemonize /<full_path_to_django_directory>/manage.py celeryd

Everything should then be up and running. Good luck!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Sat, 10 Sep 2011 22:53:51 -0700 Self-Incorporating a Tech Startup http://wiscospike.posterous.com/self-incorporating-a-tech-startup http://wiscospike.posterous.com/self-incorporating-a-tech-startup This is in response to my friend Ben's recent post, What about money for a lawyer?

I spent a bit of time last month self-incorporating my tech startup and since this topic is very fresh on my mind, I thought I'd reflect on it for a moment.

The first thing I'd like to reflect on is the actual decision about whether or not to do this myself or hire a lawyer. The decision here comes down to deciding whether or not the additional $1,200-$1,500 in fees is worth the peace of mind that your company is starting off on the right foot in the legal department. Since I am confident that I know what I'm doing and have free resources to monitor I'm not doing anything dumb, the decision to by-pass hiring an attorney was an easy one. And I believe any competent entrepreneur can do the same, if they are willing to give up some time and energy to educate themselves. The trick is to get to the point where you can be confident that you are doing it right without missing any steps. So that's what I'll focus on here.

To figure out what all goes into forming your company, there are more than enough free, reliable, online resources to help educate you. Just use google to find legal articles on this stuff, and then study wikipedia to make sure you understand all of the legal terminology.  A few good blogs for learning about this stuff are Startuplawyer, TechnologyStartupLaw, Startup Company Lawyer, Brad Feld's Blog, and of course the MBA Monday's series on AVC. From reading these sites as well as learning from Quora and Stack Exchange's Onstartups, it was clear to me exactly what details I wanted to bake into incorporating my company. I won't talk about legal specifics here, and I'll just talk about how I learned enough to incorporate everything myself.

So knowing all of the details about type of entity, state of incorporation, par value, vesting agreements, IP rights, etc, is a good start, but the more intimidating part is actually going about drafting and filing the appropriate legal documents to put everything in place. The best way to do this, if possible, is to use free resources to legal assistance. You'd be shocked at how many people out there really want to help you. Two good options for this are:
  1. Free legal clinics. Tons of Universities with Law Schools have these now, and they are aimed at giving law students real world practice with startup law while being supervised by professionals. In Chicago, the top three are the program at Northwestern University, the Institute for Justice Clinic, and the Loyola Law Clinic. A lot of these will have long wait times, so if it's not urgent these could be a great free source of help.
  2. Small Business Development Centers (SBDCs). These will often give you general business advice for free, and have connections to pro-bono legal help or Certified Public Accountants (CPAs) who are plenty knowledgeable about simple legal issues. I went into the SBDC at UIC and was happy to get a lot of help from them. In fact, I still email the CPA now and then if I have a quick question and he always sends me back a clear response in an hour or two.
Once these two options for getting help with filing your documents have been explored, it is worth also checking with lawyers to make sure you are doing this right. Lawyers will all give you a free consultation half an hour or so to learn more about you and attempt to woo you into signing with their practice. I talked on the phone with about 8 lawyers for 30 minutes each, about setting up my corporate entity and drafting other legal documents. At first, I was embarrassed to say I was thinking about incorporating on my own without their help because I thought they would call me naive or say I shouldn't do it. But after the 2nd interview, I realized that these lawyers were actually enthusiastic about the idea of me saving money for my company upfront and they were more than helpful to me in hopes that I would sign on with them when I had "real" legal needs that needed to be addressed such as raising a round of capital. These lawyers would all give me lists of legal documents I needed to file, tips on how to file them and what I should incorporate on them, and told me about any pitfalls or things to watch out for (such as making sure I file my 83(b) within 30 days of incorporation). They assured me that internal documents such as company by-laws can be amended later when you need to, so getting it perfect the first time will not make or break your company. Further, one lawyer even offered to inspect my documents for free after I drafted them to ensure I was doing things right.

So I guess what I learned is that incorporating a business is not hard, and you can learn everything you need by just using Google, calling people, and asking the right questions. Since in my situation I have a lot of time but not a lot of money, this made sense. While all of this information was a bit overwhelming at first, I now feel infinitely more confident with my business since I know the ins and outs of my company's legal structure.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Wed, 31 Aug 2011 19:50:09 -0700 One month down http://wiscospike.posterous.com/one-month-down http://wiscospike.posterous.com/one-month-down It's been one month since I started working on Leaguevine full time, and it's been a very interesting experience. After being in school for 20 straight years, being on my own has been quite a shock. I now set my own schedule and am accountable only to myself. As grand as all the blogs out there make that seem, it's kind of weird actually being in the middle of it.

While I was in school, I would always have my side projects and activities that I actually cared about far more than school. While I spent most of my time on these outside activities, school has always had a constant presence. It has been an anchor and something that brings consistency to all the years of my life, even if my outside activities change. I'd become conditioned to working quickly and efficiently on my schoolwork so I could make time for my passion projects. For the last several months, I kept saying to myself how much I'd love to work solely on Leaguevine instead of having to also do relatively meaningless class work and research. But then August 1st rolled around and I didn't get into the sprint I was hoping for.

I found that during my first few weeks I often became overwhelmed into complacency. Instead of instantly working 80 hour weeks like I had planned, I was putting in 50-60 tops and couldn't really bring myself to do anymore even though I had nothing else to work on. I would take long breaks to watch youtube or play Starcraft II. And it felt like a huge waste of my time. Mostly, I felt bad that I wasn't giving the business my undivided attention.

I was being overwhelmed by a number of different things. First, there were no "real" deadlines in sight anywhere. Every deadline was just lying on a piece of paper or my google calendar and I kew I wouldn't be punished if I missed it by a few hours, days, or even weeks. Next, I had just come back from a fantastic trip to China, and had been living in "consumer" mode. I had to go back to the mindset of "what can I build today" rather than "what can I buy today". I think the thing that had the biggest stress on me was incorporating my business. I talked to about 8 lawyers, a number of non-profit clinics, a CPA, my dad, and an SBDC about getting the company set up right, and all this talking on top of reading blogs about tech startups made me feel like I wasn't getting anywhere. I was spending so much time working on the business plan and worrying about legal stuff, that I was barely writing any code. And this lack of progress on the web app just made me feel like I was falling farther behind and wasting my time.

I'm not sure exactly what happened, but sometime in the last week or so a light went on and I've been fully focused on Leaguevine. I think it's because I finally got back to coding. I've become okay with the notion that this is going to be a long process that will take time, and I'm finally focusing on what I can accomplish each day and just doing that. I've been working 12+ hours a day every day this past week and I'm loving a vast majority of it. Yes, there are times when I have to do some tedious stuff, but the percentage of time in a day where I am truly enjoying what I do is higher than it was while I was in school.

I'm not sure exactly how much this has contributed to this productivity improvement, but since I had been the most lazy in the mornings, I came up with a little trick to make myself more productive right after waking up. Before going to sleep, I'd open up one of the source code files and begin adding a single line of code to it. I don't finish the line, and it doesn't even have to do anything useful, but I then turn off my monitor without even saving the file. As is typical with VI guys, I can't stand files being opened and unsaved, so the first thing I do in the morning is delete that line of code or finish it. Just this one little 3 second action gets me in the mood to develop something cool, and immediately I'm working at full speed the moment I sit down at my computer. This tip goes back to the philosophy of beating procrastination by just starting to do something.

So as this first month comes to a close, I'm working at a productivity level higher than ever before and I'm excited to see how much I can build in September!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Tue, 23 Aug 2011 12:59:36 -0700 Migrating a Django Postgres DB from Concrete Inheritance to Abstract Inheritance http://wiscospike.posterous.com/migrating-a-django-postgres-db-from-concrete http://wiscospike.posterous.com/migrating-a-django-postgres-db-from-concrete Django comes with several ways of implementing model inheritance, and specifying which one you would like to use takes only a line or two of code. Setting up your database for the very first time is extremely easy, but migrating between types after you have existing data in infinitely harder. I have a live site whose needs have changed since I built the database and the concrete inheritance I set up is no longer needed. In my case, the site should be using Abstract Inheritance

There are a number of gotchas in this process, and I will outline exactly how I navigated my way through this process. First, I should note that I use django-south in my project and the migrations rely heavily on using this. I asked for advice on Stackoverflow  and I'll use a similar example here. This article will walk you through the process of migrating from the before to after schema while keeping all your data intact.

Before:

app1/models.py:

    class Model1(base_app.models.BaseModel):
        field1 = models.CharField(max_length=1000)
        field2 = models.CharField(max_length=1000)
    
app2/models.py:

    class Model2(base_app.models.BaseModel):
        field1 = models.CharField(max_length=1000)
        field2 = models.CharField(max_length=1000)
    
base_app/models.py:

    class BaseModel(models.Model):
        user1 = models.ForeignKey(User, related_name="user1")
        user2 = models.ForeignKey(User, related_name="user2")
        another_field = models.CharField(max_length=1000)

        objects = CustomManager()

After:

app1/models.py:

    class Model1(base_app.models.BaseModel):
        field1 = models.CharField(max_length=1000)
        field2 = models.CharField(max_length=1000)

        objects = CustomManager()
    
app2/models.py:

    class Model2(base_app.models.BaseModel):
        field1 = models.CharField(max_length=1000)
        field2 = models.CharField(max_length=1000)

        objects = CustomManager()

    
base_app/models.py:

    class BaseModel(models.Model):
        user1 = models.ForeignKey(User, related_name="%(class)s_user1")
        user2 = models.ForeignKey(User, related_name="%(class)s_user2")
        another_field = models.CharField(max_length=1000)
    
        class Meta:
            abstract = True

These are the models I will use to walk you through this migration process. Essentially what we are doing here is removing the one-to-one relationships between Model1/BaseModel and Model2/BaseModel and instead placing each of the fields of BaseModel into Model1 and Model2 so they actually reside in the tables for Model1 and Model2. The tricky part is migrating all your data while we work through this.

Make a Copy of BaseModel

When you add the "abstract = True" property to the BaseModel (don't do this yet), south will delete the entire table BaseModel. If you don't make a copy of the data in BaseModel, after you add the "abstract = True" property you will have no way of copying the fields in the old BaseModel to the respective new models. Making a copy isn't bad:
  1. Add BaseModelCopy within base_app/models.py that has an identical schema to BaseModel.
  2. Run a schema migration (python manage.py schemamigration base_app --auto; python manage.py migrate base_app).
  3. Run a data migration to copy the existing objects in BaseModel to BaseModelCopy (python manage.py datamigration base_app copy_contents; (then edit the contents of the data migration); python manage.py migrate base_app).
Add a Field on the Child Classes to Store the ID

The way django deals with concrete inheritance is a little interesting when you look closely at it. The Child classes Model1 and Model2 do not have 'id' columns in the database, and their primary_key is actually a column named "basemodel_ptr_id". Thus, the unique id is stored in the corresponding BaseModel object. When you eventually add the "abstract = True" property, south will naturally delete this "basemodel_ptr_id" column and you will lose all references to the original BaseModel object. This is clearly very bad, and we would have no way of recovering those relationships if we did that. Thus, we need to add a field to Model1 and Model2 that stores the id of the corresponding BaseModel object so we can reference it in the data migrations after we move to abstract base classes. Doing this is also straightforward:
  1. Add a field "tmp_id = models.IntegerField()" on both Model1 and Model2.
  2. Run a schema migration on app1 and app2
  3. Run a data migration on app1 and app2 copying the id of the corresponding base model into the "tmp_id" field.
Prepare the BaseModel to be Abstract

There are two things you will need to change in your BaseModel class before you can add the "abstract = True" property. First, you will need to fix the related_name for every ForeignKey field in BaseModel. If you don't do this, there will be multiple tables with ForeignKeys on the User object with the same related_name and this conflict will cause an error. Adding the name of the class to the related name as shown in the "after" state of the database will solve this issue.

The second issue is that you will have to remove any managers on BaseModel. These managers will throw an error and instead need to be bound to the child classes.

Mark the BaseModel as Abstract and Create a New Primary Key

There is yet another gotcha with this step. When we add the property "abstract = True" to BaseModel, after django deletes this model, it will automatically add an "id" field to Model1 and Model2 and it will make this field a primary_key. This is fine if we have no data, but since we do have data, we have no way of specifying what to set these initial "id"s to, and since it is a primary_key and there can't be any overlaps, we simply can't do this. Conveniently, we have this "tmp_id" field full of unique values, and we can use this as our primary_key. We can specify this when we make our migration:
  1. Add the property "abstract = True" on BaseModel.
  2. Add a field "id = models.IntegerField(primary_key=False)" on BaseModel
  3. Change the "tmp_id = models.IntegerField()" on Model1 and Model2 to be "tmp_id = models.IntegerField(primary_key=True).
  4. Run schema migrations on app1, app2, and base_app.
  5. Run a datamigration that copies the relevant data from BaseModelCopy to every corresponding object in Model1 and Model2. To do this, just make use of the "tmp_id" field we created and the new BaseModelCopy class. Make sure this datamigration also copies the "id" field.
Remove all our Temporary Models and Fields

Our database should be near-perfect now and you should be able to run the development server to view your site. Inserts will not work yet because we have not specified a way to auto-increment the primary key of objects when we insert them, but we'll get to that later. Right now, if we see all our data has migrated successfully, we are ready to remove all this temporary stuff we created.
  1. Remove the "tmp_id" columns from Model1 and Model2. 
  2. Change the "id" field on BaseModel to "id = models.IntegerField(primary_key=True)".
  3. Remove the model BaseModelCopy.
  4. Run schema migrations on app1, app2, and base_app.
Build the AutoField Functionality on Your Primary Key

The final gotcha of this process is that Postgres handles the django field models.AutoField() a bit strangely. You can inspect this using pgadmin or whatever gui you have to look at how it adds a "serial" property to the field. Because of this peculiarity, south will not allow you to migrate from an IntegerField() to an AutoField() out of the box. There is a workaround for this migration, but if that seems to messy to you, you can simply handle the auto-increment manually. Handling it manually only takes a couple of lines, but make sure you implement some sort of database locking to ensure you don't run into race conditions.

If you are not using Postgres, you may be able to completely skip this step and instead just change the id field on BaseModel to "id = models.AutoField(primary_key=True)" and run a migration, but I can't confirm this.

Push Changes to your Production Server

I'm assuming you made all of these migrations on local servers and not a production server, so the final step is to make the jump to deploying the changes. We now have our new schema with all the data filled in, and it should be working nicely with our code base on our local server. However, our set of migrations poses some problems for when we migrate. We can see that each set of migrations we did in the steps above are reliant on the full set of migrations having been completed in the previous step. This means the migrations for one app are reliant on the migrations for another app so going through a full set of migrations for a single app would not work. 

To avoid this issue, we could have pushed each individual set of migrations along with the code to the production server right as we generated them. Alternately, we can simply clear out the migration history for each of our apps, dump the local datastore, and then rebuild the production database using these migrations. This is the method I used because I happened to be doing this at 4am when no one users were generating new data on the stie.

So, these are the steps for how to do this:
  1. On your local server, remove all of the migrations from from migrations/ folder for app1, app2, and base_app. The history is no longer important to us because we did not specify a way to do backwards migrations anyway.
  2. On your local server, create initial migrations using south for app1, app2, and base_app, clearing any ghost migrations (python manage.py schemamigration app1 --initial --delete-ghost-migrations).
  3. On your local server, run the migrations. South will tell you that nothing needs to be changed. This is good.
  4. Save all your changes to git or whatever you use.
  5. Dump the local database (pg_dump --no-owner --no-acl -U username postgres_db_dev > postgres_db_dev_dump.psql).
  6. Clone your working repository onto your production machine.
  7. Copy your sql dump to your production machine.
  8. Delete the production db (dropdb -U username postgres_db).
  9. Re-create the db (createdb -U username postgres_db).
  10. Load the new data (psql -U username postgres_db < postgres_db_dev_dump.psql)
Now your production server should look identical to your local_server that you had a working version on. Now that they are synced, you can go back to using whatever regular deployment scripts you use and everything should be just fine.

Conclusion

The methods used in this tutorial are useful if you have a live server with data you don't want to lose, but also have a span of time where you can be confident that users will not be adding new data. If users had entered new data into the production site during this process, their data would be lost during the final deployment to the production server. My site has negligible traffic from 1am-7am when I did this, and the database is small (<50,000 objects of type BaseModel) so I could do this quickly. To overcome this on a medium-high traffic site that uses user generated data, you should really schedule some "read-only" downtime and alert your users of this to ensure they don't enter data that gets overwritten.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Sat, 20 Aug 2011 11:58:29 -0700 Installing libjpeg and PIL on OSX Snow Leopard with Python 2.7 http://wiscospike.posterous.com/installing-libjpeg-and-pil-on-osx-snow-leopar http://wiscospike.posterous.com/installing-libjpeg-and-pil-on-osx-snow-leopar If you found this article through a google search, you've probably already read through tons of articles and forums where people gave advice on how to do this. Here's some examples of what I'm talking about:

It's clearly an annoying problem, and I'm guessing none of those worked for you. While this is clearly an annoying problem and I spent at least a couple hours of head banging myself, I believe most of peoples' issues can be solved by completed a thorough cleanup. If you tried one or more of those approaches above, you probably have libjpeg and PIL folders in all sorts of places on your system that are screwing up stuff. 
To remove the libjpeg files, make sure you remove anything that looks like libjpeg* or jpeglib*. You'll also want to remove all your PIL directories. Some of the most common directories these might be living in are:

/usr/local/include
/usr/local/lib
/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/
/sw/lib
/sw/include
/opt/local/lib
/opt/local/include

Be sure to do this between trying any different installation methods or you will end up wasting a lot of time. 

For me, once I removed all these files, getting libjpeg and PIL to work was actually quite simple:
  1. Download this combo installer and install it with the default settings.
  2. Run "pip install pil"
I hope this helps someone!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Fri, 19 Aug 2011 08:52:25 -0700 Setting up my Hackintosh on my Toshiba Satellite M55-S329 http://wiscospike.posterous.com/setting-up-my-hackintosh-on-my-toshiba-satell http://wiscospike.posterous.com/setting-up-my-hackintosh-on-my-toshiba-satell I'll go through the process of turning my 6 year old Toshiba Satellite into a brand new Hackintosh. So far it's working great! Wifi and all! And best of all, it didn't take much time at all to set up.

First, I have to give credits to the two best sources for this that I found:

Not everything went totally smooth for me, though, so I'll walk through each step in a bit more detail than in those links above.
  1. First, you should download iDeneb 1.4 via torrents. This is an iso image of an OSX installer that works phenomenally. 
  2. Burn this iso image onto a disk. If you're already using OSX like me, use disk utility as explained here.
  3. Partition your harddrive. Since you probably already have windows installed on your Toshiba, you can download the free Partition Wizard which works beautifully. You need to make a fairly large OSX partition. Since you're in windows you won't be able to format this partition as Mac OS Extended Journaled, but you need to create a blank partition and give it a drive name such as "D:\".
  4. Insert the iDeneb disk and restart the computer. Boot from the disk by hitting F12 at startup. 
  5. When you reach the first iDeneb install screen, it asks you to choose which drive you would like to install it on, but doesn't show any drives to choose from. This is because you need to format your blank partition. Simply choose Disk Utility from the utilities drop down, select your blank partition, and erase that partition while choosing Mac OS Extended Journaled. You can then exit the Disk Utility and choose this new partition you created as the place to install osx.
  6. At the screen where it prompts you to install, you need to first click on the "options" or "configuration" button to choose which drivers (kexts) you want installed. Choose exactly the options specified here.
  7. Click install and wait a while. Then just go through the regular startup questionaire.
  8. After it boots up, everything except wifi/ethernet should be working. Download the post-install files from this insanelymac thread using a computer with internet. Also download Intel PRO/Wireless 220BG driver. Place them on a flash drive, and open the folder on your Toshiba.
  9. Install the AppleYukon kext, then the Seatbelt Kext, and finally the Intel Pro/Wireless driver kext that you downloaded from project: camphor. You can use this guide for installing kexts
  10. Reboot and cross your fingers!
The thing to note that I did differently than in those links above is that I used a different method for getting wifi up and working. I didn't use the iwi installer, and instead used the camphor installer.

After using my new hackintosh for a little bit, the biggest shortcoming is that the wifi can connect only to unauthenticated or WEP encrypted networks, not WPA or WPA2. This is a bit inconvenient. Further, since this computer is so old, even though OSX is running smoothly, the hardware limitations make streaming video choppy, and of course the battery life kind of sucks. 

That being said, I now have a fully working hackintosh that I can use for development when I'm away from my desktop. Awesome.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Sun, 14 Aug 2011 10:30:31 -0700 Scheduling Regular Backups on Webfaction http://wiscospike.posterous.com/scheduling-regular-backups-on-webfaction http://wiscospike.posterous.com/scheduling-regular-backups-on-webfaction For Leaguevine, we take backing up our information seriously and have deployed a custom backup system for our database. I figured I would share how we back up our databases here. Note that this sort of backup technique is certainly not a replacement for source control or backing up user uploads, but that's a topic for another day.

First, we have to decide on a schedule for what copies of the database we want to keep, and how long we want to keep them for. For Leaguevine, we decided on the following scheme:
  • Hourly backups for the past week
  • Daily backups for the past month
  • Monthly backups indefinitely
After defining a schedule, creating these backups on webfaction is as easy as creating some folders and specifying commands to periodically dump the database information into those folders. Thus, we have directories for each the hourly, daily, and monthly backups. To dump the data into files within these directories and write the success/failure of these dumps to a log, we create new cron jobs. For example, if we open up our list of cron jobs, we can add a single command to do all this. It might look something like:

30 * * * * /usr/local/pgsql/bin/pg_dump -Ft -U db_username db_name > /path-to-backups/leaguevine/hourly/leaguevine-hourly-`date +\%a\%H`.tar 2>> /path-to-backups/leaguevine/hourly/backups.log && echo "Database backup completed successfully on `date`" >> /path-to-backups/leaguevine/hourly/backups.log

There is a lot going on here. First, we notice that this job will run every hour on the half hour due to the way we defined the cron job.

Next, we specify the database name and username for the PostgreSQL database that Leaguevine uses. For this to run without needing to prompt the user for input, we need to set up the .pgpass file which just takes a second. 

The next thing this command does is specify the folder we want to dump the database to, along with the day of the week and the hour of the day. This works for us because the database dumps from previous weeks will get written over by files of the same name after a week passes. 

The command then writes any errors to a file called backups.log, but if there are no errors it writes a success message that has a date stamp on it to that file.

This single command will create all of the hourly backups for a week without needing any manual maintenance. However, this has the shortcoming of residing on the same server as the production database. Thus, if something happened and all of the data on that server were wiped out, all of the data would be lost. Thus, we need to additionally copy these backups to a different computer regularly. To do this, you can just install another cron job on a different machine as so:

40 * * * * . ~/.ssh-agent; scp -o PreferredAuthentications=publickey username@web160.webfaction.com:/path-to-backups/leaguevine/hourly/leaguevine-hourly-`date +\%a\%H`.tar /local-path-to-backups/leaguevine/hourly/ 2>> /local-path-to-backups/leaguevine/hourly/backups.log && echo "Database backup completed successfully on `date`" >> /local-path-to-backups/leaguevine/hourly/backups.log

This cron job is very similar to the previous one in some ways. First, it runs 10 minutes after every supposed backup should have happened. Next, it uses scp to grab the backup and store it on the local disk. And finally, it handles the logs the same way as before.

The trick here is to set up your .ssh-agent so that scp can run without needing a login. This isn't too hard, and webfaction has good docs for how to use ssh keys.

And that's it! With just a few lines in your cron files, you can have customized automated backups of your important data.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Thu, 04 Aug 2011 20:31:03 -0700 Sanya Beaches and Relaxation http://wiscospike.posterous.com/sanya-beaches-and-relaxation http://wiscospike.posterous.com/sanya-beaches-and-relaxation After Yangshuo, we took a bus to Guilin where we again stayed at Liu Tao’s youth hostel for a night in preparation for our flight to Sanya. This time we paid for our room at the hostel because we would feel bad abusing his generosity towards Couchsurfers. We walked around the town that night, and enjoyed a beautiful atmosphere. Our guide book said Guilin would be overly crowded with tourists, but the city is big enough to hold a lot of people while giving everyone enough space to really enjoy it’s beauty. There were tons of mood lights all along the slow moving river and there were great paths to walk along it. We sat and watched an amazing violin player for a little while, which is probably why I remember the mood of the town being so serene and peaceful.

The next day we flew down to Haikou, a city in Hainan. Hainan is an Island in the south of China that many equate to being “China’s Hawaii”. We flew into Haikou on the north shore of the Island because it was cheaper, but our destination was Sanya so we took a bullet train (cost about $10 each) that got us there in about 2 hours while going 250 km/hr! So cool.

Once in Sanya, we took a couple of busses to try to get to our tiny little beach house where we booked an inexpensive room right on a beach. However, even though we got off at the right place, we couldn’t find the beach house by ourselves and no one there had ever heard of it. We asked the concierge at a resort where it was, and he helped us by talking to the owner of this beach house and she had us wait at the resort while she walked over to meet us. She walked us back to our rooms, and we were very pleased that we decided to stay there for two nights.

I posted a photo from our awesome room in a previous post andmentioned that unfortunately, the beach we were on was not a swimming beach. On the second day of our stay which happened to be my birthday, we went to visit a couple of swimming beaches. The first, Yulong Bay, had gorgeous views and incredibly fine sand. It was exactly what Americans typically picture when they think of a beach in paradise. We splashed around in the big waves and threw a disc around for a while and there were very few people in the ocean. Unfortunately, the only reason this one was so clean and impeccably nice was that the entire coast line of the bay was owned by “6 and 7 star resorts”. We walked through a couple of resorts and one of them had something like 4 outdoor pools with water slides and the works. While these resorts are great for people who want to spend tons of money, they suck for people who are living in budget rooms like Rachael and I because they don’t let you hang out on their stretch of beach. Rachael and I sat down on the chairs of one of the resorts and read our books for a couple hours, not exactly sure what the policy of the resort was. After a while, someone from the resort came and asked us for our room key and then kicked us out. It’s too bad this beautiful beach is only really friendly towards the people who spend tons of money.

Later that same day, we visited a less pretentious and highly recommended beach called Dadonghai. This one was totally free to the public, and the scenery and sand was just as nice as Yalong bay. However, it had none of the peacefulness that Yalong Bay had because it was jam packed with Chinese tourists. That being said, it was really fun playing in the giant waves with the masses of people who were all in great spirits and laughing all the time. At all times, there was another person within 3 meters of us, but I enjoyed the atmosphere here more than the stuffy one of Yalong Bay.

Img_5155


After our two nights at the Papiluo Bluehouse, we decided to pack up and change our location to a youth hostel where the owner speaks English. As nice as our room was, we felt like we were a bit stuck and had no clue how to do anything except visit beaches. We checked into the Raintree Youth Hostel for a four night stay and this turned out to be a great decision.

Soon after checking in, Rachael got sick and then remained very sick through the following day. The hostel was in an area where we could go downstairs and easily find food, and there was enough to do in the hostel that we could take a full day off and not be too bored. One of the staff helped us move a DVD player into our nice AC room where we proceeded to watch a couple movies and read for most of the day.

Rachael felt better after the full day of rest and we went with Justin, the owner of the hostel, and a group of about 8 out to a remote bay for a day of water activities. The bay we went to had gorgeous, clean beaches and our group and one other were the only ones swimming in the entire bay. We swam, rode on a banana boat, tried standing on body boards being pulled by a speed boat, tried wake boarding, and snorkeled for a long time. Unlike my only other snorkeling experience, this one was actually good! I had very few problems with my facemask and I was able to see a ton of cool stuff under the water. This bay was quite undeveloped so we were able to see coral, sponges, anemones, crabs, hermit crabs, fish, urchins, sea cucumbers, and even several small jellyfish.

Img_5186

On our final full day in Sanya, we went to see the rainforest. It sounded very cool, but turned out to be a frustrating and disappointing experience. The place we went was excessively developed and felt like Disney world. There was no resemblance whatsoever to any U.S. state or national park. The only paths in the rainforest were perfectly well kept walkways. All of these walkways were completely packed with Chinese tourists and all of the sounds of nature were drowned out by the tour guide speakerphones coming from every direction. The largest animal we saw were some small bats hanging on a rock overhang under a nice wooden bridge. Rachael, myself, and one of our friends from the hostel ventured off the trail a couple times which was fun for a few minutes until we ran into another walkway or were scolded by tour guides to stay on the walkways.

Despite the one disappointing day in the “rainforest”, our trip to Sanya was great and I’m glad we planned that into our vacation. We got to see a bunch of beaches, had fun playing in the water, and were able to relax a bunch more before what we knew would be a fast paced week with my family in Beijing and Shanghai.

Img_5236

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Thu, 04 Aug 2011 20:19:47 -0700 Looking back on our time in Yangshuo http://wiscospike.posterous.com/looking-back-on-our-time-in-yangshuo http://wiscospike.posterous.com/looking-back-on-our-time-in-yangshuo This final set of blog posts is coming extremely late, but I figure better late than never!

We spent most of our second week (6 nights) in Yangshuo and this may have been our favorite location on our trip. Yangshuo has been different from every other city/town we’ve visited in that it is small, not too busy, and a surprising number of people speak English. There are only about 200,000 residents in the town and thus you can get absolutely anywhere you want on foot. After so many taxi rides during the first week, this was a great relief.

Another huge plus of Yangshuo is that the tourism catered to Western tourists instead of Chinese tourists. The domestic tourism industry is ridiculously huge, and a big part of what they do is to make absolutely everything accessible to anyone. This means trams, cable cars, stone pathways, covered boats, AC busses, and refreshment stands everywhere you can look. The idea behind making the best sites accessible enough so people of any age can enjoy them is cool, but the downside is there are often unimaginable hoards of people crowding around these sites. What makes Chinese tourism less attractive to me (and probably other Western tourists) is that there is exactly one way to see each of the tourist sites and the tourism companies dictate what way that is. Exploring on your own, or getting off the beaten path is very difficult and sometimes impossible at tourists sites aimed at domestic tourism.

Yangshuo was geared almost solely towards Western tourists. You could easily arrange your own outdoor activities such as bike riding, tubing, rafting, hiking, or exploring without having your trip being forced into doing exactly what a tourism company tells you to do.

When we got to Yangshuo, it was late at night and we stopped by the English school we planned to volunteer at since we were told we would have a room there. Sadly, they did not have a room prepared for us so they directed us to an area with a number of youth hostels. On our walk there, an awesome Canadian couple started talking with us and then walked us all the way there even though they were staying somewhere else.

We spent our first night at a wonderful hostel called the Showbiz Inn. It had a clean, comfortable room with AC, TV, Wi-Fi and a really nice rooftop bar with an English speaking staff. It cost less than $20/night for the room and we were all ready to book that room for the remainder of our stay, but we found out the next morning that they had already booked our room for the next night and there were none left, so we moved over to a place 30 meters away called Monkey Jane’s and booked our stay for 5 nights there (just $10/night for the same amenities but dirtier).

Monkey Jane’s was a really unique place with the most lively and fun rooftop bar I’ve ever seen. They had a beer pong table that was always in use, extremely cheap beer, tons of couches, and a gorgeous view of the surrounding Karst pinnacles. It was always full of European tourists, and it felt weird being English speakers for the first time on our trip.

On our first night in Yangshuo, we spent some of the evening in the Monkey Jane rooftop bar playing beer pong and some friends we made that night suggested we go tubing down the river with them the following day. So on our second day, we spent a good 3-4 hours sitting in giant inner tubes floating down the beautiful river. It was a ton of fun, and there were no rapids or anything dangerous. It was just a good relaxing time. A bunch of Chinese tourists on motorized bamboo rafts were passing us, waving, and taking photographs of us which was very entertaining. On the downside, our phone got wet and mostly ruined, and everyone on the rafting trip got terribly sunburned. My skin is still peeling from that trip which was about 4 weeks ago.

That night, we participated in something called the English Corner at an English language school in Yangshuo. This turned out to be incredibly fun and was a great learning experience for both me and Rachael. All we were asked to do was sit in a small classroom of 8-10 students and talk with them for 2 hours in English. Of the 6 days we spent in Yangshuo, only two of those days had English corner scheduled, and we happily attended both of them. Most of our discussions were us learning about the Chinese culture and talking about life in America. We hung out with one of the students named Gunnar and some of his friends after class and we had a great conversation. Turns out he is a computer science guy like me, we’re the same age, and we have the same last name. Unfortunately, we only met him on our last day, because he offered to teach us Chinese which we would have gladly accepted.

The highlight of the second full day was the bike trip I talked about in a previous blog post. The day after, we went exploring on foot because Rachael’s butt hurt from a terrible bike seat and a super long bike ride the day before. We decided we’d hike up to the top of a Karst pinnacle. We didn’t want to do a tour or anything, so we picked a fairly nearby pinnacle that was higher than the rest of them and had a TV tower sitting at the top. It’s safe to assume there is some pathway up to a peak where there is a TV tower. Thus, we started walking toward it and found our path blocked by solid rows of apartments. We kept taking small alleys between these apartments, and backtracked to find new alleys whenever our path ran into a dead end. Some residents also helped point us in the right direction because it was pretty clear where we were trying to go. After a lot of meandering, we found the entry to a very nice stone paved trail that let all the way to the top. The entire trek both up and down we didn’t see a single person, which was incredible. It was even more solitude than a hike in a national/state park in the U.S. The view from the top was a lot of fun, and the day was almost identical to a really fun day in 2008 when Tom and I managed to find our way to the peak of the “mountain” on the Island of Hydra in Greece.

The rest of our stay in Yangshuo was spent relaxing and rejuvenating after an overly hectic and rushed first 10 days or so of our vacation. We bought a couple books at a bookstore, watched a movie, hung out with our Canadian friends Jonathan and Kristen, participated in English corner again, strolled around town, bought some cheap stuff, planned the rest of our trip, drank some beers at night, and ate a lot of fruit. Oh and we went to a cave where we took a mud bath that I talked about in a different blog post!

Being able to relax for a few days was an amazing luxury, and we didn’t feel bad doing it because our vacation was so long. If our vacation were only for a week, we would feel guilty spending any of our time doing things that we could also do in the U.S., but since this was a full 5 week trip, we could really slow down and enjoy ourselves now and then.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Mon, 11 Jul 2011 01:36:04 -0700 First week of China - Beijing, Luoyang, Hua Shan, Xi'an, And Guilin http://wiscospike.posterous.com/first-week-of-china-beijing-luoyang-hua-shan http://wiscospike.posterous.com/first-week-of-china-beijing-luoyang-hua-shan Blogging has been a bit sporadic over our first three weeks of this trip so I'm going to go back through the fun events of our first week here. I'll get to the stuff from the past 2 weeks soon!

We didn't spend much time in Beijing since we will be doing a lot more of that when my family arrives. Our first impression was really nice, though. We stayed in a gorgeous Marriott room thanks to my dad's connections. That first night, my dad's friend Ying and her daughter Yuan treated us to an excellent meal of peking duck and showed us around a pretty park. In the morning, Rachael and I took a nice walk around, ate some cheap food, and then met up with Ying and Yuan again for a delicious lunch. After that we were off to Luoyang to meet oir friend Kevin.

I believe I already touched on our experience in Luoyan in a previous post so I'll skip it here. The highlights of the two days there were seeing the Longmen grottoes, hanging out with some kids who wanted to practice thir English, meeting an awesome girl named Cassie who helped show us around town, and then having a loud and fun dinner with a guy we met on couhsurfing. From Luoyang we traveled to Hua Shan which is a town based almost solely around tourism for hiking a famous mountain. I wrote a blog post from an Internet cafe there and remember being tired an in a bad mood. The town wasn't a very friendly place since everyone was pushy and trying to get us tourists to buy stuff from them. But the mountain hike was fun.

We started the hike at 6 in the morning, right after the sun came up. From the base to the top was more than 5000 vertical feet so we had to start early. The most common route was to take a cable car up the first 3000 feet and then hike the final stretch but there is also a trail to walk up this stretch and we opted to do that. It wasn't anything like a hike in a US national park. The entire trail on the mountain was paved with stones and phenomenally maintained. In my opinion, it seemed too easy and didn't feel like nature. However, I understand that their goal was to make it super accessible to everyone and indeed plenty of dressed up girls with high heels or flip flops were able to complete the hike. Rachael and I were super over dressed with our hiking boots :P.

Despite the hoards of people, the hike was still fun and was really good exercise. It felt great to get outside for so long. The first part of the hike where we skipped the cable car had very few other hikers which was nice. Although every 50-100 meters all the way up there was a shop selling refreshments. The view was obscured by a dense fog so we didn't get any photos of what would probably have been an incredible mountainous landscape.

After this hike, we took a bus over to Xi'an. Upon arriving it was pouring rain and we had to meet our couchsurfing friend Meng whose place we would stay at. We had a tough time finding each other at the train station but eventually she popped up with a huge smile on her face and led us to a local bus that cost us next to nothing. We took that for 45 minutes, an when w got off she bought some vegetables for dinner. We then took a taxi another 20 minutes to her home on the countryside. We trekked through some slick mud and finally made it into her home. Quite an adventure!

Meng's home was so serene and nice. It had an open courtyard with no roof over it and under an awning her mom prepared us dinner with the vegetables Meng bought. Meng made up our beds and then served us a fantastic dinner with several tasty dishes. Despite how amazing this stay was, it was about 60 minutes from downtown Xi'an and Rachael and Kevin really wanted to spend the next night somewhere closer. I was a bit disappointed, but it worked out ok and Meng found us a room at a cheap but nice Inn that was close to a bunch of stuff.

The next day, we checked into that inn and then went and saw the Terracotta Warriors which is one of China's most popular attractions. It was actually quite impressive and I think it lived up to the hype. After we got back, we went and explored Xi'an's snack street where there was an endless row of restaurants and snack shops. This street was just amazing! It's some of the best night life I've seen of all the places I've been in the world and the food was even better than the atmosphere. It was full of both locals and tourists but mostly just locals. It was loud but not excessively crowded and plenty of groups of happy youngsters had their tables covered with empty beer bottles as they played their Asian drinking games. Because of this street, I wish we had spent more time in Xi'an. Unfortunately, we already had our tickets booked for the following night down to Guilin.

On the following day, we spent a couple hours biking on the old wall around Xi'an. Our guide book had this as one of the best things to do in China and I can see why. The 9km wall surrounded the entire old part of Xi'an and was maintained extremely well. Because there was an entrance fee to go up and bike on it, it was totally empty. We could see the sights of the old town while also seeing the endless line of huge buildings just on the outside of the wall. I couldn't believe how huge this city was and it was truly a sight to see. It felt so much bigger than Chicago.

That night we flew to Guilin. We were just using Guilin as a convenient entry point to get to Yangshuo so our stay there was for less than 24 hours. We met up with a couchsurfer who runs a youth hostel and he gave us a room for no charge. He also spent a good 30-45 minutes talking to us and telling us what there was to do in the area. Liu Tao, this couchsurfer, is an avid rock climber, cyclist and outdoorsman so it was great to hear his advice. After these conversations and a breakfast at a local noodle place that cost $0.60 each, we headed to Yangshuo. We spent a bunch of time in Yangshuo and I only talked about a little of it so I'll write another post about it soon!

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Wed, 06 Jul 2011 19:53:50 -0700 First day in Hainan http://wiscospike.posterous.com/first-day-in-hainan http://wiscospike.posterous.com/first-day-in-hainan Today's my birthday and to celebrate, Rachael and I are going to go to a white sand beach and do nothing all day! We're staying at a cheap little place that is off the beaten path that sits right on the beach. I'm posting a photo from bed since I still am laying in it. Although the view is nice, it has AC and WiFi, the beach is not a goof beach for swimming so we'll probably change locations tomorrow. It's the offseason here and one super lavish 5 star place we looked at had it's two cheapest rooms (usually $300 & $450/night) heavily discounted (down to $90 & $115/night). I hope we find some good deals along a better swimming beach!

Photo

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Mon, 04 Jul 2011 02:16:22 -0700 Mud cave! http://wiscospike.posterous.com/mud-cave http://wiscospike.posterous.com/mud-cave Sorry I still haven't given any updates on last week's adventures and I probably won't for another few days at least, but I wanted to post this photo after a fun afternoon. We took a tour to a mud cave right outside Yangshuo today and it was exactly how it sounded. It was a huge cave that we took a boat into to start. After that, we walked much farther into the cave and wore some cheap helmets because the ceilings were low and we were all constantly bumping our heads. After a while, we arrived at a designated area where we could jump into the super muddy water and splash around for a while!

It was kind of gross and cold at first, but it turned out to be a lot of fun. I really liked floating on the surface because the mud made us so buoyant. Our guide took a photo which we bought and I am attaching a copy of it I took with my iPhone. Fun!

After the mud bath, we bathed in some natural hot springs in the cave which was awesome. The whole adventure was pretty short and lasted less than 3 hours, but was certainly worth the tiny entry fee.

We then came back and had lunch at our favorite restaurant in town for under $2 each. Now we have to figure out a way to get our clothes cleaned up before leaving for Guilin tomorrow! From Guilin, we're catching a flight to Hainan, China's southern island with tons of beaches where we'll spend almost a full week. We're really excited and booked a fairly inexpensive room ($35/night) supposedly on a quiet beach with big windows looking out at the water. Laying around on the fine sand there sounds like a great way to spend my birthday! Rachael and I will probably write more updates after we get settled in and have soaked up the sun for a while. Zaijian!

Photo

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Fri, 01 Jul 2011 05:39:12 -0700 The Yangshuo Countryside http://wiscospike.posterous.com/the-yangshuo-countryside http://wiscospike.posterous.com/the-yangshuo-countryside It's been many days since I last posted, and have since trekked 6000 feet up Hua Shan mountain, stopped in Xi'an to see the terra-cotta warriors, flew to Guilin where we relaxed a bit, and have spent the last 2.5 days here in Yangshuo. I'll talk about those other places soon, but just wanted to give a brief update on today's awesome journey! I apologize for being brief, but I am typing this from my iPhone (this cheap hostel has wi-fi!).

Today was the first day Rachael and I were able to escape the tourists. We woke up and had a leisurely morning where we dropped off our laundry that really needed to be done and then grabbed breakfast/lunch at a surprisingly not-so-touristy restaurant which is a rarity in this town. We then rented nice mountain bikes for $5 each and started on a long and gorgeous journey.

We biked out of town on the main roads which took us a good 45 minutes. Thankfully, the shoulders here are all very wide and the drivers are incredibly respectful to bicyclists. Drivers often honk their horns just to let you know they're coming and tend to drive as far away from the shoulder as possible, even when there is oncoming traffic.

Once we made it out of town, we veered off onto a smaller one lane road that hugged the less traveled Yulong river. There were still a bunch of tour busses taking travelers to their resorts along the river, so the first 30 minutes weren't that much fun. However, the traffic died down and the scenery became incredible. We were surrounded by mountains on both sides, biking along a slow moving winding river that was surrounded by rice paddies. I really enjoy countrysides and seeing these farming communities, so this was incredible.

We often veered away from the main road to get some more peace and quiet and see the countryside better. The people in the villages were really friendly, and we often had brief exchanges where we'd try to speak Mandarin and they'd try to speak English. At best, we learned each others names and figured how far away we were from things. One of my favorite parts was when Rachael thought we should turn one way when actually it led to a dead end at the river. When we began going that way I could see a Chinese man looking at us with an expression of 'why are those white people going that way?'. After a few minutes we reached a very remote stretch of river where a woman was preparing some vegetables. No one else was within shouting distance. The river was really pretty, so we hung out in the shade for a few minutes and laughed with the other woman who was really happy and trying to tell us stuff we couldn't really understand.

After continuing up the river a long ways, we reached a place where there was a bridge with people jumping off it into the water. A bunch of tourists also made it out here because there was a direct road from town, and everyone was swimming. Rachael and I grabbed some ice cream and put our feet in while watching tons of Chinese men in tighty whiteys jump off the bridge. We then crossed the bridge and followed a much more remote path along the river and through a series of small farming communities. There were no other tourists here, which helped add to it's beauty. It was a tougher bike ride since we were often riding on one food wide dirt paths that were wining between rice paddies, and we had to walk our bikes for a few minutes because I wad afraid of falling off the elevated path and into one of the paddies. When a local 12 year old boy on a giant bike came by riding through this like a pro, we of course hopped back on ours and became a bit braver. We ran into 2 local women going the same way as us and they helped lead us to the dragon bridge (our final destination) and kept us from getting lost. We actually passed the bridge and one of them came flying down the path after us yelling to us that we passed it! The stretch of the bike ride on this side of the river took about an hour, but would have taken much longer if she hadn't helped us!

After this really long ride, we decided to take a direct route back to town. We rode on the shoulder of a highway (again with huge shoulders and courteous drivers) and returned our bikes an hour later.

Today was incredibly fun and refreshing because we were able to escape the hoards of tourists as well as the ever-present crowds and traffic of Chinese cities. The Yangshuo countryside is incredibly beautiful and the karst mountain landscape is just as nice as it looks in the photos.

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu
Sat, 25 Jun 2011 03:12:11 -0700 We're in China! http://wiscospike.posterous.com/were-in-china http://wiscospike.posterous.com/were-in-china
Rachael and I arrived in China about 3 days ago and the time has flown so far. We've both been really jet lagged, and have probably been trying to pack too many things into too little time to start this trip. However, we've already done some very fun things and have a lot more to look forward to!
 
We started in Beijing for just a day where my dad's friend Ying and her daughter Yuan met us and treated us to a fantastic feast. We had Peking duck that first night, they gave us a driving tour of Beijing, and we walked through a park until Rachael and I were falling asleep. We stayed in a super nice hotel, and then walked around that area the next morning where Rachael and I tried mystery food from a bunch of different street vendors. All of it was delicious, by the way. Ying and Yuan took us out to lunch where we had a fancy meal of authentic Beijing food. The restaurant we went to was famous for its cow stomache, but Rachael was definitely not a fan. Ying and Yuan were extremely gracious hosts, and helped us out with tons of stuff including buying us a sim card and our train tickets to Luoyang.
 
On Thursday we spent 8 hours in the train to Luoyang and enjoyed a nice nap in our sleeper car, further prolonging our jetlag. We then stayed at a hostel that a couchsurfer recommended. That night we went out and ate a bunch more food from street vendors in a lively area that stayed up well into the night. It was really fun to be in Luoyang because it is clearly a city without much tourism.
 
Yesterday we met up with Kevin and went to a place just outside Luoyang called the Longmen grottoes where there a bunch of giant Buddhist statues carved into the rocks. What was really fun about this adventure was that a couple of 10 year old kids came up to us to practice their English. Soon after, a Chinese college student named Cassie also started talking to us, and from that point these three were our English speaking tour guides for the grottoes. Cassie then took us around to neighboring places where we saw a really old temple, ate a nice lunch, and then walked around a park in Luoyang. She then had to go home, but not before recommending Luoyang's most popular restaurant to us for dinner. After she took us there, we met up with our couch surfing friend who goes by the name Lovelush and he was a blast. He read us the entire menu like a story book, explaining every dish and even describing how some of them are cooked. He was extremely enthusiastic about everything and was a ton of fun to be around. After our 3 hours at the restaurant, he took us to a massage parlor that he said he visits every day. We got some inexpensive massages and then called it a day. Lovelush was the first couchsurfer that we met, and so far we're really glad we joined that community!
 
Today we took a train from Luoyang to Hua Shan because we are going to hike up a mountain here. We arrived at about 2pm, but Kevin was pretty exhausted from trying to bargain prices and make sure we did not get ripped off. One taxi driver was clearly breaking our agreement with him to try and squeeze 10 extra yuan out of us and by refusing to pay him that, we wasted at least an hour and added a lot of stress to our journey. Personally, I would rather get ripped off $0.50 each to save that time, energy, and headache, but I can see where Kevin is coming from in terms of principles. Anyway, we arrived here a bit tired and frustrated, and largely because of this we decided to book a room at a hostel and just hike the mountain tomorrow instead of today. So tomorrow we're going to wake up really early, climb to the top for about 6 hours, and then decide if we want to hike back down or just take the cable car down. We'll then move onto Xi'an tomorrow night where we'll meet up with our second couch surfing friend!
 
It's nice to be on this vacation right now, but it feels really weird not working. The week before I left, I was getting 4 hours of sleep a night, finalizing my thesis/research, preparing for & helping run the Wisconsin Swiss Ultimate tournament, finishing up some things with EWB, preparing to move, and preparing for this trip. So now I feel really lazy. I'm sure this will be just like the Kenya trips where it just takes me a few days to get into the mindset of moving slowly and not worrying about anything. Once I get to that point, I know this trip will be very theraputic, and will be a great transition between finishing grad school and starting up a business in August.
 
I'm a bit tired and thus didn't go into many details, but Rachael is currently writing a much longer email to friends & family that I might post to this blog as well. She doesn't feel very comfortable with the world seeing all her thoughts, so it might not happen. Regardless, I'll be sure to write a lot more as the trip progresses.
 
Lastly, I should note that Twitter and Facebook are blocked here, along with a bunch of other services I usually use like Google Docs and Posterous. Thus, these blog posts will likely be my only media outlet over the next month. I can't see posterous right now so I can't tell if comments are enabled. If they are, your comments show up in my email, but if they aren't you'll have to email me to get ahold of me. I hope everything is going well for you all in the U.S.!
 
-Mark

Permalink | Leave a comment  »

]]>
http://files.posterous.com/user_profile_pics/87231/IMG_0439.JPG http://posterous.com/users/eERFTSv60N Mark Liu wiscospike Mark Liu