Jamie Gaskins

Ruby/Rails developer, coffee addict

The Taboo of Salary Discussion

Jun 19, 2014 @ 11:49pm

The phrase "salary discussion" bring about weird feelings in me. My first thought is "salary negotiation", which always scares the hell out of me because:

  • Salary is considered personal and, therefore, something people don't really discuss with each other.
  • Because of that, I don't know how much a developer should expect to get paid.
  • As a candidate for a job, I don't want to ask for too much and the interviewer thinks "this guy has way too high an opinion of himself".
  • I don't want to ask for too little and the interviewer think "this guy cannot possibly be a professional developer".

The other thought that comes to mind is that, relating to the second bullet point above, in order to find out what other developers are getting paid, I have to ask them, which feels invasive. I don't know how anyone will take that question, so I'm always afraid to ask. I've always been told it's like asking how much someone weighs — it's personal.

The problem with that line of thinking is that, if you don't know what others in your field make at a similar level of experience, you don't know how much you should ask for.

I make $80k/year. I grew up pretty poor (single mother making barely over minimum wage), I struggled through college, even served in the military, so in a way that figure seems astronomical to me and it feels kind of weird to even think I deserve more.

Sometimes it doesn't matter …

I live with my roommate in a pretty cheap townhouse in the suburbs of Baltimore, MD (I didn't think it was all that cheap at first, but I'd lived a pretty minimalist lifestyle up until I moved here).

Since my expenses have been low, I haven't been really concerned with whether I've been getting paid as much as I should because I've made more than I need to get by. Worrying about that would've added stress that I just couldn't handle.

… but sometimes it does

I'm about to move into a new apartment in the city with my fiancée where the rent will be about $700/month more. Add to that the fact that I'll be going from splitting the expenses in a cheaper house with a roommate to being the only person with a paycheck in a more expensive place and my expenses/salary ratio just got a lot higher.

To clarify why I'm the only one with a paycheck there, my fiancée won't be able to get a job until she gets a US visa giving her residency, which probably won't be until after we're married.

Since suddenly money will be significantly tighter, I actually do need to know whether I should be getting paid more.

But I stiiiiiiill … haven't found … what I'm looking for

Before I got my current job, my entire career in programming had been freelance, so I have no idea how much money a full-time senior developer should make. So when thinking about this, I realized I didn't know how to approach researching it. I didn't think it'd be a great idea to bluntly ask my employer "Hey, am I being screwed here?" Though I imagine they'd probably be straight with me, even if I asked it like that, the idea of rocking the boat is really scary.

UPDATE: I want to clarify that I didn't mean "am I being screwed" seriously. I use a fair amount of hyperbole in my everyday speech, but I try to make it obvious and my usage of it here was not obvious. I know that my employers aren't intentionally short-changing me.

I asked my friend Alicia about it (she's an absolutely fantastic person if you ever get a chance to meet her). She told me she wasn't sure what a senior developer makes but that junior devs at her last job started at $70k/year.

That kinda stung a bit. A junior dev starts at $70k? My first reaction was: My more-than-a-decade of experience and my depth and breadth of knowledge of Ruby, Unix, and various front-end web technologies is only worth an extra 14%? But my first reactions are almost always pretty extreme and visceral.

So after I calmed down, I realized that maybe $80k was all they could offer at the time. I asked for $100k, but I was the first developer whose work they could bill for. Before they hired me, the only other employees were apprentices and they weren't billing clients for their work (quite commendable, since they acknowledged that it's unfair to charge full contract rates for inexperienced work). So the only money the company made was from billable time that the two of them (the business owners, not the apprentices) put in while also doing the business-development side of things. When I took that into consideration, it seemed a bit more reasonable. (UPDATE: They clarified for me that this was indeed the case.)

I had received offers from two other companies (one here in Baltimore, the other a remote position) and both were in the same ballpark, so I wondered if maybe the only jobs that were paying in the $100k range were in Silicon Valley making feline timesharing apps and you had to work 29 hours a day, 8 days a week. However, Alicia told me I should check payscale.com. When I answered their questions (location, experience, etc), this was what I saw:

Payscale.com results for a software developer in Baltimore

Well, shit. Now I'm really confused. After checking out the various graphs beneath that one, I confirmed that these numbers are for Ruby developers in Baltimore with experience similar to mine. So maybe I am getting paid 20% less than I should be?

Grab that cash with both hands and make a stash

So now I'm actually asking everyone out in the open. I'd like to know how much Ruby/Rails developers with a decade of experience are making in a full-time position. If you work in the industry, if you know someone who does, if you hooked up once with someone who does, I just want to know (well, not about the hookup, just the salary thing).

I don't want to know for the purpose of matching a person with a dollar amount. I'm more interested in finding out on a statistical level.

I'm also not asking because I'm looking for something better. I like my job. The people I work with are great. I'm curious because I'll likely be asking for a raise at my next review, and I'd like to have some numbers ready for that. However, if you're offering a significantly higher amount, you might be able to convince me. ;-)

Just as a closing thought: companies capitalize on the fact that people are afraid to talk about how much they make. It means that they can pay different people different salaries to work in the same position because they'll never know. It contributes to wage gaps based on gender and race. Help yourself and others in your position. Talk about it. By all means, tweet about it (I'll be monitoring the #DevSalary hashtag on Twitter or you can tweet directly at me).

My experience at RubyNation

Jun 07, 2014 @ 11:22pm

Yesterday and today, I attended the RubyNation conference in Silver Spring, Maryland. One of my goals this year was to attend more conferences because I've been trying to meet more people in the community. I attended BohConf last year in Baltimore and it was great, even though it didn't have a Ruby focus.

I suppose I'll get this bit of personal information out of the way because it's very relevant to almost the entire post: I suffer from generalized anxiety disorder. I go to great efforts to mask it because I don't want others to have to deal with it, but the idea of being around a lot of people, quite frankly, terrifies me. Thankfully, RubyNation is a small conference (only 250 attendees according to their website), so I figured it'd be a good place to start and asked if my company would buy a ticket for me. They ended up sponsoring the conference, so they shut down the office on Friday and we all went.

Day 1

I live about halfway between Baltimore and Silver Spring, so I drove down to the conference that morning. I mostly kept to myself or tried to stick with my work posse because being around people I know helps ease the anxiety.

The conference was really great. The presentations were fantastic. Sarah Allen gave a fantastic opening keynote, followed by Eileen Uchitelle talking about ActiveRecord's crazy association logic. A little bit after that, Davy Stevenson gave an awesome talk about the science, art, and craft of programming, which really hit home with me. It was pretty awesome to see three of the first four presentations being given by women.

Everyone was very friendly, but I still felt very nervous about talking to anyone, so I kinda sat and waited for people to approach me or tagged along with my workmates.

We broke for lunch around 1pm, which I'd been dying for because in the scramble to get ready to go I forgot to eat breakfast. My company took about 20 people to Nando's (by the way, excellent food, but overpriced for the quantity and level of service). We couldn't get a bunch of tables together so we broke into groups of 3-4 per table.

I got to sit with Florian Motlik and Russell Osborne. They're really awesome people and I really enjoyed talking with them. Russell actually reminds me a lot of Hampton Catlin, but with a beard. We took a #RubyFriends photo and everything. It was fantastic.

Spoiler alert: anxiety wins

After lunch, we went to a presentation about machine learning with Ruby. During the talk, the presenter mentioned the sexmachine gem, which claims to be able to tell whether a name is male or female. I'd heard of this gem before, so my first thought was "Oh, right, that gem that thinks I'm a woman".

When from the time I started preschool all the way up until my adult life, I heard "Jamie is a girl's name" a lot. A lot of bullies found it fun to torment me because I had the audacity to be given a "girl's name" at birth.

I tell you this solely because this was the presenter's very next slide:

http://i.imgur.com/yxg3Pzf.jpg

"jamie, female"

Notice that the name "Kim" is labeled as "mostly female", but it shows my name as unequivocally "female". Given that it took a lot of effort to manage my anxiety at the conference already, that's all it took to trigger an attack.

I gathered my things as calmly as I could and headed straight to my car. The day was over for me before 2:30pm.

Day 2

I laid in bed this morning wondering if I should even go back today. What if a similar thing happens? What if I'm just too afraid to talk to anyone? Then I'll have wasted all that time just so I could feel terrible about myself.

I think I felt guilty because my company paid for me to be there. Whatever the reason, I got out of bed, showered and went back. I actually managed a decent mood, too.

I'd talked to my fiancée and my roommate last night about my experience yesterday and they told me to remember to take breaks. I realized I hadn't taken any breaks away from the crowd yesterday, so today I did and it really, really helped. They both texted me to make sure I was okay, too. That's a nice feeling.

For lunch, I went to lunch with one of the guys from work and several other people I'd never met (including the aforementioned opening-keynote speaker, Sarah Allen) and we chatted about Ruby, JavaScript, Apple's new Swift programming language, Rails, and the US government — not necessarily in that order. Two out of two lunches at this conference went perfectly.

Nothing else super eventful happened for the rest of the conference. It was just a pleasant time all the way up until I left. Great talks, great people, hell they even served cheesecake! Okay, so one super eventful thing happened. :-)

Jamie at the Bat

The conference experience was totally worth it. I managed to meet some new friends despite my aversion to actually talking to people. I got beaned on my first at-bat, but I stepped back up to the plate and, though I didn't hit it out of the park, I did at least make it on base. I don't know why I went with a baseball metaphor, but I'm keepin' it.

The Seven Methods of Highly Introverted People

For other programmers who are introverted and/or suffer from social anxiety, I absolutely recommend going to a conference. A small one worked well for me. Maybe something like RailsConf or RubyConf would've been significantly more stressful, I'm not sure, but it's tough to imagine a small conference being worse for social anxiety than a large one.

I was lucky enough to have one so close to my house, but Katrina Owen told me in a Ruby Rogues Parley thread that one thing that helps her at conferences is to get a hotel close to the venue. This way, if you start to get overwhelmed, you have a place you can retreat to. I plan to do exactly this if I go to any conferences that are outside of my backyard.

There are almost certain to be moments you'll struggle with. Take breaks. Take breaks proactively and away from people. It's like dehydration: by the time you realize you're thirsty, you're already dehydrated. The later you realize you need a break, the longer break you'll need to calm down.

You'll likely need to be alone for a while afterward and that's okay. Ever since I got home, I've been in the basement avoiding everyone. It's how I unwind after being around people all day.

And now I'm exhausted, so I'm going to bed.

Rails idioms considered harmful

May 17, 2014 @ 08:17pm

I'm not sure if it's Rails itself or the community by which it is surrounded, but something encourages us to treat our Rails apps as if they are simply a web interface to the database. Consider the idiomatic Rails create controller action:

class ThingsController < ApplicationController
  def create
    @thing = Thing.new(params[:thing])

    if @thing.save
      redirect_to @thing, notice: 'Thing was saved.'
    else
      render :new
    end
  end
end

The good news is that, in a lot of cases, this is about all you need. If you're adding a product to your e-store's catalog, publishing an article, or any other time you're just giving the app some data to be presented later, it's perfectly reasonable to treat it as nothing more than an SQL INSERT statement wrapped in a web request.

The problem I see is when people essentially copy and paste this code (whether they actually copy/paste or rewrite it with little to no modification, the end result is the same) for absolutely every create action in their app. If, instead of a ThingsController, this were a UsersController, would you still treat it as a simple CRUD controller?

The reason I'm writing this is that, at work about a week ago, someone suggested I do just that, and it's been bothering me. At the domain level, "creating a user" tells me absolutely nothing. From the user's perspective, they're not creating a user; they are the user. They're creating an account or, put another way, registering.

License, registration, and proof of email address

I've tried renaming the User class to UserAccount or simply Account in the past and it feels awkward. What if other users can be added to the same Account for payment-consolidation purposes? How do you reference the user themselves if calling the registration process "creating a user account", which simply INSERTs a record into the user_accounts table?

First, I've stopped thinking of a lot of scenarios like this as simple CRUD. When a user registers with your app, you're not simply inserting a record into the database. After the user record is saved, you'll usually send a welcome email or an email asking the user to click a link to confirm their email address. Maybe you'll add them to a mailing list or create some default data for them.

Where would you put all of that logic? If you put it into the controller, then you have to duplicate it all any time you add a user outside the normal registration process. This could happen through the admin interface, in a Rails console, a user-invitation process, etc.

A lot of apps I've worked on put that logic in after_create callbacks on the model instead. When setting up associated models, this is definitely an advantage over putting it in the controller because whether you add the user through the normal registration process or through the admin interface, the default data will be setup with no duplication. Sounds great, right? It does until you realize that every time you test a persisted user model you're inserting, at a minimum, twice as many objects into the database as necessary. We maintain an app at work that performs over 10 inserts per user because it follows this pattern (it's okay, we didn't write it like that; it came to us that way).

I consider both of these approaches to be harmful and yet both are used in countless Rails apps that people pay actual money for.

Okay, rocket surgeon, where do I put this crap?

The way I've been dealing with user registrations — and several other processes that aren't basic CRUD — by instantiating what basically amounts to a fake ActiveRecord model. You talk to it somewhat like an ActiveRecord model, but it stores the data elsewhere.

class UserRegistration
  include ActiveModel::Validations

  attr_reader :user

  validates_presence_of :email
  validates_presence_of :password
  validate :passwords_match

  delegate :email, :password, :password_confirmation, to: :user

  def initialize(attributes={})
    @user = User.new(attributes)
  end

  def self.create(attributes={})
    new(attributes).save
  end

  def save
    ActiveRecord::Base.transaction do
      if valid? && user.save
        create_defaults
        UserRegistrationMailer.welcome(user).deliver
        return user # We don't want to return the result of the transaction call
      end
    end
  end

  def passwords_match
    unless user.password == user.password_confirmation
      errors[:base] << 'Passwords do not match'
    end
  end

  def create_defaults
    user.foos.create
    user.bars.create
  end
end

This way, if you use a UserRegistrationsController that uses the idiomatic Rails create action as shown at the top of the article, it will save the user just as you would normally, insert associated models, and send out the welcome email. This makes sure that inserting a user model is as lightweight as possible (the validations are in the registration, so are not run during testing) while still allowing you to insert user records (including fully registered users) easily from elsewhere in the app, such as a Rails console.

As a bonus, if you were including other information than just the user data, such as payment information, you could pass it to the UserRegistration and sort it out there.

class UserRegistration
  # ...

  attr_accessor :user, :credit_card

  def initialize(attributes={})
    @credit_card = CreditCard.new(attributes.slice(:card_number, :expiration_date, :cvv))
    @user = User.new(attributes.slice(:email, :password, :password_confirmation))
  end

  def save
    if valid? && user.save
      payment_gateway.add user
      payment_gateway.charge user, AMOUNT
    end
  end

  def payment_gateway
    @payment_gateway ||= PaymentGateway.new
  end
end

See how in the initialize method, we split out the attributes for the separate models. Without this object to wrap the two models, you'd have to use accepts_nested_attributes_for, which is poor form. Sure, there's a little extra code to maintain, but this isn't a bad thing. Since you're doing validations here, you don't need to do them on the User model. This lets you forget entirely about bypassing validations when adding the user from the admin interface and will increase the speed of any tests that hit the database.

Ask customers for information you actually want

Apr 19, 2014 @ 06:15pm

I came across this tweet from Kurtis Rainbolt Greene:

Tweet about names

If you've ever interacted with a form on any website, you've seen registration forms that ask for things like first and last name as well as your gender. The form asks for first and last name separately so the app can address you by your first name and gender so it can use the "correct" pronouns.

"Correct"

I put "correct" in quotation marks because as much as companies and programmers would like to believe we're doing the right thing by addressing people by their first names or using masculine pronouns just because someone said they were male, what we're actually doing is pigeonholing users into our narrow view of the world.

Hello, #{first_name}!

Patrick McKenzie wrote a blog post a while back entitled The Falsehoods Programmers Believe About Names that I'm not going to attempt to duplicate here. It's pretty much the best article ever about how everything you think you know about names is wrong.

The TL;DR is that names in various parts of the world don't fit our Firstname Lastname convention. In fact, there may be more names in the world don't fit our convention than do.

Additionally, in many cultures, a business addressing a customer by their first name is considered too informal and can insult them or at least make them uncomfortable. Either way, you've lost that person as a customer.

EDIT: Kurtis also recommended that I link to the W3C article titled Personal names around the world. It's a fantastic article that provides background for a lot of the falsehoods listed in Patrick McKenzie's article linked above. It's fairly long, but so worth it.

Download gender binaries or compile from source

When you use the words "male" and "female", you're not referring to gender at all. If you are, you're using the wrong words. Rather, male/female refers to a person at the biological level, which is a weird fucking thing to ask about in your registration form for your new social app. Katrina Owen and I discussed this in a Ruby Rogues Parley thread once and it's really stuck with me since then.

The gist of it is: We're not animals. We're civilized (more or less) people that have layers of abstractions on top of our biology. We're not "male" or "female", we're "men" and "women".

And this brings me to my next point: even if you went with man/woman rather than male/female, you're still not covering all the bases. There are a lot of people in this world who don't fit the man/woman archetypes (regardless of whether that matches their biology). My long-time friend and current roommate is one shining example of this. This friend (who will remain nameless until I get their permission to use their name) struggles pretty constantly with their gender identity. They are neither masculine nor feminine (until a cat walks in the room, then they become Agnes from Despicable Me), but have to pick one very often. And a lot of times, people in the same situation feel excluded because the world wasn't designed to work for them the way it has been for everyone else.

Basically, we're making people feel like shit just so we can figure out whether to use "him" or "her" when referring to them. That is literally the only reason. Even if you don't care how your customer feels, this hurts your bottom line by encouraging them to find a competitor that does cater to them.

Okay, so my shit's broke. Please to tell me how to fix?

We ask people for first and last name so we can infer how to address them. We ask for their gender so we can infer which pronouns to use for them.

Both of these are wrong. The thing they have in common (well, besides being wrong) is that both are used to infer something about them. Rather than trying to guess details based on metadata, ask for the information you want directly.

That is, don't chop up their name to guess at how to address them. Ask them how you should address them.

Don't use third-person pronouns based on a multiple-choice gender response. Ask them what pronouns to use when referring to them. And don't limit pronouns to him/her. Don't even limit it to him/her/them. If you want to be truly inclusive and respectful, get a freeform response for every single pronoun they want used:

  • Subject pronoun (he/she/they)
  • Object pronoun (him/her/them)
  • Possessive pronoun (his/her/their)
  • Possessive gerunds (his/hers/theirs)

The examples in parentheses are just that: examples. Let your customer enter a freeform response in case the pronouns they want aren't there (trust me, cisgender people, there are pronouns transgender people use that you haven't heard of).

I've mentioned this to a few people before and almost universally, their knee-jerk reaction was "But using male/female is so much easier!" This is simply not true. It's slightly (seriously, only slightly) easier for building the form, sure, but then you have to use a function or helper all over your app to select the "correct" pronouns every time you need one.

#{user.name} updated #{possessive_pronoun_for(user)} profile.

But wait, here's the one with user-specified pronouns:

#{user.name} updated #{user.possessive_pronoun} profile.

Since user.possessive_pronoun is a simple reader method and doesn't check anything else, there's no logic that needs to be tested (you are doing TDD, right?). You're just getting data from the model.

Besides, if you put in that little bit of extra effort to offer proper pronoun support, you're very likely to have a lot of very happy customers who will feel included rather than looking at your registration form and saying "Oh, another fucking 'male/female' select box." They'll tell their friends that your service is inclusive and it will forever be known as "the service that doesn't hate marginalized people".

No More Octopress

Feb 16, 2014 @ 04:01am

I've been using Octopress for a while to serve my blog and as much as I like the fact that it handles a lot of things for me, I didn't like publishing with it. Posting an article took longer than it needed to because I had to:

  • leave the browser
  • open a terminal
  • run a rake task (which means making sure I'm using the right Ruby version and gemset)
  • open up my editor
  • write my article
  • run rake preview and check it in the browser to make sure it doesn't look like crap
  • git add --all
  • git commit -m 'blah blah blah'
  • git push heroku master

It's a hassle to do this and if you wanted to edit an article later, you had to go through that entire process again. This is the reason I've mostly ignored the blog. I'll very likely be posting more often now that it can all be done from within the browser without all those context switches (editor, terminal, browser).

Powered by Perpetuity

The ORM used to manage all of my database queries is Perpetuity, the Ruby Data Mapper-pattern ORM that I wrote. This was the fun part, actually. I got to play around and tease out a few patterns that might be useful. I even used the Postgres adapter I've been working on.

Notice the id formats of the articles: for this particular article the id is no-more-octopress. This is the actual id of the article in the database and the article model itself doesn't even know about it. This was so simple to do with Perpetuity it made perfect sense to use here:

Perpetuity.generate_mapper_for Article do
  id(String) { title.downcase.gsub(/\W+/, '-') }
  attribute :title, type: String
  # ...
end

The id(String) { ... } DSL call lets the database know the generated value is a String and will be generated by executing the given block against the object when it is inserted.

This way, I've got the SEO-friendly URLs without having to resort to putting slugs on the model. HTTP concerns shouldn't be on your model.

Markdown format

The format of the articles is Markdown, just like it was in Octopress, and they're converted to HTML on the fly. I also threw in a little caching so it's not running that conversion on every page load.

RSS/Atom Feed

To be honest, I didn't even check to see if there were any decent Ruby RSS-feed generators, but I figured that most of them would be built to work with ActiveRecord and similar libraries so I have my doubts that they'd work with PORO articles anyway. So I built my own. It wasn't all that difficult, really. It's just an object that you pass the values to and its to_xml method spits out the XML.

I'll probably extract it at some point but I just wanted to get a feed working. My blog has a few RSS subscribers and I didn't want to break that for them, although the articles will probably appear to have been updated.

Maybe I'm wrong and existing RSS generators would work, but I'm not all that concerned with it. This was probably one of the most fun parts of building this blog engine (even though it was XML) because it's something I'd never done before.

Payload size

On Octopress, my blog index payload was about 350kB. Now, I'm at around 50-60kB, including assets. This is a HUGE win for viewing on mobile devices (though I don't have mobile-friendly styles yet; I'm working on that). I did this by:

  • Removing social buttons and replacing them with links
  • Writing only the CSS styles I needed on semantic markup
  • Using only jQuery, Rails UJS, and Highlight.js for syntax highlighting on code examples
  • Serving minified and gzipped assets
  • Running the HTML through Rack::Deflater to reduce the HTML payload

Moved off of Heroku

This doesn't have anything to do with abandoning Octopress, but I also moved off of Heroku. This decision was primarily because the startup time of a Rails app on a sleepy Heroku dyno would have a severe impact on SEO. Adding a second dyno would keep it from going to sleep, but I'm not going to pay $35/month to host a blog. It's entirely possible that RSS subscribers would hit it often enough to keep it from going to sleep at all, but I wanted to try setting up a VPS to handle this anyway. It's been a while since I've had to do any ops stuff.

This blog is now running on my $5/month DigitalOcean droplet. This was a droplet I already had running, so the monthly fee isn't additional. My benchmarks show that this droplet far outperforms a Heroku dyno, too, so hopefully this improves SEO a bit — especially considering the payload benefits I mentioned above.

Le Fin

All in all, this was fun. I'd built several toy blog apps before and even Perpetuity's spec suite uses an Article object in damn near every example (they have a diverse range of attribute types: String title/body, Fixnum views, Time timestamps, etc), but actually deploying an app that uses Perpetuity's Postgres adapter was awesome to see.

I've still got some styling work to do, too, so that'll be fun. I'm not a designer, so if someone wants to contribute a design (even just a concept graphic), I'd be forever grateful. :-)

TwitterGithubRss