Almost all freelance web developers are doing it wrong. And it’s a miserable existence to endure.
If you awake one day and say “I’d like to be a freelance web developer. Now how do I get clients?” you’re already setting yourself up for failure. Yet nearly everyone who chooses this career path makes this mistake. Like consulting, freelancing ought to be a natural progression of a web developer’s career (though of course, not the only path). Done right, it becomes the obvious next step for someone who has built an extensive professional network, developed finely honed skills, and has an entrepreneurial spirit. But prematurely deciding to freelance for any length of time can severely damage your career and mental health. You’ll feel pressured to take any work you can find just to keep the bills paid and your skills will erode as you crank out an endless stream of cheap WordPress sites. Here’s how to do it right:
Step #1: Obtain full-time employment in web development.
For the readers starting from scratch, this first step obviously is the most frustrating. After all, how do you get experience if every job requires experience? My initial advice is dependent on just how close to zero you’re starting. For those who are self-taught but just lack a bit of pedigree, I would recommend contributing to open source projects on GitHub and building a technical portfolio. Then craft a project-focused resume rather than a chronologically organized one. If you are uncertain about your skills and find the thought of contributing code a bit intimidating, offer to write documentation. Good documentation is critical to the success of every open source project and yet most maintainers are loathe to spend time on it because writing code is more fun. Plus, the ability to understand and explain other people’s code is an exceptionally valuable professional skill to develop.
If you lack the expertise to yet make sense of code, my recommendation would be to consider taking a few classes at a local community college. A well-taught certificate course will help you establish a baseline of skills. A two-year degree may or may not be worth the investment, depending on the content. A four-year degree in “web development” is, without a doubt, not worth either the cost or the time (a Computer Science or MIS graduate from a reputable school is infinitely more employable).
Obtaining full-time employment early in your career, even if you eventually prefer to be a freelancer, is important for a number of reasons. You’re exposed to how a business operates from the inside and will learn what works (and what doesn’t work) on someone else’s dime. Operating as a freelancer can also be a financially precipitous position for even those that are successful. A secure job will provide the opportunity to build up a savings cushion and practice managing personal finances.
Step #2: Start networking like your job depends on it.
I’ve been a professional web dev since 1997 when I first wrote a simple online shopping cart in Perl for a small computer store where I was a PC tech. Over the past 16 years, the biggest mistake I’ve made was spending too much time honing my technical skills at the expense of all other aspects of my career. I’d rather spend hours tangling with the toughest bug than go to a business social event. It wasn’t until my late 20s did I stop discounting “soft skills” as pure fluff.
Spend some effort getting to know your co-workers; especially those whom you rarely interactive with and don’t know well. Chat with clients about more than their immediate project. As the level of trust builds, ask about other aspects of the business you might be able to assist. Attend local user groups, meet-ups, and developer conferences. These events can require a significant time commitment but are invaluable because everyone is there specifically to meet you (and people like you.) MeetUp.com is a popular resource that I’ve used with success, as are searching for local groups on LinkedIn.
This networking is laying the groundwork for your eventual freelancing and is absolutely necessary for your success. A moderately-skilled programmer who’s really good at communicating with people is worth at least 3-5x more to most businesses than a brilliant programmer who prefers to be left alone all day. Schmoozing increases your “social surface area”, which leads to more professional contacts and more work, but also improves your ability to translate between tech-speak and business-speak. Your goal is to become known as someone who solves problems, rather than the “web guy.”
Step #3: Leverage your network to find a job that’s compatible with freelancing.
For most developers who love their field and have no desire to freelance, I’d recommend seeking employment at a software/web development company rather than an insurance company, bank, etc. Being on the front lines as production staff (the people making the money) generally means that your interests and the company’s interests better align: higher pay, more interesting, varied work, and greater respect. However, for those looking to moonlight on the side, this often presents a conflict of interest. Don’t be tempted to circumvent a non-compete agreement, unless you have both a good legal standing and a very good personal reason for doing so. This can backfire and ruin much of the goodwill you’ve spent your career building. Additionally, it will burn a bridge that otherwise could be a great backup plan in the event that freelancing doesn’t work as well as planned.
A better option is to find a job that allows you to comfortably leave “work at work.” While not as exciting (or lucrative) as other career options, it does leave enough energy to pursue side work in the evenings and on weekends. You may even want to make a lateral move and spend your days doing something other than development to help prevent burnout. In my case, I took a job as an IT Manager for a microchip engineering company.
Step #4: Tap your network to find gigs.
I use quantity of LinkedIn connections as a measure of preparedness to enter the freelance marketplace. Are you connected to less than 100 people? If so, step up your networking efforts. While wholly arbitrary, it’s a good barometer to whether you lack a large enough network to support freelancing. Using this network, reach out to past employers, coworkers, and clients. Let them know that you’re currently seeking side work and whether they know anyone who needs help. This type of shameless self-promotion might push you outside your comfort zone, but becomes easier with practice. And if you have the desire to becoming solely reliant upon freelancing to earn a living, learning how to sell yourself is an essential skill.
I’d also recommend that you give freelance marketplaces such as ODesk, Elance, Freelancer.com, and Guru.com a shot. Don’t concern yourself with the fact that you’re bidding against developers from all over the world who are willing to work for pennies. My company is in the top 0.8% of providers on one of the aforementioned marketplaces. We use it for short term, filler work and routinely bring in more than $100/hr because I’ve gotten rather good at marketing our services. I’m highly selective about the projects I bid on and in my initial contact I address the specific problem that’s immediately in front of the client.
Step #5: Keep raising your rates until your gigs pay better than your “real” job.
Rational people make purchasing decisions based on value, not price. Pricing yourself beyond the reach of irrational clients is a good first line-of-defense against taking jobs you’ll regret. As you work to establish your reputation as a freelancer, your time becomes more valuable. The industry is fraught with unreliable and poorly skilled freelance web developers. Always ensure that your rate grows with your value.
Properly pricing your web development services can be tricky, especially for those who don’t have a background in business. However, if you’re taking the advice of this article and gradually and organically growing your freelancing, it actually becomes rather simple: Allocate a fixed amount of hours each week to your freelance gigs, perhaps 12-20. Doing so will help prevent burnout and, since work expands to fill available space, force you to work more efficiently. Once you’re consistently booked each week, raise your rates on new clients by 20%. Rinse and repeat until you’re turning away more than half of new business and then begin raising rates on existing clients. The goal is to hit an equilibrium where your schedule is booked solid 4-5 weeks in advance. Getting to this point will take several years of hard work and you will make a lot of mistakes. Learn from them and move on.
Step #6: Profit! (er, hopefully)
Assuming you’ve been patient, persistent, and extremely driven, by this point your freelancing should be more lucrative than your day job. The decision of when to make the leap into full-time freelancing is a very personal one. It will vary depending on your risk tolerance, skill level, amount of debt, family obligations, and other factors. In other cases, such as with myself, the decision is made for you through the loss of your job. However, looking back on it now from the perspective someone who’s managed the finances of a business, ideally I wouldn’t recommend quitting until you’re consistently making double your annual salary with freelancing 20 or less hours a week. If that sounds like a lot of money for a lot less work, you’re in for a shock. At the end of most months, you’re likely to be making about the same as you did at your day job.
Now that you’ve escaped your day job, remember this mantra: Always Be Selling. Freelancers frequently slip into a “feast or famine” cycle. One month you may be feverishly pulling 70 hour work weeks while the next your diet may consist mainly of Ramen and PB&J. This cycle occurs when you let your pipeline dry up and focus exclusively on the fun work of building web sites. Even if you’ve got more work than you can handle, spend one day a week (20% of your time) on business development efforts. It needn’t be anything as dreadful as cold calling (and in fact, shouldn’t be if you’ve carefully managed your pipeline). Call/email past clients to reconnect, attend networking events, or update your blog.
Beyond: Specialize and then clone yourself–or hire (whichever is easier.)
I could write a book on everything that I’ve learned (the hard way) that fits under this heading. Briefly though: the most important bit is to specialize. My company, RightBrain Networks, specializes in consulting on engineering best practices for applications that run “in the cloud” (generally Amazon Web Services). There are other companies out there that only make websites for luxury car dealerships or personal injury law firms. And there are yet other, independent freelancers who make a killing supporting legacy ColdFusion or ASP classic applications. Don’t be afraid to specialize. At first, it can be unnerving to turn away work that doesn’t fit with your new, better-defined vision. But it’s a necessary step unless you want to permanently be competing with every person on the planet that has ink jet-printed business cards that say “freelance web developer.”
Hiring is an even more tangled topic to wade into. Growing to need employees is what graduates a freelancer from the ranks of the self-employed to a business owner. However, a person absolutely must be sure that he wants be a business owner first and a web developer second. Plenty of businesses fail because the owners figure that it will run itself if they focus on the billable work. Employees are God-awful expensive and the company has to be in a condition to support them, but it’s because they’re worth it when/if you’re ready. Don’t be in a hurry to hire and remember that a comfortable living can be made as a successful freelancer, sans employees. Work your network and find other freelancers to assist with overflow work on a contracted basis. Do this for as long as possible before contemplating hiring actual employees.
A couple of years ago, as part of a health kick, I took up weight training. I quickly realized that this was something I had a genetic gift for doing well. As my desire to lift heavy things increased, it drove me to begin seriously training for competitive powerlifting. It felt more purposeful and interesting to work towards a goal, rather than simply “lifting weights.”
A few months into my training, I learned that one of the most respected powerlifters in the world, Matt Kroc, lived a short distance from me. In 2009 Kroc became the all-time world record holder in the 220lb class posting a 2551lb total via 1003lb squat, 738lb bench press and an 810lb deadlift. While inhumanly strong, he’s actually better known for being one of the most intense professional athletes on the planet, in any sport. He’s torn nearly every major muscle group in his body at least once (occasionally on video), invented the infamous “Kroc Row“, and beaten cancer while simultaneously training to beat the world record.
With a bit of Internet sleuthing, I tracked down his contact information and arranged a personal training session. Given his reputation, I showed up to the session completely unaware of what to expect–and I’ll admit, rather intimidated. But I discovered Kroc to be a very thoughtful and sincere person with a willingness to share an incredible amount insight into what it takes to be the best in the world at something.
At the top of the game, strength itself no longer becomes the deciding factor. Everyone on that platform has gotten strong through the same methods of years of consistent training and dedication. It’s also not about who wants it more; each person has the same reason for being there and is driven by the same dream. When that 1,000 pound bar is laid across their shoulders, it comes down to who can endure the most pain and still stand back up. And then doing it twice more again. It’s also about the pain it’s taken to get there: sticking to the proper diet, training through the inevitable injuries, and balancing the mundane demands of family life and career.
While I don’t have any illusions of becoming a top powerlifter, understanding this mindset has helped me as an entrepreneur. I can’t count the number of times I’ve wanted to give up. Some days, the allure of being able to leave “work at work’ tempts me to consider updating my resume, especially now that I have a young daughter waiting for me at home. But like lifting, I work through the pain because I know, somewhere, someone else is giving up. If I can survive today and wake up tomorrow just a little bit smarter, eventually I’ll find myself at the top of my game too.
Don’t store persistent data on EC2 instances functioning as application servers.
In nearly every n-tier application architecture (not just those in a cloud deployment), the application tier should be treated as stateless, meaning containing nothing other than the application code. Databases, files, and even logs should be shipped to another location. Each application server (or EC2 instance) should be treated as if it were disposable.
This design is especially important on AWS, because individual instances tend to have short lifespans. The underlying hardware in Amazon’s datacenters randomly fails and is sometimes scheduled for retirement. And unlike a physical server, which may be repaired, a terminated instance is gone forever.
The most common refactoring required here is in the case of applications that allow users to upload files. These files should be directly uploaded to S3 whenever possible, using the code library available for your language of choice. Best practices also dictate that a database not be stored on the same instance as the application code. Better options are Amazon RDS, SimpleDB, or DynamoDB. For applications requiring a different configuration (such as PostgreSQL), rolling your own using EBS-optimized instances and provisioned IOPS EBS volumes are preferred for high-availability and performance reasons. Using a placement group for a database cluster may also be a viable solution, though I haven’t yet experimented with it.
Always set up an autoscaling group, even if it contains just a single instance.
Autoscaling is the ability of EC2 instances to detect system load and either bring additional servers online or to spin down excess capacity. This happens automatically by using CloudWatch alarms that respond to average CPU utilization, outgoing network throughput, disk I/O queue, or any combination of several dozen metrics. Custom metrics can also be defined that scale based on application or business rules (such as number of active user sessions or Twitter activity).
Autoscaling is the heart and soul of AWS–it’s how you build scalable, self-healing applications. If you’re not using it, you’re better off using a traditional VPS or co-lo server, as it will be cheaper and more reliable. An autoscaling group of a single server (a group defined with an instance count of
max=1) can also be created if running multiple instances is cost-prohibitive and/or several minutes of random downtime is acceptable. This will permit AWS to kill an unresponsive server and automatically bring a replacement online.
Using MySQL? Convert MyISAM tables to InnoDB if possible.
Amazon’s FAQ for RDS reads, in part:
The Point-In-Time-Restore and Snapshot Restore features of Amazon RDS for MySQL require a crash recoverable storage engine and are supported for InnoDB storage engine only.
I consider these two features to be among the top reasons to be using RDS. Combined, they ensure that your database is automatically being backed up on regular schedule. Point-In-Time-Restore uses the transaction log to allow you to revert the state of your database to any point in time (down to the second) within the past 24 hours. Snapshots occur once every day and are rotated according to a user-defined schedule. I generally retain a week’s worth of RDS snapshots.
InnoDB tables can nearly always be converted to MyISAM without much issue, except in the case of database using full-text indexes. MySQL 5.6.4 introduced full-text indexes to InnoDB, but as of this writing, RDS only supports up to MySQL 5.5. Developers using full-text searching in their applications may wish port over that functionality to AWS’s CloudSearch so InnoDB can be used on RDS. Alternatively, MyISAM can continue to be used, but a manual backup strategy (using the
mysqldump utility) will have to be implemented.
Find an alternative to cron jobs.
The ability to have cloud-based task scheduling is one glaring omission that I’ve discovered in AWS’s bag of tricks. There’s no (official) way to ensure that a job is run once, and only once, on a regular schedule. Best practices dictate that no single-point-of failure exists in a deployment, yet I’m not aware of any type of clustered solution that works as simply as a plain ole’ cron table. Attempting to run cron jobs on autoscaling application servers will inevitably cause race conditions–and headaches.
The solutions that I recommend: First, make sure that cron jobs are really the best application design. In many cases, they’re used to communicate between various tiers in an application. A cron job might kick off a batch process to resample audio files, for example. In this use case, a message queue would be a preferable solution. It scales easier and has the benefit of ensuring that a job was completed successfully. Amazon’s Simple Queue Service (SQS) is a good choice.
If actual job scheduling is needed, then two options would be to use either an autoscaling group containing a single t1.micro instance, or use a third-party solution such as iron.io.
Enable sticky sessions on the elastic load balancer.
An Elastic Load Balancer (ELB) sits in front of an application server pool (autoscaling group) and distributes incoming requests amongst the instances. It will automatically pull a dead instance out of the pool and then resume routing traffic to the replacement once it comes online. By default, users may randomly be bounced around to different instances throughout their website visit. If your application uses session cookies, this can cause the problem of users constantly being logged out each time they request a new page. This occurs because session information is stored locally to each instance (generally in text files in a temporary directory). When the user is bounced to a different server, the session is lost.
Elastic Load Balancers include a “sticky session” feature that eliminates this problem. This is a special cookie that instructs the ELB to send the user to the same instance for the duration of his visit (session). The ELB can be configured either use it’s own cookie, or an existing cookie that is set by application.
Sticky sessions do have one notable drawback: if the instance that the user is “stuck to” dies, or is scaled down, the user loses his session. This may not be a major problem for your use case, or it could mean that a couple hundred shoppers lose their shopping cart contents. The alternative is to move the session data to a shared datastore, generally either ElasticCache (AWS’s version of Memcache) or DynamoDB (a very fast key-value “NoSQL” database).
Don’t send email from your EC2 instances directly to users.
Since it’s so simple to quickly and cheaply get a temporarily server online with EC2, spammers have been drawn to the platform in droves. Unfortunately, the result has been that large swaths of EC2′s address space has been penalized by major email hosts. Additionally, AWS monitors and limits the amount of outgoing SMTP traffic that can be sent per account. These facts can often make reliable email delivery an impossibility.
My recommendation would be use a hosted SMTP relay (such as AuthSMTP) for simple email needs. It’s extremely cheap ($32/yr) and well-worth the additional peace of mind if reliable email delivery is important to your application. If the ability to automatically track bounces, unsubscription requests, and complaints would be helpful, several transactional email providers provide feature-rich solutions, among them: JangoSMTP, SendGrid, and Mandrill. Amazon also has Simple Email Services (SES), which provides much of the same functionality.
Try to avoid creating custom AMI’s.
It can be tempting to set-up and tune a server to work exactly as needed and then “burn” it to an AMI so the configuration can be reused, either in an autoscaling group or as a backup. However, AWS doesn’t offer much in the way of AMI versioning or management. It’s not even possible to determine on which date an AMI was created. This also creates the hassle of having to recreate a new AMI each time a minor change is applied.
A better option is to use a base AMI from a trusted source, such as Amazon’s official AMI’s or one that you’ve custom built, and then use a provisioning-at-boot strategy. When an EC2 instance is launched, a “user data” string can be specified. This string can be something as simple as a Bash script that executes a
yum update -y to update all the system software and then checks out a copy of the application code from a Git repository. Or the script can be used to bootstap a more complex provisioning process managed by Chef, Puppet, Salt or other configuration management and orchestration tools. Amazon’s official AMI’s also support cloud-init scripts, which are a good balance between the simplicity of a shell script and the power of larger orchestration tools.
The primary advantage of building the instance at boot is flexibility and control. The provisioning scripts can be stored alongside the application code in a source repository, which makes it simple to track changes to the application’s environment. For example, are you upgrading from Tomcat 6 to 7? Simply change version number in your bootstrap script, commit the changes, and create a new autoscaling group. Once the new servers boot and provision (10 minutes later), test your application. If it looks OK, then change the elastic load balancer to point at the new server pool. Done.