NCZOnline - The Official Web Site of Nicholas C. Zakas

Subscribe to NCZOnline - The Official Web Site of Nicholas C. Zakas feed
The Official Web Site of Nicholas C. Zakas
Updated: 2 hours 32 min ago

My (somewhat) complete salary history as a software engineer

Mon, 10/29/2018 - 20:00

It’s 2018 and somehow women are still getting paid less than men, even in supposedly progressive industries like software.[1] Whether that be from companies offering women less than men for the same position, women being less likely to negotiate or less successful in negotiations, or any of the other myriad reasons, the results are still the same: women make less than men for the same job. That’s something that shouldn’t be happening in today’s world and it’s up to us (read: men) to step up and make things right. This is my attempt to do just that.

Why am I doing this?

Longtime followers know that I’ve been dealing with serious health issues for several years. Two and a half years ago I had to stop working to focus on my health and it will likely be a couple more years before I’m able to even consider working a full-time job again. The people responsible for my last compensation package have long since left that company. That puts me in a unique position where I am not beholden to any past employers, and any future employers are far enough into the future that the information I’m sharing here will be mostly useless by then. Plus, as a white man, I know I’m going to be able to negotiate for my salary without backlash[2] when I do start working again. As such, the information in this post is more valuable to others than it is to me.

As an aside, I’ve been annoyed that throughout my career I’ve been lectured many times to avoid discussing my compensation with colleagues. Usually it’s with the warning that, “not everyone is getting what you’re getting, and we don’t want to hurt feelings.” It took me a long time to realize that the “hurt feelings” they’re talking about come from an overall lack of transparency into the compensation process, and that simply explaining why people are compensated in certain ways would be a better solution than to hide all of the information from everyone. Yes, there will always be people who think they deserve to be making more but who don’t actually deserve it. That seems like a great way to communicate that they aren’t doing a good enough job and figure out ways to improve.

The bottom line is that nothing gets better unless people are willing to share information. And while I could just share my last salary, I don’t think that’s very useful, especially when compared with the variety of already-available sources of information online. No, to be useful, I felt like I would need to reveal my entire salary history so people can determine for themselves if they’re seeing enough improvement in their salaries over time.

Where did this data come from?

The data in this post comes from the following sources:

  1. My memory. Yes, memory is fallible, but there are some data points that are so important on an emotional level that they tend to stick in my brain. I’ll point those out.
  2. Offer letters. As my offer letters post-2006 were always emailed to me, I’ve been able to be 100% sure of those details. Prior to 2006, my offer letters were always mailed to me, and I have no record of those.

Where my memory fails me and I don’t have an offer letter, I’ve made an attempt to guess the salary range I had at the time.

The data

The table below contains all of my salary (and some other compensation history). I’m including what I believe to be data relevant to evaluating the compensation include the year I received the salary, the years of experience I had at the time (YOE), the starting and ending salary to take into account raises, and any signing bonus (Signing $) and stock options (Options) I might have received. Any amount with a question mark indicates that I’m guessing. I did not include any restricted stock units I might have received because I only ever received them at Yahoo as part of my initial offer.

Year YOE Company State Title Starting $ Ending $ Signing $ Options 2000 0 Radnet, Inc. MA Webmaster $48,000 $55,000 - ? 2001 0 Radnet, Inc. MA UI Developer $62,500 $62,500 - - 2001 0 MatrixOne, Inc. MA UI Designer/Developer $68,000? ? $2,000 ? 2003 3 MatrixOne, Inc. MA Senior Software Engineer ? $75,000? - - 2005 5 Vistaprint, Inc. MA Lead Software Engineer $82,000? $98,000 - 3,000 2006 6 Yahoo, Inc. CA Senior Front-end Engineer $115,000 ? $10,000 3,500 2008 8 Yahoo, Inc. CA Principal Front-end Engineer ? ? - - 2011 11 Yahoo, Inc. CA Presentation Architect ? $165,000? - - 2013 13 Box, Inc. CA Staff Software Engineer $175,000 ? $25,000 50,000 2014 14 Box, Inc. CA Principal Architect $208,000 $220,000 - - Job Details

The data alone doesn’t really tell the full story, so here are the details around each position. I’ve also included how I came to work at each company, as I think it’s important to recognize blind resume submissions from having contacts as a company.

Radnet (2000-2001)

My first job out of college was at a small startup in Wakefield, MA called Radnet, Inc. I got this job because the woman who used to babysit me as a child was running human resources at the company. My official title was webmaster, and I thought I would be coming in to run the company website. As it turned out, between the time they offered me the job and my starting day, they had hired someone to oversee both UI development and the website. As it turned out, I would never manage the website and instead would spend time making JavaScript components for the company’s web application.

I know that my starting salary was $48,000 (about $70,284 in 2018 dollars) because I was very excited about it. After spending summers working jobs that ranged from $2/hour to $6/hour, this seemed like an incredible amount of money to me. A few months in, they gave me a raise to $55,000 because I was doing a good job. Towards the end of 2000, they believed the company would be bought and so they changed my title to UI Developer and upped my salary to $62,500 with the belief that an acquirer would immediately fire the “webmaster” and ensuring I’d benefit from the acquisition.

As it turned out, the company never got bought and so it shutdown in January 2001. I never really saw much of the $62,500, and eight months after I had started my first job, I was unemployed.

Note: I did receive stock options for this position, but I don’t remember what they were. I didn’t really understand what stock options were at the time so that information never stuck in my brain.

MatrixOne (2001-2005)

When Radnet closed down, my manager ended up at MatrixOne and asked if I would like to join him. I had enjoyed working with him at Radnet so I accepted. It’s important to understand that this was during the dot-com crash and there weren’t a lot of tech jobs to be had in Massachusetts at the time. I considered myself lucky to have formed a good relationship that allowed me to find a new job fairly quickly after Radnet shut down.

I don’t remember all of the details but I’m pretty sure my starting salary was close to $68,000 ($96,814 in 2018 dollars). I’m also reasonably certain that I got a small signing bonus, maybe around $2,000, that my manager negotiated for me. I also received some stock options, but once again, I didn’t really understand what they were and so didn’t even register them as part of my compensation. It didn’t matter, though, because the company stock was never again as high as the day I joined. I was never able to exercise options, even when I got some repriced options later in my career there because the stock only ever went down. (Eventually the company would be bought out by a competitor.)

My salary didn’t improve much there because the company was in perpetually poor financial health. There was a salary freeze in place almost the entire time I was there. I survived four rounds of layoffs. I was eventually “promoted” to the position of Senior Software Engineer, but it was a promotion in title only. There was no increase in salary (because of the salary freeze) and no change in my responsibilities (because the organization was weird). It was just a pat on the back to say, “good job, please don’t leave.” Spoiler alert: I left as soon as I could.

Right before I left, I did get a salary increase to around $75,000. It wasn’t enough to make me want to stay.

Vistaprint (2005-2006)

I often refer to my position at Vistaprint as my first real software engineering job. It was the first time I applied for a software engineering job without having a connection at the company; I just sent my resume in to their email address. I reported into the engineering organization (as opposed to the design organization in my prior jobs), and I got what I considered to be a good offer. The company was pre-IPO, and I was excited to get 3,000 stock options. (By this time, I actually understood what stock options were.)

I don’t recall the starting salary but I suspect it was around $82,000 ($105,867 in 2018 dollars). I definitely recall the ending salary as $98,000 for a few reasons. First, I was complaining a lot about the boring project they had assigned me to so I expected that would eliminate me from any serious raise considerations. I was shocked to get a raise and even more shocked at the amount. Second, I was bummed they didn’t give me the extra $2,000 to make an even $100,000. Last, I was secretly interviewing with both Google and Yahoo, and upping my salary meant that I could use that number when it came time to talk compensation with them.

I was only at Vistaprint for a little over a year before deciding to move to California to work for Yahoo. Vistaprint did go public while I was there, but since I left after a year, I didn’t see much from those stock options.

Yahoo (2006-2011)

Yahoo’s initial offer was the best I had received up to that point. In addition to a $115,000 base salary ($143,833 in 2018 dollars), it included $10,000 signing bonus, 3,500 stock options, 1,500 RSUs, and relocation expenses. This was the first time I tried to negotiate for a higher starting salary and was summarily rejected. At least I tried.

I ended up at Yahoo through a circuitous route. I had heard that Yahoo was using my first book, Professional JavaScript for Web Developers, to teach JavaScript at the company. As such, I had an open invitation to stop by the campus if I was ever in the area. I had traveled to Mountain View to interview Google (they had found me through my second book, Professional Ajax) and so reached out to the folks at Yahoo to meet up. I didn’t realize that conversation would turn into an invitation to apply to work at Yahoo as well.

I don’t remember a lot of my pay details after I joined. Being at Yahoo for almost five years, I got several raises and two promotions, so my pay did keep increasing. All of that information was sent to my Yahoo corporate email address, and as such, I no longer have any of the documentation. That was intermixed with periods of layoffs and salary freezes. My initial stock options ended up worthless because the company stock price never again reached the level it was at when the options were priced. I would later get repriced stock options and more RSUs, but I don’t have specifics on that.

By the time I left, I suspect I was making around $165,000 based on how I felt about the offer from Box.

It’s worth noting that I left Yahoo to try to start a company with some friends and so didn’t have a regular salary for about 18 months.

Box (2013-2016)

My offer from Box was also strong. The starting salary of $175,000 ($189,415 in 2018 dollars) was more than enough to make me happy at the time, and the offer included 50,000 stock options. Box was pre-IPO so that high stock option allocation (which I was told was higher than what they usually gave people at my level) was a big consideration for me. I negotiated for a $25,000 signing bonus, as well.

As part of my consulting business, I would regularly give talks at different companies. I agreed to give a talk for free at Box because a friend worked there and mentioned that they were having trouble managing their growing JavaScript code base. I spoke with a few people after the talk, including the VP of engineering, and we decided to explore if working at Box was a good fit for the company and me. Through several more discussions, it seemed like a good opportunity to get back into the stability of a regular salary with some interesting problems to tackle.

My memory is a bit hazy around what happened between joining and the end of my time at Box as this was the period when my health was on a steep decline. I think I got one raise as a Staff Software Engineer about three months after I joined, and was informed of being promoted to Principal Architect six months after I joined (although I wouldn’t get the pay increase for another six months). I’m reasonably certain the promotion pay increase bumped me to $208,000. I recall clearly that I got one last raise to push me to $220,000 during 2014 because I had started working from home full time due to my health and I thought it was very nice of them to give me a raise regardless.

I left Box when I was no longer physically able to work from home.


In my sixteen year career, I averaged a pay increase of $10,000 per year, even when taking into account several years of salary freezes at MatrixOne and Yahoo. As such, I suspect I’d be making around $250,000 if I was working full time today.

It’s also important to understand that I never asked for a raise and only negotiated other details occassionally (as mentioned in the post). I never really felt comfortable with negotiations prior to working for myself, and generally was happy with the offers I received.

With the exception of my one year at Vistaprint (during which I was a grouchy pain in the ass), I was consistently reviewed as a top former at my position. I wasn’t put on any sort of improvement plan and most of my feedback had to do with improving interactions and communication with colleagues. And again, with the exception of Vistaprint (because…pain in the ass), I took the feedback to heart and worked to improve in those areas.

Being single and not having a family to support throughout my entire career meant that I had more options. I could afford to take salary that was lower than what I wanted or could get elsewhere, and I could also afford to walk away from valuable stock options (such as with Vistaprint) to find a job that was more fulfilling. I recognize that not everyone has that option, so I think it’s important to make my situation clear here.

I have two hopes from sharing this information. First, I hope that having this information will make it easier for women to understand how much they should be paid for similar work and just how their pay should be increasing throughout their career. Second, I hope that other men who are in a similarly independent position will also share their compensation history to benefit others.

We can all be better if we’re willing to share.

  1. By the Numbers: What pay inequality looks like for women in tech (
  2. Women Know When Negotiating Isn’t Worth It (
Categories: Tech-n-law-ogy

Extracting command line arguments from Node.js using destructuring

Mon, 10/01/2018 - 20:00

If you’ve worked on a Node.js command-line program, you were probably faced with the extraction of command line arguments. Node.js provides all command line arguments in the process.argv array. However, the contents of the array aren’t what you might expect.

What’s in process.argv?

The first two items in process.argv are:

  1. The path to the executable running the JavaScript file
  2. The path of the JavaScript file being executed

So the first command line argument is the third item in the array. For example, consider the following command that runs a Node.js program:

node index.js --watch

The contents of process.argv will look something like this (depending on your system and file root)

  1. /usr/bin/node
  2. /home/nzakas/projects/example/index.js
  3. --watch

While the first two items in the array might be useful to some, chances are that you’re only interested in --watch. Fortunately, you can use JavaScript destructuring to pick out just the command line arguments you want.

Using destructuring to extract arguments

Using JavaScript destructuring, you can separate the process.argv array into pieces and only use what you need. For example, this code separates the array into its three parts:

const [ bin, sourcePath, ...args ] = process.argv; console.log(args[0]); // "--watch"

Here, the bin variable receives the Node.js executable path, sourcePath receives the JavaScript filepath, and the rest element args is an array containing all of the remaining command line arguments.

You can take this one step further and just omit bin and sourcePath if you have no use for them:

const [ , , ...args ] = process.argv; console.log(args[0]); // "--watch"

The two commas at the beginning of the pattern indicate that you’d like to skip over the first two items in the array and store the remaining items in the args array. You can then further process args to determine what to do next.


While the process.argv array is a bit confusing at first, you can easily slice off just the information you’re interested in using JavaScript destructuring. Destructuring assignment is ideally suited for extracting just the information you want from an array.

Categories: Tech-n-law-ogy

Detecting new posts with Jekyll and Netlify

Mon, 09/03/2018 - 20:00

This blog has long featured the ability to subscribe by email, so you could get an email notification when a new post was published. I’ve used various services over the years to achieve this, first with FeedBurner and later with Zapier. As I’m a do-it-yourself kind of person, I never liked relying on external services to determine when a new post appeared on my blog. I figured I would never be able to build my own system When I moved this blog from the dynamic Wordpress to the static Jekyll[1]. Still, it seemed like a waste to have a service keep polling an RSS feed to see if it changed. After all, I know when my blog is being built…why can I just check for a new post then? It took me a little while and several iterations but eventually I figured out a way.

Step 1: Creating a data source

Most services that check for new blog posts use RSS feeds to do so. I didn’t want to use the RSS feed for two reasons:

  1. Parsing RSS is a pain
  2. Bandwidth concerns - My RSS feed is quite large because I include full post content

So I decided to create a small JSON file containing just the information I was interested in. This file lives at /feeds/firstpost.json and contains metadata related to just the most recent post on the blog. Here’s the Liquid template:

--- layout: null --- { {% assign post = site.posts.first %} "id": "{{ post.url | absolute_url | sha1 }}", "title": {{ post.title | jsonify }}, "date_published": "{{ | date_to_xmlschema }}", "summary": {{ post.content | strip_html | truncatewords: 55 | jsonify }}, "url": "{{ post.url | absolute_url }}" }

This file includes just the information I need for any new blog post notification, which might include emails, tweets, Slack messages, etc. I’m using the absolute URL for the blog post as a unique identifier, but you can use anything is sufficiently unique. (You can always add or remove any data you may need if this dataset doesn’t fit your purposes.)

Credit: This format is loosely based on JSON Feed[2] and the code is partially taken from Alexandre Vallières-Lagacé’s Jekyll JSON Feed implementation[3].

Step 2: Deploy the data source

This is very important: the data source must already be live in order for the detectiong script to work correctly. So before going on to the next step, deploy an update to your site.

Step 3: Create the new post detection script

The new post detection script checks the live data source against the one on disk after running jekyll build. If the id of the most recent post is different between the live and local versions of firstpost.json, then there is a new post. Here’s the detection script:

"use strict"; const fs = require("fs"); const fetch = require("node-fetch"); (async () => { // fetch the live data source const response = await fetch(""); if (response.status !== 200) { throw new Error("Invalid response status: " + response.status); } const currentFirstPost = await response.json(); console.log("Current first post is ",; // read the locally built version of the data source const newFirstPost = JSON.parse(fs.readFileSync("./_site/feeds/firstpost.json", { encoding: "utf8" })); console.log("New first post is ",; // compare the two if ( !== { console.log("New post detected!"); // do something for new posts } })();

This script uses node-fetch to retrieve the live data source and then compares it to the local data source. If the id is different, it outputs a message. How you respond to a new post is up to you. Some options include:

  • Send an email notification
  • Post a tweet
  • Post a Slack message
  • Emit an event to AWS CloudWatch (this is what I do)

The most important part of the script is that it needs to be executed after jekyll build and before the site is deployed.

Step 4: Updating Netlify configuration

One of the advantages that Netlify[4] has over GitHub pages for Jekyll sites is the ability to modify the build command. The easiest way to do that is by using a netlify.toml file[5] in the root of your site. In that file, you can modify the build command. Here’s an example:

[build] command = "jekyll build && node _tools/newpostcheck.js" publish = "_site"

The command entry specifies the build command while publish indicates the directory into which the built web site files should be placed (most Jekyll builds use _site, and this is Netlify’s default). The command should be updated to run the new post detection script after jekyll build.

Note: You must have a package.json file in the root of your repository to have Netlify install Node.js and any dependencies (such as node-fetch) automatically.

Step 5: Deploy to Netlify

The last step is to deploy the changes discussed in this post. When Netlify builds your site, the new post detection script will be executed and you will be able to respond accordingly. It’s a good idea to run the script once with a new post and observe the logs just to make sure it’s working correctly before hooking up notifications.


The advantages of using a static site generator (such as Jekyll) sometimes means giving up a big of convenience as it relates to changes on your site. While dynamic solutions (such as WordPress) might offer more hooks, static solutions are often capable of similar functionality. New blog post notifications are important for most blogs and being able to achieve them using Jekyll is one more vote in favor of static sites.

While this post focuses on Jekyll and Netlify, the same approach should work for any static site generator and any deployment system that allows you to modify the build command.

  1. Jekyll (
  2. JSON Feed (
  3. jekyll-json-feed (
  4. Netlify (
  5. The netlify.toml File (
Categories: Tech-n-law-ogy