Discovery is the Problem

Discovery is the Solution

Explaining Exclusion in the Tech Industry to an 11-Year-Old

image

Son: Dad, you look upset.

Me: Yes. Here, this cartoon will explain.

Son: Someone said something you don’t agree with?

Me: Yeah.

Son: Well, just because you think they are wrong doesn’t mean they are. They are entitled to their opinion.

Me: Of course they are. But maybe let me try to explain why I think they’re wrong, OK? I think it’s important for you to understand me and think about what I’m saying, even you don’t end up agreeing.

Son: [Slighty annoyed.] OK, OK.

Me: I don’t really want to bring this up, because it’s a couple of years behind us now, but remember when you were at your last school, and you were having problems with some of the kids being hot and cold. Like being your friend one day and then locking you out the next day?

Son: [Looking down. Speaking quietly.] Yeah. [Irritated now.] Why are you bringing that up?

Me: Well, I have a question. Do you think those kids were cruel? Like really mean?

Son: [Still looking down. Upset.] No-o!

Me: Well, why do you think they did that?

Son: [Irritated.] I don’t know. [Pausing, then speaking more evenly.] I just felt left out a lot. We sometimes had fun and I felt like they were my friends. But other times they would talk about things I didn’t know about or didn’t care that much about, like “Transformers” or whatever.

Me: Do you think they felt more comfortable with each other, because they all had Russian parents. Like, closer to each other in some way, even if they didn’t realize it.

Son: Yeah, maybe. Yeah, I think that was part of it. They sometimes spoke in Russian to each other too. Or talked about places in Sheepshead Bay or someone’s birthday party or whatever. [Suddenly more forceful.] But that doesn’t mean all Russian people are like that.

Me: No, of course not. Most people are just more comfortable with what is more familiar to them. Like people they have a lot in common with. There is nothing wrong with that, but it can end up like it did for you, with one group sometimes leaving other people out in part because they just stay with what is most familiar and comfortable to them. They might not start out every day thinking, “We’re going to leave that other kid out of our game at recess today.” It just sort of happens because they act on their feelings without thinking about it much.

Son: So you’re saying it’s harder to include people more different from you. You have to think about it and work at it.

Me: Yes, exactly.

Son: But some kids are really mean too.

Me: Yes, that’s true. So there are a lot of different reasons people might leave other people out. Sometimes kids maybe just aren’t thinking much about it either way, and sometimes they are doing it more on purpose.

Son: Yeah. Like another thing that happens in school sometimes is the kids who are better at a game don’t want other kids to play, like basketball. Or kids who are really good at one subject get impatient when the teacher has to give extra help to some kids.

Me: So do you think your friends at school even noticed all the time what they were doing to you?

Son: [Looking down again. Upset.] No, maybe not.

Every Aspect of Your Project Is Design

image

In my personal projects, I’m not a world-changing software engineer. I tend toward developer tools, productivity tools, and personal scratch-an-itch projects (like, I suspect, many people). So it’s rare that I have an idea that is small enough in scope that I can finish v. 0.1 on my own, seems like it fills a hole, and seems like it could be useful to a relatively wider group of others.

In sofine, I hit on something I thought might have a wider appeal. While still targeted at developers, it solves a general use case with several possible applications. The project gives you a simple way to create and manage data collection plugins, and to compose them as you wish to return back unified data sets.

My original motivation was to be able combine the data I could get from scraping my Fidelity portfolio page with calls on the same stock tickers to multiple other web APIs. But I realized the same approach could also be used to build pipelines for machine learning, where you are building wide feature sets of attributes from multiple data sources for a set of initial keys. Or by a web developer chaining calls to Web APIs to build a single data set to display.

Because the project seemed to potentially have general utility, I wanted it to be as easy to start using, and as easy to continue using, as possible.

In pursuit of this noble goal, I decided to think hard about the design and to very strictly follow a few principles. These became apparent as I thought about the requirements, which themselves fell naturally from the use case described above.

I decided a user should be able to:

  1. Manage any number of data collection plugins in one way, but with no dependencies on each other and no possibility of collision
  2. Combine calls to them in one call, to support use in shell scripts, automation, piped expressions, and so on
  3. Retrieve one set of data from any number data sources
  4. Begin and continue to use the library with the minimum possible number of install and configuration steps

It was obvious to me once I formulated these requirements that the first one required very careful design, and the second was in fact the API for usage of the library.

But I didn’t realize the third one would raise design issues until I ran into them, at which point I had the obvious-in-hindsight realization that data sets returned by sofine were also an API.

The fourth point clearly concerned the unsexy issues of package management and configuration management (among other things), which we don’t always look at as design. But here too I ran into novel issues because of the goals of the project, and in solving them came to see these aspects as integral to project design.

In short, I learned that design matters, even if your application’s only interfaces are the command line, URLs, configuration files, data output, and the file system.

Designing the Call APIs

This was the first area of design I thought about, because it was the most apparent at the start (which is probably a lesson in itself). I wanted users to be able to chain Unix-style piped calls to multiple plugins from the command line. I wanted as few arguments as possible.

The first iteration looked like this:

python runner.py '-s fidelity arg_1 arg_val_1 | -s ystockquotelib'

Some design basics were already in place. I decided to avoid making runner.py executable, to save users from relying on a hardcoded shebang path. And I knew I wanted the sofine arguments to come first, followed by any additional arguments required by the plugin call itself.

But I’d already hit my first design issue. Using traditional single-letter short-form arguments for sofine would break as soon as any plugin took an -s arg of its own. I’d hit a namespacing issue. I decided the simplest solution was to prefix sofine arguments with --SF:

python runner.py '--SF-s fidelity arg_1 arg_val_1 | --SF-s ystockquotelib'

Designing Plugin Management

I hit another namespacing issue with plugin management. Early in development I had a few test plugins in a directory. But I knew that system wouldn’t scale to fulfill my first requirement: “Manage any number of data collection plugins in one way, but with no dependencies on each other and no possibility of collision.”

The simplest solution seemed to be to use the natural namespacing provided by the file system. So a plugin directory might look like this:

- plugins
  - scrapers
    - fidelity.py
    - schwab.py
  - api_wrappers
    - ystockquotelib.py
    - all_finance.py
  - db_wrappers
    - ratings_data.py

or this:

- plugins
  - finance
    - fidelity.py
    - schwab.py
  - municipal
    - city_1_data.py
    - city_2_data.py
  - ratings
    - muni_ratings.py

This design had several other benefits that convinced me it was a good approach. Plugin groups let users logically organize plugins by whatever criteria worked for them, as in the above examples. Separate directories per group also meant users could manage all plugins in one code repository from the root plugin directory, or have one repo per group. And as long as plugin calls now supplied the plugin name and its group, configuration would still only require one value, the plugin root directory.

This last point showed that the CLI API design and plugin management design were tightly coupled. But here the coupling was intuitive and didn’t reduce user flexibility (or code flexibility, as I confirmed by implementing the feature). You could manage plugins as you wanted, and still call them in any combination with a very simple interface, which now looked like this:

python runner.py '--SF-s fidelity --SF-g example arg_1 arg_val_1 | --SF-s ystockquotelib --SF-g example'

Designing the Data Output Format

The data output format also went through two iterations, again related to namespacing. The purpose of the library is to collect data from multiple data sources for a set of keys, so I initially decided on a data format like so:

{"APPL": {"price": 102.45, "52-wk-high": 178.32}}

But what if the fidelity and ystockquote plugins both had a field named price?

My first solution was to use the same namespacing I’d used to solve the plugin issue, prepending the plugin group and name to each field:

{"APPL": 
    {"example::ystockquotelib::price": 102.45,
     "example::ystockquotelib::52-wk-high": 178.32,
     "example::fidelity::price": 102.45}
}

This solved the problem, and also let users filter or group data based on the plugin that had provided it. But it also added a lot of repetitive bloat to the return data set for users who just wanted a flat data set, and returning a flat data set was in fact the stated goal of the library.

Thinking more about it I came up with the idea to use JSON arrays rather than objects, which would allow attribute keys to repeat:

{"APPL": 
    [{"price": 102.45}, {"52-wk-high": 178.32}, {"price": 102.45}]
}

Namespacing became an option that you could access by making a slightly different call. (Which led to other design considerations in the CLI API.)

Packaging, Installation, Configuration and Build

In keeping with my goal of optimal simplicity for the end user, I knew I wanted a package install for sofine. I did some research and chose pip, and then did some more to work through the issues of getting my package script working.

I got a nice rush the first time I went through the steps and they worked. My project was uploaded to pypi! I could download it and install it!

But then I hit a snag. The import paths were broken. To fix them to work with the packaged install I had to change them from the paths I was using when running directly from the development directory. But then I couldn’t develop the code in a way that worked when it ran from the package install. I needed to use the package install import paths all the time.

The solution I came up with was running the code from my Python package directory, which I called using $PYTHONPATH for portability. The design cost was that this required the new user to set one additional configuration.

python $PYTHONPATH/runner.py '--SF-s fidelity --SF-g example arg_1 arg_val_1 | --SF-s ystockquotelib --SF-g example'

There was an additional cost to this decision. The development workflow now required a local build to create a new pip package and install it in my Python package directory. This seemed cumbersome, but automating it and running it locally added only a few seconds to each test run. I also learned how to use pip to make and install builds locally, and a couple of make tricks to suppress the build output.

My make targets look like this:

deploy:
     @rm -rf dist > /dev/null
     @rm -rf sofine.egg-info > /dev/null
     @rm -rf $$PYTHONPATH/sofin* > /dev/null
     @python setup.py sdist --formats=gztar,zip > /dev/null
     @pip install --allow-unverified --no-index --find-links dist sofine > /dev/null

test: deploy
     python ./sofine/tests/test_runner_from_cli.py
     python ./sofine/tests/test_runner_from_py.py
     python ./sofine/tests/test_runner_from_rest.py
     python ./sofine/tests/test_format_csv.py

Conclusion

Open source software is made to be used. To encourage others to use your project, you should make it easy to get started with and easy to use. Achieving these broad goals means thinking about design across many aspects of your project – it’s call APIs of course, but also return formats, extension mechanisms, installation process, development workflow and any other user-facing aspect.

Be Inclusive

I’ve been thinking about writing about equality in the technology industry for a while now, without being able to figure out something worth saying and what tone to use. Somehow broadcasting legend Larry King, being interviewed on the occasionally epiphanic and frequently interesting podcast “WTF with Marc Maron,” finally showed me the way forward.

“We only need one rule to live by,” he said, “and that’s ‘Do Unto Others.’ That covers everything else.”

Applying Do Unto Others to the issue of equality in tech, I came up with one rule, a transposition of the original that remains universal: Be Inclusive.

No (I hear someone objecting (Hey! That someone is my rhetorical straw man!)), this rule doesn’t solve the whole problem. No, it doesn’t address other behaviors or conditions that one can reasonably argue are part of the problem of inequality in technology. But objecting to any suggestion to change human behavior for the better on those grounds is both true and trivial.

Because no social change is a complete solution, and given the complexity of the domain (the whole world), it is arbitrarily complex to even define what “complete solution” means. Rather, what is empirically evident to me about the social change I have lived through is that change happens incrementally. As sentient software developers in 2013, this is a concept you are likely familiar with and perhaps have internalized enough to have stopped noticing it; we call it Agile. The insistence that a comprehensive solution to inequality in tech is the only worthwhile solution is waterfall thinking. Instead, look at it like this: Be Inclusive is on the backlog of work items we need to implement to provide equality in technology. It’s a big project, with an unknown release date, and we can have interminable planning meetings about what else needs to go on the backlog. But we also know that doesn’t stop us from pulling this item off the backlog right now and starting to work on it, because there is a clear path to implementation and it clearly provides value.

In fact, it’s simple to explain how to implement Be Inclusive: every time you are considering a decision that has a dimension of inclusivity/exclusivity, just ask yourself, “Am I being as inclusive as possible?”

Just like time spent fixing bugs in your software, you’ll need to budget ongoing effort for Be Inclusive. Exclusiveness manifests itself as many bugs affecting the correctness of our systems of interaction and collaboration. Be Inclusive is time spent fixing those bugs. And it’s helpful to frame the problem and the response this way, because doing so highlights that just like software systems, we will keep discovering social exclusivity bugs. We are at least as fallible as the software we create, so we should expect to keep finding bugs.

This is not to be feared. Bugs are fortuitous discoveries of improvements to be made. Change happens incrementally, through the efforts of all of us.

Explaining the Economic Argument for Social Equality to a 10-year-old

Imagine there were two people, and each of them was starting a business.

OK.

Now, there were 100 adults who could work living in the land where the two business owners lived. Each owner could hire as many of these workers as he or she wanted. Are you with me?

Umm, yeah. There are two business owners, and they have 100 workers to choose from.

That’s right. Now, here’s the interesting thing. The best workers were so good at working that the stuff they made for the company at the end of the day could be sold for $500. Out of the 100 workers, only five of them were this good at working. 20 of the workers were also quite good at working, but not as good as the 5 best workers. These workers made stuff that could be sold for $200 in a day’s work. Does this make sense?

Yes, it makes sense. Some people were better than others at working. Isn’t that true of everything? Like you’re good at working with computers, and other people aren’t as good.

Eh, I’m pretty good. But there are some people much better than me. Anyway, back to our story. So out of the 100 workers, 5 are awesome and 20 are pretty good. It turns out that 50 of the workers weren’t bad at working, but weren’t great. Kind of average. When they worked for a day they could make enough stuff to sell for $100. And there were 25 workers who were pretty bad. They could only make $25 worth of stuff in a day.

OK.

So, 5 awesome workers, 20 good workers, 50 OK workers, 25 bad workers. Makes sense?

Yeah. I’m not sure what the point is, but yeah.

OK, you’ve been very patient, so here’s the point. It turns out that 22 of the 25 bad workers grew up where their parents had been bad workers too. Almost everyone in their neighborhoods were bad workers too. Almost everyone they went to school with. The same is true for 25 of the 50 OK workers, though more of them came from all over the place. And 18 of 20 of the good workers came up together, living in families with parents who were good workers and going to school with other kids with the same kind of parents. Same is true for 4 of the 5 awesome workers.

OK, I get it, I get it, I get your point. So basically most people ended up with the same kinds of jobs as their parents and their friends and their neighbors and the people they went to school with. Not everyone, but most people.

Yup, that’s it.

So?

So if we could figure out how to help some of the bad workers be OK, and some of the OK workers be good, we’d have more people who could make more stuff that was worth more. Think about the whole country – there aren’t 100 people who can work, it’s more like 100 million. It would help the whole country if more of them were better workers. Plus, like we said, a lot of people grow up to be like their parents. So if we could help some people get ahead then we would be helping a lot of their kids and their grandchildren, and so on.

OK, OK. I get it, I get it. Can I watch the iPad now?

Hiring One of Us: The Startup ‘Cult of Personality’

In the first two months of this year I was interviewing for a new job, focusing on that exruciatingly narrow sliver of the world known as “NY tech startups.” I was in fact coming off two years at a NY tech startup, and while the experience was often stressful and occasionally harrowing, it was also consistently engaging, often fascinating and occasionally exhilirating. For sure I had decided I couldn’t go back to a regular old company.

I had just spent two years as a sporadically coding VP of Engineering, and three years before that as a sporadically coding architect. Now I had decided to pursue more hands-on engineering roles, I was getting coding questions, and I was rusty. I also had to tell a story about why I wanted to back away from the VP level, without sounding like I didn’t like hard work or wasn’t ambitious. In addition, I have a mortgage and two kids and I am the only income for my family, so taking a major step back in salary solely for my own fulfillment wasn’t an option.

To recap:

  • I was looking for a job
  • The particulars of my situation filtered out many of the more straighforward possibilities
  • I was changing roles back into something I had done previously, which meant I was rusty and that I had a tricky story to tell

So I was realistic. I expected some rejection and disappointment.

As it turned out, my expectations were met – I went deep into the process and was rejected three times in the first weeks of my search. All of the rejections were on the grounds of “poor fit.” Which got me thinking about what “fit” means, how it’s used as tool of judgment in the interview process, and what, if anything “fit” should mean to a startup.

In the case of the first rejection, the on-site interview was long, varied, somewhat challenging technically and mostly interesting. I did well overall. But I also told the CTO an ill-advised story about using a temporary dose of harshness and fear to teach a junior engineer a lesson about hosing production data. This was a touchy-feely place where extremely enthusiastic collegiality was professed by everyone I met. I knew I hurt myself with the story as soon as I told it and said so. So I wasn’t surprised to not be the right fit. It was a fair and true assessment. I am as collegial as the next person, but I am not euphoric enough for these folks.

The second rejection was more mysterious. The on-site was technically more challenging and the engineers were smart and odd in a way I’m comfortable with and fond of. I wanted the job and they were interested. The courtship went on a week or so, and included an evening visit to their offices for an event and a meeting with their CEO. I liked the people I met. I thought they liked me. But again the “fit was not right.” “There was something off in my communication style.” I was puzzled and disappointed, but the process had been thorough, intelligent and subtle. I respected their decision even if I couldn’t figure it out.

The third rejection was moderately infuriating – the impetus for this post. This time the on-site was short and shallow. The tech questions were weak tools for guaging engineering ability or experience. The last half-hour of the interview was explicitly about “cultural fit.” What internet sites do I like? What social media do I use? What would my friends say is my best quality? My worst? What do I think is most exciting about the mission of the company? This section of the interview also featured several exuberant statements about the important culture Company X was building and how excited everyone was to be part of it. I again received word that the fit was not right. I was “not someone the engineers could get excited to work with.”

At which point I called bullshit.

The first thing that had been striking about the company behind Door # 3 is that everyone looked the same. Twenties, great clothes, thin, attractive at the very least. Shiny. White or Asian almost to a one, in a large, open, former-sweatshop SOHO room with perhaps 75 employees in it. I connected this observation to the section of the interview about the great culture they were building, how excited everyone was to be part of it, and the series of questions that seemed to me could only help an interviewer diagnose how close I was to their desired Internet norm. How much I was like them?

At which point I called ironic bullshit.

Because an interview process that professes reverence and enthusiasm for a “great” company culture should not be designed to eliminate every person not exactly like every person already working at the company. A hiring process that selects from a very narrow band around a norm based on superficial similarity reinforces a weak culture. If you need everyone at your company to confirm rather than challenge your worldview, then you are fearful and you are building a fearful culture.

A team working in a strong culture finds strength in their individual differences. People of different ages, different life, work and educational experiences, who grew up in different places. They don’t all think the exact same thing about the Internet this week, and that doesn’t matter.

What matters is what really matters in a company culture. Each person cares about helping every other person do their job better. Each person cares about the customer, always. Each person tries to learn all the time what the company is trying to accomplish and how they can contribute more to that success. Everyone learning to be better every day, together. Values matter. Taste does not.

It is very hard to figure out whether someone you are interviewing for a few hours over perhaps a few days has the right values. I’m explicitly punting here on a process that will help you find those people. But what I am asserting is that if your company uses “fit” as a weapon to eliminate people beyond an epsilon of difference from you and yours, that your process is a failure. In a NY tech startup market where talent is so tight, turning away capable people for superficial reasons does your startup, your investors and all the millions of your future customers whom you may never reach a disservice.

It’s not about you.

A Good Problem to Have

I recently had the combined misfortune and good fortune to look for a new job. It was bittersweet to leave my previous company, because we were in the middle of building the platform that for almost two years I had been dreaming of building. But I was excited to hit the market for software engineering talent in New York City in 2013, which by all accounts (including my own as a startup hiring manager the previous two years) was white hot. Everyone told me I would end up with something great, colleagues, hiring managers I interviewed with, my wife, the CEO of the company I was leaving.

But how did I really know it was a good market? Recruiters. I changed my LinkedIn profile, starting working my network, and within days and over the next six weeks or so (until I accepted an offer) I had an ever widening group of recruiters contacting me and hustling me. Well-meaning but empty assurances are fine: “I’m sure you’ll find something great.” But recruiters were actual demand for my supply.

I have observed among my peers here in New York City over the last several years many expressions of disdain at being pursued by recruiters. In casual conversation. All over online comments and in Meetup group discussions. On Twitter. Lamenting “ignorant” recruiters “wasting my time” without even “bothering to do their homework.” I’m guilty as well.

But no more. All it takes is a sliver of perspective to realize how entitled this attitude is. Perspective like, say, losing your job and knowing you still have to pay for mortgage and co-op maintenance, food, health insurance, car and car insurance payments. For a family of four.

A sliver of perspective is all it takes to realize how lucky we are. Real unemployment in the US has been around 15% since the world economy tanked five years ago. National economies are collapsing and being bailed out. And here we are in one of the world’s great cities being chased by recruiters and employers who want to pay us a very comfortable wage to do interesting, satisfying, creative work that offers a great degree of autonomy and requires us to keep getting better at doing what we love to do.

So the next time you catch yourself bitching about spending 15 seconds deleting an unsolicited LinkedIn message from a recruiter, take a half step back and let Louis CK remind you what it means to completely lose perspective.

May recruiters bother me for the rest of my working life. That would be a good problem to have.