Tag: Programming

  • We Hire The Best

    We Hire The Best

    The tech industry prides itself on its rationality, and yet is filled with trite slogans that are demonstrably untrue… and further, harmful.

    Originally published in Model View Culture, November 2014.

    Credit: Public Domain Pictures / Dawn Hudson
    Credit: Public Domain Pictures / Dawn Hudson

    “We hire the best.”

    It’s a slogan we can rally around in company meetings, a tagline to put on the jobs page… a shoring up of identity bound too much in employment.

    It’s also manifestly untrue. A more accurate way of putting it might be:

    Among people we know, we hire the best” (as determined by our subjective process), who are willing and able to work in a specific place (or remotely), and who accept our offer.

    Sadly, that’s not as catchy.

    People We Know

    Who you hire is profoundly limited by your company’s network, so when looking to diversify your workforce, it’s worthwhile to broaden your network. It is unlikely that everyone has heard of you, and of those that have, many may not know that you are hiring. Among the people who do know: are they aware that you hire people with their skills and/or backgrounds? A historically elitist hiring process may well discourage people from non-traditional backgrounds from applying, even if you later change it. Given the prevalence of team pages filled with white men, it’s possible for someone to hear about your company, look at the team page, and determine it’s not worthwhile to apply. The later diversity in hiring is considered, the bigger problem it becomes.

    If you aim to hire mostly through referrals, those referrals are likely to reflect your current demographic — unless you work hard to change that. For example, women are often shut out of social gatherings (casual drink ups), or assumed to be service staff or partners of attendees at events where career connections are formed, and the typical social network of a white American is 1% black. Meanwhile, companies spend large amounts of time (or delegate to external recruiting firms at enormous expense) in order to find “passive” candidates – those who aren’t applying, but could be convinced to interview. This strategy is subject to serious bias, such as preferencing alums of pedigree universities and well-known companies. And recruiters often pitch minorities on too-junior roles — due to sexist and racist stereotypes, women are deemed less competent and black people are perceived to show less leadership. Such stereotypes and biases can play a huge role in if and how candidates are approached, and for what positions.

    …As Determined By Our Subjective Process

    All hiring processes are subjective which means: open to bias and flawed.

    In Shaft’s article Thoughts On Diversity Part 1 (tackling the Meritocracy Myth), he observed:

    “Yahoo > Google > LinkedIn > FaceBook > Twitter. After Yahoo each of these companies’ diversity numbers have been worse than the company that followed them. I believe this is because Google recruited from Yahoo, LinkedIn from Google, and so on. Each subsequent company becomes less diverse due to the sub-conscious amplification of educational, cultural and work history biases.”

    These biases are particularly evident in the educational pipeline leading to the tech industry. Just looking at the Ivy League, we see disproportionate representation of “legacy” enrollment: between 10 and 15% of students are children of alums. A 2011 study found that when a parent had attended that same university, students’ chance of admission went up 45.1%. In Dartmouth’s 2015 class, legacy admissions comprise 8.5% of the student body. Princeton legacies have a 33% admission rate, compared to 8.5% general admission rate, and Yale 20-25% compared to 6.7% (2013). And legacies make up 12-13% of Harvard undergraduates, with a 30% admission rate (2013).

    Meanwhile, racial diversity in the Ivy League is dismal. As of 2012, the highest enrollment rate of black students in an Ivy League school was 7.7%, compared to a US population that is 13.1% black. And the highest rate of enrollment of Hispanic students was 13.2%, compared to their 16.7% overall representation. Socio-economic status and parental income is also a significant factor in admissions to Ivy League universities. 69% of Yale undergrads come from families with incomes over $120,000, with just 15% of students coming from families with incomes below $65,000 (2014). Meanwhile, the US mean household income is $60,528, and is lower for black and Hispanic households.

    Further, admission rates are distinct from graduation rates, where we also see systemic inequality. For example, at University of California (Berkeley), white students have a graduation rate of 92%, while Hispanic and black students have a graduation rate of 81% and 71%, respectively. (For further reading, Whistling Vivaldi contains some good discussion). And while this data looks at a particular school system, it is a great illustration of how filtering resumes on pedigree companies and universities builds on the flawed, biased processes that have come before.

    Interviewing

    While much inequality enters the hiring process even before an interview, this is yet another place where we see significant bias enter the equation. Interviews range from unstructured conversations based around assessing “culture fit”, a vague practise that opens up almost unbounded opportunities for discrimination, to extremely structured interview questions which may or may not cover topics that are even relevant to the job.

    For example, the prevalent interview system in the Valley means assessing a candidate’s grasp of 2nd year Computer Science Algorithms and Data Structures courses through contrived problems. This is commonplace at companies including Microsoft, Google, Amazon, Facebook and Palantir. These interviews mainly test people’s abilities to remember, revise, or learn the algorithms and data structures, and to respond appropriately to artificial problems (pro-tip: the answer is usually a Hashtable). It is problematic in hiring people from non-academic backgrounds (who may not have taken 2nd year Algorithms and Data Structures), or who went to universities which do not cover that material to the necessary standard.

    This is also challenging for specialists; for example, much of this content has little bearing on front-end Javascript, and often the most challenging problems in mobile have less to do with algorithms than physical concerns like networking and battery life. In this subjective process, a tangentially related thing is used to assess someone’s ability to do a job that comprises 99.9% other things. In fact, Google’s own data from using this process has revealed interview performance has little bearing on job performance.

    There are numerous other issues with the design of interviews — many of which have been discussed elsewhere — including:

    1. Trick questions. May be based on knowing some esoteric factoid about the JVM, or that require an “aha” moment or are otherwise impossible to solve.
    2. Other kinds of bad questions: for example, questions that are too large for anyone to get through during the interview process, and so results are biased by which part of the question the interviewee focused on.
    3. Bad interviewers: interviewers who are not clear in their questions, who manage time poorly, etc.

    Finally, interview structure aside, feedback on applicant performance is riddled with “unconscious bias”. Having read much of the research around stereotypes and bias, when working at a major tech company I would spend around 30 minutes combing through my feedback for bias after interviewing minorities. Consistently, I found small comments and phrases that I would remove or rephrase to reduce bias. This doesn’t mean I invariably supported the hiring of minorities who I interviewed; I did not. Merely, I tried not to further stack the odds against them through my feedback and picking on things I would not have highlighted had the person I interviewed been a white male. This also does not mean that my feedback was without bias: bias runs too deep in all of us. It just means that I did my best to be conscious of my bias and work to combat it.

    It is unlikely that many, or even any of your interviewers are doing this: they lack the domain knowledge, and will not take the time. And the reward for employees who do do this kind of extra work is often being asked to do more of it: recruiters are incentivized to hire and learn which interviewers care and are quick and painstaking in their feedback. Relatedly, programs to improve the experience for women candidates often create additional work for the women who are already there. Well-intentioned companies interviewing women often aim to get a woman on each interview slate, which can be problematic and causes the work of interviewing to disproportionately fall on women. These extra tasks around hiring and outreach are something that is for the good of the collective, and are exactly the kind of task that women receive little or no recognition for but are penalized for not agreeing to (see Women Don’t Ask).

    Plus, it often doesn’t make for a good experience for women-interviewees, either – yes, we notice when we come in and don’t meet any women, but we also notice when we get a female interviewer who is clearly in the wrong domain, or inappropriately junior.

    Reviewing

    At this point in the hiring process, feedback is collected and another person or group of people review it and make a decision. Many of the filtering and interviewing biases are amplified here as more people get involved and look for adverse signals (like “shyness” or lack of technical credibility, or an unusual background), unless a serious and concerted effort is made to remove them.

    It’s also in this stage that we commonly see reference checks: another possible source of bias. As part of some volunteer work, I ran a committee assessing female CS students for scholarships. As part of the process, we reviewed recommendation letters from their (mostly male) professors: the prevalence of gendered feedback in these letters was frankly appalling.

    Are Willing to Work [In a specific place] / [remotely]

    The phrase “we hire the best” irks me everywhere, but most of all in the Valley. One would think that the competition and amount of movement between companies in the valley precludes this, but there are incredible people around the world who who do not wish to move there, and with good reason. There are the US issues, such as the difficulty of procuring visas, poor social policies around childcare and maternity leave, and the localized issues of high rents. In addition, professional women are more likely to face the “two body problem”, having a spouse with a job that is hard to move, or to be the primary carer for a relative that limits their geographic mobility.

    Remote work is one way of getting around this, however some people, particularly junior people, may not wish to work remotely, and want the benefit of in-person interaction and mentorship. Some may just find it too lonely, or not a fit for their working style. Remote work is not suited to everyone’s temperament.

    Those Who Accept

    In the end, hiring “the best” is dependent on “the best” accepting your offer in the first place.

    One of my friends has been interviewing recently and it’s fascinating how poorly some of the companies have treated her over the course of extending her an offer. One company lowballed her salary, was late on every deadline, and generally did not respect her time. Perhaps they think anyone who makes it through their arduous and time-consuming process must be committed to saying yes, or perhaps they’re just relying on their brand.

    During the process they used to determine she was a good fit for them, she figured out they were not going to be the best fit for her. She’ll go somewhere where they respect her time, and offer her fair compensation up front. Somewhere that communicates with her in a timely manner. As for startups she spoke to who treated her really well, there’s every chance that she’ll consider them again in 2 years or so, when she’s thinking about making a move again.

    There’s massive competition to hire engineers, and engineers talk amongst themselves. We know that Glassdoor and the like are full of bitter animadversions from people who didn’t get hired, but it is much more damning when someone gets an offer and chooses not to, based on their experiences in the process. Most people consider multiple options when making their next career move, and they will weigh things that you may have no control over: their commute time, their desire to start their own company. But they will also weigh up things you can control. Their compensation. Their impression of the company. And how recruiters and interviewers treated them.

    No Such Thing

    The tech industry prides itself on its rationality and yet is filled with trite slogans, like “hire the best,” that are demonstrably untrue… and further, harmful. White men declare loudly and proudly that the issue is “unconscious bias” and ignore the depressing data on sexual harassment. They claim to be trying to build more diverse companies and yet all effort focuses on the pipeline, children and university students, while the 56% of women who drop out by mid-career are ignored.

    “We hire the best” they cry. But they don’t. They never will. This mentality justifies the homogenous workplaces, where much is invested in maintaining this facade and propping up the status quo. The system works, and therefore I am here. I am here, and therefore the system works. It is used to justify lengthy processes of interviewing, of unpaid or poorly paid projects. The new trend of unpaid labour in less prestigious roles such as customer support reflects previous trends of demanding Open Source contributions, which is problematic and ineffective. Candidates must prove that they want it enough, prove that they are “the best”, where “the best” sometimes just means the most willing and able to work for free.

    The truth is, that there is no such thing as “the best,” except for the most arbitrary of metrics.

    Perhaps if we could admit that we all hire flawed humans, then we could do the work to create environments where those flawed humans treat each other well. We might not be able to claim “the best,” but we would all be better off.

    Thanks to Julia Evans for reviewing and giving feedback on this article, and Alex Wilson for helping with research.

  • 4 Situations Where Managers Write Code

    4 Situations Where Managers Write Code

    P vs NP
    Credit: Flickr / Takashi Hososhima

    The two hardest things about becoming a manager have been: 1) the emotional exhaustion, and 2) letting go of the part of my identity that was tied up in writing code. I accepted that writing code wasn’t the best thing I could do reasonably quickly, but it took longer to finally stop saying I was an “engineer” and instead say “engineering manager” (or “soy manager de ingenieros de sistemas“). I still frequently think that my next job may be back to being an IC.

    But, I do sometimes write code, and I try to be very intentional about when and why. I’ve identified four reasons why managers might.

    1. I miss it and I wanna.

    I understand this, and I feel it. But, it’s a bad reason.

    This doesn’t mean that you never get to write code, but maybe write it on a side project and not the project your team is working on.

    Or maybe it means that you don’t actually want to be a manager anymore, or never did, and you need to change your job rather than inflicting that on your team.

    2. X is important, but not quite important enough.

    There’s something you would like to get into the next release but it’s not more important than anything your team is currently working on. Either you do it, or it’s not going in.

    I did this recently for a feature that had been cut that I thought was triggering too many support issues. It was pretty minor, and I could break it down into small pieces. Mainly I wrote it before my team woke up – and being in another timezone (5 hours ahead of our core timezone) made that much easier. I ended up working really long hours that week though, because I did it on top of all the stuff I normally do.

    I don’t think this is a good thing to do, because the real problem is that your team is overwhelmed. The real problem is that you are cutting stuff this important because there are things that are even more important. This is the situation you want to fix.

    And maybe writing code for this one thing helps achieve that. But maybe – likely – there are better things you could do instead.

    3. Modelling behaviour.

    I have a lot of thoughts about code review, which I will eventually document. At it’s best code review makes the code and everyone writing it better. At it’s worst, code review is a place where passive and not-so-passive aggression plays out under the guise of “technical standards”. If you have an interpersonal problem on your team, there is a good chance it is showing up in code review.

    Writing code as a manager is an opportunity to demonstrate the standards you expect your team to adhere to by adhering to them yourself. Test coverage. The code review process.

    When it comes to code review, giving thoughtful and thorough code reviews yourself is important – and also less time-consuming and more sustainable (I wrote about my desired SLA of looking at every PR once). But creating the odd PR yourself and submitting yourself to that process is a way to illustrate that as a team, code review is a process of give-and-take.

    4. Understanding.

    Your codebase is a product of your systems and processes as much as the people working on it. On approach you can take in order to understand what’s going on – why are there no tests? Why is there so much pushback on this migration? Why do we have so many problems that look like X? – is to go and feel that pain. Especially when there is disagreement about what is wrong and why, the best place to get an unbiased opinion of what is wrong is to go an experience what’s going on for yourself.

    One thing that came up for us was a JSON API migration. I went and wrote some networking code, and came to a much better understanding of what was going on, why that would be hard, and what work we should do ahead of time to make that easier.

    Could I have figured this out another way? Probably. But busy engineers are sometimes happier to pair for a bit and do some code review than to explain.

    Manager TODO List

    As a manager my todo list at a high level is:

    1. Is there a fire? Put it out.
    2. Are there potential fires looming? Do fire prevention.
    3. Make things better.

    Writing code needs to fit into this list. If there’s an interpersonal fire and you’re writing code to “understand” when actually what you should be doing is having a difficult conversation (and I feel not wanting to do that – but pro-tip – it doesn’t get easier), you’re really operating under reason 1 not reason 4. Don’t lie to yourself, or your team – they know.

    But… Does This Mean Managers Need To Be Able To Code?

    Nope.

    Nope. Nope. Nope.

    Let me say that again: No.

    Just because I find coding useful to managing doesn’t mean that it’s necessary for managing. I spent a lot of time learning how to be a good programmer, this is a foundational skill for me. But someone who comes in with a different set of skills will have their own foundational approach and take a different approach that is not necessarily better or worse, just different.

    My experience as an IC was that better managers I had, their technical ability was rarely relevant to me, and that bad managers often hid behind their ability to write code. I did not think that technical skills were that important to being a good manager, and had no idea why they were prized so much – other than the hubris of engineers that leads them to believe and insist that only another engineer can possibly lead or understand them.

    However, since I became a manager I have somehow been more confused and less confused about why people want engineering managers to have technical backgrounds. More confused, because the skills I acquired in spite of being an engineer are in fact more useful. Less confused, because there are certain things – like this one – where having that background is a shortcut, and a likely signal – but far from a given.

    Having a technical background doesn’t mean that you know what good code review looks like, and that you can coach people to do code review well. Having a technical background doesn’t mean that you can ask good questions and figure out what’s going on without having to get in there yourself every time. Hopefully these are skills acquired as an IC. But that is not necessarily true.

    TL;DR

    Being a manager is really hard and of course sometimes writing code seems more appealing. But it’s rarely the right answer.

  • Testing Intents on Android: Like Stabbing Yourself in the Eye With A Blunt Implement

    Testing Intents on Android: Like Stabbing Yourself in the Eye With A Blunt Implement

    App running on emulator in Android Studio
    App running on emulator in Android Studio

    The picture above shows what am I testing: the home screen of my app. There is a camera button, a gallery button and an inspire button. All of these launch intents, but the camera and gallery buttons launch intents that are expected to return something – an image – either from the camera or the gallery.

    First up: how the hell do you test an intent? I started with my straightforward Dagger/Mockito setup, but my tests were failing because they were launching things – the camera, or the gallery – which I then couldn’t get out of to continue my tests.

    The answer is the IntentsTestRule, which extends ActivityTestRule (if you’ve written a test for an activity, you’ve probably seen this). This took me a little while to make sense of, mainly because I kept getting this error saying that something had been initialised twice. I was launching the intent in my test, and also calling Intents.init(). Turns out, you don’t need to do that. Having an intent rule is a little bit of magic. It just launches itself.

    I have a very straightforward test that just checks that things have loaded, and it’s one I kept coming back to in order to see how things work. Here it is.


    @Test
    public void testLaunchActivity() {
    onView(withId(R.id.home_camera_button)).check(matches(withText("Camera")));
    onView(withId(R.id.home_gallery_button)).check(matches(withText("Gallery")));
    onView(withId(R.id.home_inspire_button)).check(matches(withText("Inspire")));
    }

    I think sometimes it seems like it’s not worth having tests like this because all of this will be covered elsewhere. Not true. I always include really straightforward tests. If they’re useless, then who cares, you’ll never think about them again. But in practise I return to them again and again when I’m debugging.

    First up I got my gallery test working. I had no idea what I was doing, so basically everything was broken, so I started by being really general and then getting more specific once I had it all working together. For example, figuring out which package it was in was a PITA, so to get thing working I used this handy catchall:

    intending(not(isInternal())).respondWith(result);

    This will return my “result” intent to every intent outside the app. Definitely not as exact as we want in our tests, but really useful for getting things working. In theory you can check ActionType, which in the case of the gallery is Intent.ACTION_PICK. In practise, this Intent was not being captured.

    I eventually managed to find out what the package was by using:

    Intents.assertNoUnverifiedIntents();

    This is a check for unverified intents, and handily in the error message it gave me it returned the package that the intent was coming from. So, for the gallery I had:

    intending(toPackage("com.android.gallery")).respondWith(result);

    Useful! The full test is:


    @Test
    public void testTapGalleryButtonAndReturnOK() {
    // Stub the Uri returned by the gallery intent.
    Uri uri = Uri.parse("uri_string");
    // Build a result to return when the activity is launched.
    Intent resultData = new Intent();
    resultData.setData(uri);
    Instrumentation.ActivityResult result =
    new Instrumentation.ActivityResult(Activity.RESULT_OK, resultData);
    // Set up result stubbing when an intent sent to "choose photo" is seen.
    intending(toPackage("com.android.gallery")).respondWith(result);
    onView(withId(R.id.home_gallery_button)).check(matches(withText("Gallery")));
    onView(withId(R.id.home_gallery_button)).perform(click());
    // Check image processor is reset.
    verify(imageProcessor).resetOriginalImage();
    // New activity should be launched
    intended(hasComponent(ImageEditingActivity.class.getName()));
    }

    Here I had some SUPER FUN debugging. By super fun I mean: like stabbing myself in the eye with a blunt implement. Luckily there were no blunt implements to hand and I have a strong sense of self preservation. Once I got my intent working, it seemed like the test was done. I added some validation of a new activity getting launched. Also, I decided to add validation on something else should happen – that the image processor gets reset. This should be straightforward, right? Everything is set up with Dagger and injected, I have my Mock Providers, and Mock Component for my tests. I just need to verify.

    Nope. It didn’t work, and it took me a long time to figure out why.

    The issue was that I didn’t have my mock objects in my activity. I thought this was because the activity was being relaunched, but that was a red-herring. The mock objects were being interacted with in the new activity, so I knew that my setup was somewhat right, but clearly not completely! Obsessively trying to hunt down the cause for this did help me fix a bug though – I had a fall through in a switch statement in my activity.

    Breakpoints had failed me (I was putting breakpoints in my tests, but the debugger wasn’t stopping on them – grr) I returned to my very straightforward test, and started just adding some log statements. This helped me figure out the problem: onCreate() in the HomeActivity was being called before setup (labelled with the annotation @Before) in my HomeActivityTest. So I had real objects in my activity under test, because it was using the usual dagger components, not the test ones.

    Once I knew what the problem was, I knew what to search for to find a fix for it. The conclusion: I needed to subclass the IntentsTestRule, and override beforeActivityLaunched() to put my pre-activity setup code in it.


    private static class HomeActivityTestRule extends IntentsTestRule {
    private HomeActivityTestRule() {
    super(HomeActivity.class);
    }
    @Override public void beforeActivityLaunched() {
    Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
    ShowAndHideApplication app =
    (ShowAndHideApplication) instrumentation.getTargetContext().getApplicationContext();
    ShowAndHideTestComponent component = DaggerShowAndHideTestComponent.builder()
    .mockFileUtilitiesModule(new MockFileUtilitiesModule())
    .mockImageProcessorModule(new MockImageProcessorModule())
    .build();
    app.setComponent(component);
    }
    }

    There was no new code here – this was in my standard test setup. It’s just in a new place. I made it within the test, so it’s a private static class. As I add more tests I may move it out to be reusable. For now, it’s only binding the components I need in this test. If I move it out, I’ll need to bind everything.

    Then I added the test for the result failed test. This is more straightforward so it’s tempting to add this first, but in practise I never add my tests for nothing happening first because I never have any confidence that they actually work until I can break them.


    @Test
    public void testTapGalleryButtonAndReturnCancel() {
    // Build a result to return when the activity is launched.
    Intent resultData = new Intent();
    Instrumentation.ActivityResult result =
    new Instrumentation.ActivityResult(Activity.RESULT_CANCELED, resultData);
    // Set up result stubbing when an intent sent to "choose photo" is seen.
    intending(toPackage("com.android.gallery")).respondWith(result);
    onView(withId(R.id.home_gallery_button)).check(matches(withText("Gallery")));
    onView(withId(R.id.home_gallery_button)).perform(click());
    // Check image processor is not reset.
    verify(imageProcessor, never()).resetOriginalImage();
    }

    Notice that in the first test, I assert my image processor gets reset. In this one I assert that it doesn’t get reset.

    Next, testing the camera intent. It’s basically the same, but I also have to mock the Uri, because when you take a photo on Android you first have to allocate space for it. I have complained about this before so I will refrain here.


    @Test
    public void testTapCameraButtonAndReturnOK() {
    // Uri needed to launch the Camera intent.
    Uri uri = Uri.parse("uri_string");
    stub(fileUtilities.getOutputMediaFileUri()).toReturn(uri);
    // Build a result to return when the activity is launched.
    Intent resultData = new Intent();
    Instrumentation.ActivityResult result =
    new Instrumentation.ActivityResult(Activity.RESULT_OK, resultData);
    // Stub result for camera intent.
    intending(toPackage("com.android.camera")).respondWith(result);
    onView(withId(R.id.home_camera_button)).check(matches(withText("Camera")));
    onView(withId(R.id.home_camera_button)).perform(click());
    // Check image processor is reset.
    verify(imageProcessor).resetOriginalImage();
    // New activity should be launched
    intended(hasComponent(ImageEditingActivity.class.getName()));
    }

    Notice at the end of it we assert that we’ve transitioned to the next activity – the ImageEditingActivity. This is what this line does.

    intended(hasComponent(ImageEditingActivity.class.getName()));

    And again, we check that it works when the result is cancelled.


    @Test
    public void testTapCameraButtonAndCancel() {
    // Uri needed to launch the Camera intent.
    Uri uri = Uri.parse("uri_string");
    stub(fileUtilities.getOutputMediaFileUri()).toReturn(uri);
    // Build a result to return when the activity is launched.
    Intent resultData = new Intent();
    Instrumentation.ActivityResult result =
    new Instrumentation.ActivityResult(Activity.RESULT_CANCELED, resultData);
    // Stub result for camera intent.
    intending(toPackage("com.android.camera")).respondWith(result);
    onView(withId(R.id.home_camera_button)).check(matches(withText("Camera")));
    onView(withId(R.id.home_camera_button)).perform(click());
    // Check image processor is not reset.
    verify(imageProcessor, never()).resetOriginalImage();
    }

    It was tempting to delete the mock fileUri here, because it seems like we don’t need it, but actually if I did that it would not be testing what I want it to, even though the behaviour (nothing happens) would be the same.

    In both the cancel intents, I wanted to assert that no new intent is launched, but it’s wasn’t clear how to do that. Intents.assertNoUnverifiedIntents() was failing on things I’d stubbed. I realised that I had to stub and expect things, so I needed an intended() for each intending(), and voila! It works. Once I discovered this, I went back through all of my tests and added the requisite lines.

    Finally! My most straightforward intent test, and you may wonder why I didn’t start with it. As do I. There’s a third button, inspire, which launches the web browser with a specific URL. Note that this test uses intended() rather than intending(), and that it comes after the intent is launched – not before. At the end, we assert no unverified intents.


    @Test
    public void testTapInspireButton() {
    onView(withId(R.id.home_inspire_button)).check(matches(withText("Inspire")));
    onView(withId(R.id.home_inspire_button)).perform(click());
    // Capture web browser intent.
    intended(toPackage("com.android.browser"));
    Intents.assertNoUnverifiedIntents();
    }

    Note: one tutorial I found suggested stubbing all external intents with the blanket catchall, and setting it up in a method annotated with @Before. I didn’t do this and I don’t think it’s a good idea. Launching an external intent is something that you should be capturing deliberately, and that’s the whole point of testing it. I would be more likely to go the other way and assert no unverified intents in a method annotated @After!

    Complete code for this HomeActivityTest.

    Useful Resources

    • Setting up
    • Sample code
    • Chiu-Ki took me through getting Dagger and Espresso set up in the first place, ages ago. And if you’re not lucky enough to be friends with her (and passing through Denver), she has a bunch of resources for testing on Android on her website.
  • I Wrote a Book Chapter and Finally, You Can Read It

    I Wrote a Book Chapter and Finally, You Can Read It

    app

    My 2014 side project was a technical book chapter on image processing for the Architecture of Open Source 500 Lines or Less Project. It was my bête noire, that consumed various evenings and weekends either by actual work, or by guilt.

    2015 was mainly guilt, and some editing.

    Recently the final copy edits came back, I read it one last time, and now I never need to read it again. You can though, the online version is here and the print copy will be out at some point.

    For me, the process of writing a book chapter was one of coming to deeply hate a project that I had at one point, loved. But returning to it one last time, after enough time away from it, I was able to see why I had found that project so fascinating, and appreciate in retrospect the amount of care and work that had gone into it.

    I really hope that others get something out of it. It’s about colour, and creating image filters, but also about testing the untestable, and the joys and benefits of prototyping.

    [read it]

  • Interview Prep Tips

    Interview Prep Tips

    Credit: Wikipedia
    Credit: Wikipedia

    I have done a lot of technical interviews. And mainly I think that interviewers have to change, but I do have some suggestions for interviewees. Ages ago I wrote up my prep list for interviewing at The Conglomerate, which is comprehensive, and frankly excessive. This is the short version with the benefit of spending more time on the other side of it. It’s targeted somewhat more at algorithms and data-structures interviews which are the ones that I think people are most afraid of.

    1. Be Calm

    I can’t tell you what to do here – you need a degree of self-awareness to figure out what will make you stressed and what will make you feel in control. For me, I need to know what to expect and feel like I have prepared enough to do it.

    As an interviewer, I’m always working to make someone feel calm and in control. But you can’t necessarily tell if someone is freaking out (especially if that freaking out ends up coming across as arrogance). And this is a lot of work, that sometimes results in the interview going over time, or me concluding some fraction of it was not a good signal. It’s much easier for everyone if you don’t freak out in the first place. So determine what you need: information? Time? And ask for it. Most places want you to be successful and will be happy to give it to you.

    2. Choose Your Language Carefully

    Worry less about what will “impress” your interviewer and more about what you feel comfortable in. Then modulo that with the kindness of the language. E.g. if you love C, but are pretty happy in Python, I would suggest choosing Python, because there is a bunch of useful stuff in Python, and you just need to write fewer lines of code to achieve the same thing.

    If you are interviewing for a platform specific job, at some point in the process you should expect to demonstrate you can be effective on that platform. And obviously many interviewers are bad, and some are setting secret tests (such as programming language) by which they judge people. However, when I say “code in whatever language you like”, I mean it.

    One note on languages that have a lot of magic in them – like Ruby – you can easily end up writing code that you can’t reason about the efficiency of because it’s doing a bunch of things for you and you don’t know what. Anything you write with magic, you should be able to write without magic, and so understand the magic enough to know how you might do that – and when you might want to.

    3. Know Your Foundation Blocks

    The algorithms and data-structures covered in interviews is essentially second year Computer Science curriculum. Data-structures at least, are building blocks for everything we use – it’s worth understanding what their properties are and when to use them. I think array / list / hashtable / binary tree are probably sufficient.

    Algorithms are like… the history of how we learned to solve problems and manipulate data efficiently. As computers become more powerful, it might seem less pressing but it’s still relevant, and still worth knowing. I don’t think you need to be able to re-implement them on demand, but knowing which is which and understanding trade-offs is good. Aside from sorting, binary search, and the basics of runtime analysis (basically: how long will it take proportional to the input?) are useful.

  • Categories Considered Harmful

    Categories Considered Harmful

    Credit: Wikipedia
    Credit: Wikipedia

    TL;DR – Don’t use categories. They just create different problems.

    Categories in Obj-C are a way to add (or expose) functionality to a class without subclassing it.

    You can see why this might be tempting, right? File getting a bit big, too many pragma marks, stick that bit in a category.

    I want to expose this method just for testing… make a category and voila I have it.

    This class someone else made does nearly everything I want, I just want this extra thing… make a category.

    I have two big problems with categories. The first is that they make the code less testable. The second, that they obscure control flow.

    Testing

    Say you moved some code into a category because the file was getting too long. You still have all the same problems of testing a really large class… but now that class is split out over multiple files.

    If categories are sensible, then they actually suggest sub-objects that could be owned by your class. Objects that can be injected, and mocked, and unit tested.

    Control Flow

    People throw around the phrase “composition over inheritance” without I think really knowing what it means. Categories are composition in the same way that Frankenstein’s monster is composition.

    Objects with Categories are Frankenstein Monster objects. You don’t know what you have unless you look at the import statements. And what if you’ve imported a bunch of them?

    And then they murder you, but it’s kind of your fault because you should have known better than to make such a thing and subject it to the torture of existence. Or something.

    Sometimes inheritance is a perfectly reasonable answer.

    Alternatives

    Categories are a fix that is used way more than warrented, over other options. Defaulting to no doesn’t mean never using categories, it just means being critical before you do.

    If a class is getting large, look for functionality you can move out into a separate object.

    If you want to expose a method for testing why not… just stick it in the header file with a comment to that effect? And then as you write your tests think critically if it really does need to be exposed.

    If you want a slight improvement on an existing class, why not just subclass (or compose it) into a new object? Bonus – you can make something that is better to inject and mock, and then you can improve testability. Wrapping library objects so that they have a better interface for testing is often a good way to go.

    There’s a bunch of interesting stuff in Obj-C, but with a lot of these things – just because you can doesn’t mean you should.

    Swift Extensions

    When I was ranting about this on Twitter some people asked about Swift Extensions. Dot suggested that because of Protocol-oriented programming, Swift Extensions are intended for a different use.

    I am not familiar enough with Swift to have a strong opinion here, but I would say that just the general idea of adding functionality to a class without subclassing it is one I have reservations about and independent of language, you should think carefully about the ramifications to the testability and maintainability of your project codebase doing it.

  • Android: Touch to Change Image

    Android: Touch to Change Image

    Credit: Wikimedia
    Credit: Wikimedia

    On iOS I do this using a button (with different pressed state), and I figured it would be the same on Android, but turns out, no.

    Step 1: Add a second image to the XML, and set the visibility to “gone”.


    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/processed_image_background">
    <ImageView
    android:id="@+id/processed_image"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="fitCenter" />
    <ImageView
    android:id="@+id/processed_image_original"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scaleType="fitCenter"
    android:visibility="gone"/>
    </FrameLayout>

    view raw

    images.xml

    hosted with ❤ by GitHub

    Step 2: Add a tap target that changes visibility.


    imageView.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View view, MotionEvent event) {
    originalImageView.setVisibility(event.getAction() == MotionEvent.ACTION_DOWN ?
    View.VISIBLE : View.GONE);
    return true;
    }
    });

    view raw

    TapTarget.java

    hosted with ❤ by GitHub

    Step 3: There is no step 3. Done!

  • The Hardest, Shortest, Lesson Becoming a Manager

    The Hardest, Shortest, Lesson Becoming a Manager

    "how does computer programming work" "magic"
    Credit: Abstruce Goose

    There’s something we all talk about in becoming a manager – and that’s the process of writing less code. We bemoan it because it’s hard to let go of that part of our identity. But also because it’s so quantifiable. Today I wrote X lines of code. Today I deleted Y lines of code. Today I implemented feature Z. Concrete achievements are reassuring. Today I left the codebase better than I found it. Good job.

    I too found this a really really difficult thing to let go of. I looked at tempting tasks. A nice feature. A refactoring. And I did not do them. The thing that I have found that helps is framing it as now coding is not the most important thing that I do. But then I get to the end of a day where I did not code, and I ask, how do I know I achieved anything today?

    Get Real About Your Schedule

    This week is a short one, but still – there was a point in it where I was triple booked. I honestly have no idea how I would cope right now if I didn’t have admin help. Ariel rules my schedule. I just look at it and panic.

    Even when I have open time, it’s like 2/3 of a day, half a day. If I have 2/3 as much time of the week that isn’t spoken for by meetings as I did when I was an IC, that doesn’t mean I can write 2/3 as much code. For starters when you code in bits and pieces you spend a lot more time rebasing. Secondly, transition between strategic (what are we doing), coaching (how can I help you) and details (what does this bit of code do) are difficult context switches. And switching in and out of details is the hardest of all.

    When a 1:1 starts with someone sharing technical details with me for ~10 minutes it’s my job to help lift them out of those details so we can work on strategy or coaching. That transition is hard. In both directions. I see it being hard on them, and of course it is also hard for me.

    I had a manager once who decided he would take on an important component in the app I was tech lead of. He did a half-assed job of it, and it took him ages. In the end, it was a really challenging piece, it ended up being rewritten 3x (2x by me) before we got it to a point where it performed well. How do you tell your manager that they are your #1 risk factor for missing your deadline? How do you tell them they did a bad job and the thing they made doesn’t work?

    I forget how I dealt with that – I think I procrastinated on dealing with it and eventually just picked it up and started improving it. I don’t in general believe we learn that much from what not to do, but I won’t inflict that on my team. If I’m trying to write code now, it’s something that me being slow to complete shouldn’t be blocking anyone. Cleanup tasks are a good place to start.

    What Makes the Team Better

    The truth that all managers must accept is that your job is now to make a group of people more effective, over a longer period than you usually consider as an IC. Even if you are genuinely the best and most effective person to take something on, does this make your team more effective 3 months from now? Unlikely. Instead of investing in someone else having context now, you’re postponing that moment to later, or indefinitely.

    And then they will have a fun time untangling your code and it would be great if you were there to talk to, but you’re in a meeting, again.

    The sooner you invest the time in coaching people on what you know that they should know the better it will be.

    One of my first weeks on the job there was a component that is pretty complex, that I have now built twice. Last time it took me ~1.5 days. I really just wanted to write this code, show everyone that I’m a good engineer, and have that win. But after sleeping on it, I took a deep breath, looked at my schedule, and encouraged someone else to do it instead. Would I have done it faster? Honestly considering my schedule, probably not. And now the person who did write the code is in a good position to own that component going forward.

    Does Your Team Need A Manager… or Another Engineer?

    As a manager I have this mental list of things about what does my team need. Things that I’m monitoring, things that I’m trying to fix, things that I’m trying to find for them. It’s my job to understand what is going on and what the team as a whole needs to be effective.

    Maybe you can look at the state of things and say, we have a deadline right now, and what we need is another engineer for the next month. That engineer is me.

    But more likely you look at the state of things and realize that what your team needs is a manager. Because you need to hire X more people. Because Y has a lot of potential but needs some coaching. Because product or design or some other team haven’t given you what you need so you need to go and get it. Because process is important, and the process you have is insufficient or just plain wrong.

    If you team needs a manager more than they need an engineer, you have to accept that being that manager means that you by definition can’t be that engineer. I know some people manage both, but you need to decide if you’re going to suck at one which one that will be.

    I feel bad when I suck at being an engineer, but sucking at being a manager would be a choice I inflicted on other people. That’s not fair.

    So at the end of another day when I feel like I didn’t write enough code and I have no way to quantify what I’ve achieved, I tell myself I was being as good a manager as I know how to be. And that has to be enough for today.

  • Further Adventures in Android Development

    Further Adventures in Android Development

    Danbo want to use my smart phone
    Credit: Wikimedia

    I suspect one of my limitations as a programmer is that I don’t hack. I don’t beat away at something until it works. I read things, and I reason about it, and I write a lot of tests.

    This makes me very effective on platforms I’m familiar with, but I worry I’m as a result not as effective when I’m picking new things up as someone who will just hack away. I’m searching for the moment when things start to make sense.

    I’ve done some Android over the past few years. I really wanted to learn it, but when I started working on it, it really wasn’t that fun. It was an over-engineered codebase, and as I tried to find my way in it, the feedback I got in code review was often of the “I would have done it differently” variety. Often that way didn’t even work, so that was… rewarding.

    The first breakthrough was that a lot of stuff is just more work than iOS. For instance, if you want to take a photo on iOS you just like… launch the camera and implement the delegate.

    If you want to take a photo on Android, you mount the hard drive, allocate space for the photo, launch the intent, and handle it returning. I always thought managing hardware and memory was a job for the Operating System, but what do I know about Operating System design, anyway.

    Aside: as I learned this lesson one of the guys I worked with told me that I must be wrong about how annoying it is to take a photo on Android. Then – once I had got it working – sent me a code review of his from a previous project and said (I paraphrase) “that is the right way to do it actually, because that’s how I did it too”.

    So I returned to Android this year with a degree of trepidation. I really wanted to be better at it, but based on what I’d learned so far about it, mainly I was happy in Java and I’d learned maybe how not to do some things, but as I’ve commented before, that doesn’t always teach you that much.

    Last week was the ~2nd week this year where I was able to focus on Android and things finally started to click. It was so exciting, because now I feel like I can pick up small bugs here and there, whereas before I felt I needed minimum 2 days to make progress. It’s like going from navigating with a compass to having a compass AND a (slightly fuzzy) map.

    The big thing that clicked was understanding the ways in which the platform encourages bad design.

    On iOS, that thing is mixing View Code and Control Code. The more tools I add to my arsenal to handle that, the better architected my iOS apps became. There’s another area of mixing model and persistence code. Really on iOS the design problem is mixing things that would be better separated. Learn that, make an effort to keep things apart, and everything seems more possible.

    On Android things are very separated. This is not a problem you run into. The view is defined in xml. Any background processing work needs to live in a “service”. In fact on Android separation of things goes so far the other way, that the problem is state. When you rotate your phone, the activity gets recreated. So if you have anything with state, you need to save that state. If you have anything that might be happening in the background, you need to handle getting the same service.

    This means:

    • I don’t even know how you would get a stateful Android app working without Dependency Injection (luckily I had Chiu-Ki to help me with this, because it’s tricky).
    • This encourages the use of Singletons (ai!) because it’s an easy way to make sure you get the same service when the phone is rotated.
    • Automated dependency injection is nice and good for testing, but it can allow you to have very complex object relationships. I don’t see it as some panacea for good design, more as a something that obfuscates bizarre things you have done.

    This is an app I’ve been porting over from iOS and it’s fascinating to me what’s different. Some things were easier, and some things were harder. But, Android makes a lot more sense, and I got things working enough to send out a beta, so that is exciting.