For the first time in my career, I recently got to sit in the hiring chair. My current employer let me look for a Software Engineering Intern. (With a little luck, I'm hoping to repeat the process for a senior level developer in the near future.)

I learned a lot, very quickly, doing that (a biggie: actually write down a list of questions to ask, don't just wing it—with my apologies to the very first person I interviewed, as I did not do a good job interviewing you), but this post is not about those lessons learned. This post is for all the people I had to send a rejection letter to. In my opinion, all of the people I interviewed were smart and capable of doing the job; actually picking one was a tough choice.

While our chosen field of computer programming offers amazing opportunities, finding work can be difficult¹, so don't despair. Here are some general tips to help you find work, and become a better developer.

Caveat: These are all filtered through my path as a developer, which absolutely does not match yours. The most obvious and major difference being I did not go to college. Props on going for the degree!

Finding Work

Give Talks

Local user groups—such as DSM Web Geeks, Pyowa, DSMJS, Iowa Ruby Brigade, CIALUG, and so forth in the Des Moines area—are always desperate for talks, as are larger conferences such as Iowa Code Camp. Too many people are terrified of speaking, thinking they need to be an expert, but that's just not true. The perspective you bring as a newbie, showing us all something you've learned, is both incredibly valuable, and woefully under-represented. It's also an effective way to catch the eye of a hiring manager or recruiter.

Giving talks will help you become a better communicator, which is a vitally important part of being on a team.

Contribute to Open Source

I learned a ton in my early days by contributing to open source projects. If you can find a project you care about, with a community of smart, patient people willing to help guide you, contributing to existing projects will help make you a better programmer, and give you things you can point at to say: I did that.

Contributing to open source will help you get better at revision control (e.g., git), reading code, and accepting feedback.

Develop Your Network

Getting your foot in the door of a place is a lot easier if you already know somebody on the inside. Be friendly, hobnob with other developers (e.g., at local dev groups), and stay in touch with people.

I've spent large chunks of my career as a lone developer and am a natural shut-in, so this is one area I regularly fail at.

Expand Your Mind

While learning the Framework Du Jour or a specific technology will help you be more employable in the short term, a solid foundation of knowledge will help you throughout your career, enabling you to pick up framework and language specifics more quickly, and to see the similarities with things which have come before.

While it'd be difficult to point you to the books that helped shape me as an early developer—in part because books like "QBasic For Dummies" and Laura Lemay's "Teach Yourself HTML 4 in 21 Days" would be a tad anachronistic at this point (though it appears there is a more modern version of the latter)—I can at least point you to some that have shaped me as a more intermediate developer.

How to Design Programs
This book is a gentle introduction to programming. It's a solid look at programming starting from zero. While you're almost certainly beyond it skill-wise already, I've been skimming through it lately and find the way it frames things can be helpfully clarifying.
Structure and Interpretation of Computer Programs
This was the textbook for an MIT course of the same name for a long time, and takes you from basic functions to meta-circular evaluators. Depending on what you've been exposed to already, it may well cover a gamut from "yeah, duh", to blowing your mind. Also freely available online as a textbook, and video lectures.
Practical Common Lisp
Common Lisp is a really fascinating language, and can open your mind to possibilities you likely haven't imagined. It certainly did for me! As just two examples: multiple dispatch and the condition system are among the things I sorely miss in other languages. Also available freely online.
Coders at Work
This book is a series of interviews with influential developers. It's an interesting foray into the minds and experiences of people who have ventured down the path of software development you are just now beginning to embark upon. This one isn't freely available, but it is a fascinating read, in my opinion.

Admittedly, that list reflects a heavy Lisp heritage. Lisp has had a heavy impact on me and the way I think about programs, but many languages can fall under the category of "mind-expanding". You're likely already familiar with Java, which covers the space of modern, standard object-oriented design. Some other design spaces you may want to explore, if you haven't already:

  • A purely functional language. Purely functional code is often easier to understand and test than OO code, and you'll see its influence across a broad spectrum of languages. A good way to explore this space is through Haskell, which is a very mathy language—you could easily get lost in monads and category theory—, if you're into that sort of thing.
  • Different concurrency models. Erlang's actor-based concurrency model is fascinating, and often much better suited to problem domains than thinking about lower-level things like threads and mutexes.

Sadly, none of these things are likely to make you more employable on paper—a sad reality of how we evaluate developers today—but they will make you better at the job itself.

Be Well-Rounded

It took me entirely too long to learn, but there is more to life than slinging code, so don't forget to do things that aren't programming. Take an improv class, learn some ballroom dancing, learn about user experience (UX). Whatever interests you.

  1. I am amazingly terrible at interviews, to the point where I've spent years in between jobs, and the jobs I've gotten are often in spite of my interview. I totally get how frustrating it is to be rejected.

One of my philosophies about exceptions is that they should be very specific. It's not enough to throw a generic Exception, or even a LibraryException, you should throw a specific exception for any event you want to handle separately. For instance, at a previous employer, our database layer would catch the generic exceptions (PDOException, in our case), inspect them, and re-throw as very specific exceptions (DuplicateEntry, CannotConnect, etc.).

Why

I've formed this opinion over the years, seeing things like the following code from a real application, which decides whether a nonced token has been seen before, and rejects the token if so:

try {
  // insert ...
  return false;
} catch (PDOException $e) {
  // 23000 is SQL error code for duplicate entry
  if (23000 == $e->errorInfo[0]) {
    return true;
  }
  throw $e;
}

That's ugly, and an annoying amount of boilerplate. It's much clearer if you can do something like:

try {
  // insert ...
  return false;
} catch (DuplicateEntry $e) {
  return true;
}

Particularly when you start getting into multiple scenarios, or start needing to support multiple database types. It can also be handy in the case of testing, since your mock can literally just throw the scenario you're trying to test.

If you're looking at that DuplicateEntry exception and wondering why it doesn't just check if the value exists first: race conditions, man.

Fun fact: that particular codebase has over half a dozen instances of catching DuplicateEntry for varying tables. Some of which are caught all the way in controllers to expose information to the user (hey, we already have an account with that e-mail address). The pain saved is very real.

Don't Wrap That Exception

This of course leads to another opinion about exception handling: libraries shouldn't wrap exceptions thrown by lower levels. At least, not in a generic, everything-is-now-a-LibraryException sense; clearly I'm okay with wrapping an exception to make it more specific.

There's a conflict of interest here: what exceptions get thrown is absolutely a part of your public API, and you want to be able to swap out the libraries you happen to be using underneath. Wrapping exceptions enables you to do that, whereas passing them through does not.

But here's the thing: in the age of Dependency Injection, your caller knows more about the exceptions thrown by the implementing object than you do. So if you're a Frobnicator, and your caller gives you a Fetcher implementation, you're actually hiding information the caller expects by catching exceptions thrown by the Fetcher and wrapping them in your FrobnicatorException. That's actively hostile to your calling code, because now to catch the exceptions they were expecting they must first catch your exception, unwrap it, then rethrow.

I guess another way of saying that is any layer in the middle should refine exceptions, not conglomerate them. Please, be mindful of which you're doing.

CL-FTP Bugs Squashed

2015-Jun-03, Wednesday 06:28

One of the perks of being unemployed are that I have a reasonable amount of downtime again, so I've been spending some of it updating libraries which are under my care.

CL-FTP, which is pretty stable and incredibly niche (who uses FTP anymore, anyway?), has recently had a flurry of activity. Thanks to Kambiz Darabi for finding and fixing a bug related to :if-exists nil. Thanks also to Rafael Jesús Alcántara Pérez for improving ABCL compatibility.

CL-FTP is now at version 1.6, and includes both the above-mentioned bug fixes as well as:

  • Now available via the ASDF system name "CL-FTP", in addition to "FTP". The inconsistency between library name and system name bothered me, so now they're consistent.
  • No more docs hidden away in an LML file. The method docs have been moved into docstrings, which hopefully puts things a little closer to where they should be. (And hey, maybe they'll be picked up by quickdocs in the future.)
  • "active" FTP should actually work now. Turns out it's been broken for, uh, entirely too many years. It may or may not work depending on NATs and firewalls in between the client and the server, but the library itself should no longer be an issue.

In related news, a big thanks to Mariano Montone for his patches to burgled-batteries. You may be interested to check out his burgled-batteries.syntax project, which helps bridge the gap between what burgled-batteries actually is and the ambitious goals I have yet to get anywhere near.

Before my now-previous employer folded, we were being courted by a buyer (boy, was that time period an emotional roller-coaster!). This would have changed our development direction somewhat, and so at the behest of my boss I spent a couple of weeks exploring language options for our upcoming greenfield projects.

I was asked to explore Clojure and Go, and compare them to our existing language, PHP. I decided to evaluate the languages by porting a fairly stand-alone portion of our existing app into both Go and Clojure, and comparing it to the PHP version.

The Basic Problem

That relatively stand-alone portion of our app was our recipe importer. It would allow users to import recipes from external sites, for use within our system.

There are a number of standards for recipe formats: the hRecipe microformat, schema.org's Recipe format, RDF, data-vocabulary.org's Recipe format, and probably a few others. In addition, most of the sites we explicitly supported either fail to mark up the entire recipe, interpreted the spec they purported to follow in strange ways, or merely referenced a recipe from another site, requiring us to follow the link chain to a parsable source.

In other words: it's a real (non-toy) problem, it's sufficiently complicated to get a reasonable feel for a language, and it's simple enough it can be ported within a relatively short timeframe.

The Method

I spent a week in each of Clojure and Go. This was not enough time for a full feature-parity port with the PHP version, but sufficiently long to present most of the complexity of the problem (inheritance, parser registration, and so forth).

Inheritance Is… Uh… Missing?

Neither Go nor Clojure support "classical" OO inheritance. It may be my brain is broken through years of shoehorning things into inheritance hierarchies, but it seems to me this particular problem happens to be a pretty good fit for classic OO. "Site X uses format Y, with these overrides."

So the biggest stumbling block in both languages was simply coming up with a way to model the problem. To be perfectly honest, the solution I ended up in both languages looks pretty much like classic OO shoehorned into whatever the language does support (embedded structs in Go, multimethods in Clojure), and would almost certainly not be considered idiomatic by people with more experience in each language. (But then, neither would my PHP, and I've been doing that for a very long time.)

Clojure

As a lisper bent on proving that a lisp was a viable option, I started with Clojure.

I spent approximately three days figuring out how to even model the problem without classic OO inheritance, and the following two building three generic parsers and two site-specific ones.

It was one of the most enjoyable weeks of my tenure. Clojure is as flexible as you'd expect a Lisp to be, the code was compact, reasonably fast, and I was reminded of how much I'd been missing interactive development.

When I presented it alongside the PHP it was a port of, reactions were mixed. Some developers remarked that they had a very difficult time understanding it, and that it would likely prevent the developers who worked on other codebases (e.g., our mobile developers) from swooping in to make a quick fix. My boss, who had apparently spent a significant amount of time attempting to do something useful in Clojure without much success, remarked that the comparison made Clojure much easier to understand for him than it had been previously.

Go

Next I tried Go. Having already faced a lack of inheritance, and presented with Go's much smaller field of options to choose from, I was able to jump right in, and hit parity with the Clojure version in short order.

I did not particularly enjoy programming in Go. While Go is strongly opinionated in surprisingly good ways, the edit, compile, debug cycle is obnoxious and slow (goauto may have helped reduce this pain), the code ends up verbose and repetitive, and there are little to no facilities for abstracting out patterns.

The Go code received less objection when presented. The lines were significantly longer than either the PHP or Clojure versions, and vertically it was very slightly less dense than the PHP version due to all the error checks.

Subjective Comparisons

Code Feel
PHP Clojure Go

Is it any wonder I prefer lisps? So much meatier.

Learning Curve / Programmer Effectiveness
Go was the easiest to pick up, but there isn't really anywhere left to go once you do. Clojure is definitely harder to become proficient with, but you can leverage that into increased programmer efficiency later. PHP has so many pitfalls that anyone starting out is likely to end up negatively productive at first, writing a horribly insecure buggy mess.
Developer Emotions
Clojure inspired a mixture of enthusiasm and curiosity, with strong concerns for the practicality of finding and training developers in a language which is so different from mainstream ones. Go had an enthusiastic champion in my boss, but was largely met with ennuitic "meh"s by other developers (golang is basically boring-by-design). PHP was loved by none and hated by some, with the only argument for it being "we've already got a bunch of code written in PHP".

Recommendation

I liked Clojure. It's fun to write in, it feels very productive, and the power lisps provide to build your own abstractions is unparalleled. But as varied as the quality of code was in our PHP, providing room for even more variability is a hard case to argue. While I would have loved to recommend Clojure, asking a team of devs who think in terms of C++ to make the transition was more than I could have sold.

Go, on the other hand, solves a lot of problems that we had with PHP. Stuff the wrong type? Go would have caught that. Inconsistently-formatted code? gofmt would have fixed that. Bugs because PHP did something unexpected unless you thoroughly read and retained every aspect of the documentation? Go's house is not built from bundles of thorns.

So while Go may not be the language for me, it would have been the right call for our team.

Available For Work

As you may have guessed by the mention of my previous employer folding, I am presently available for work. Feel free to contact me if you have something you think I may be interested in.

Apologies for the short notice--I've been rushed just trying to get the darn thing ready--but I will be giving a talk at Iowa Code Camp on Saturday, November 1st.

If you can't attend, or want to review my talk before/after I give it, you can find it here. Feel free to offer any feedback. It's much too late to make major changes at this point, but feedback is welcome regardless.

(Technically the talk is about PHP, but tagged Lisp because I blame Common Lisp for inspiring a bunch of it.)

November 2021

S M T W T F S
 123456
78910111213
14151617181920
212223 24252627
282930    

Syndicate

RSS Atom

Most Popular Tags