Friday, November 25, 2016

Response to Apple: The Good, the Bad, and the Ugly

This started life as a comment on Steve Yegge's The Monkey and the Apple post.

In the section Apple: The Good, the Bad, and the Ugly, Steve said:
[Objective-C] has generics, literal syntax for sets/dictionaries/arrays, try/catch/finally macros, extremely well-implemented lambdas with proper closure capturing (unlike nearly every other non-functional language out there), properties, and many other modern conveniences.

[Objective-C] Has Generics

Objective-C's generics are extremely weak. You can only define a generic type with a class method, so you can't have per-method types like in Java. Also, Objective-C classes aren't generic, so generic type-safe casting isn't possible.

Literal Syntax For Sets/Dictionaries/Arrays

Literal sets/dictionaries/arrays aren't generic, so you must use old-fashioned mutable collections if you want generic safety.

try/catch/finally macros

You're never supposed to use exceptions for control flow. Exceptions are only for programmer error, and are almost always supposed to cause a crash. Thus, the @try/@catch/@finally macros are useless.

Extremely Well-Implemented Lambdas with Proper Closure Capturing

Objective-C lambdas (blocks) are the one bright spot of the language, excepting that they don't work properly with generics.

Missing Features

Steve left out nullability from the language feature list. It works somewhat, but I haven't had enough time yet to do a proper analysis. The nullability syntax is overly complex, however. There are 3 different ways to declare nullability.

Downsides of Swift

Later, Steve says:
Obviously today I'd do it in Swift
This seems obvious, but Swift has a few major downsides that could be deal-breakers.
  • Some people report inability to print variables from LLDB for the past 2 years
  • Swift only supports modularity with dynamic libraries. If you use too many dynamic libraries, your app will start slowly. Objective-C still supports static linking, so you can use as many libraries as you want without affecting startup time.
  • The Swift runtime adds 18 MB to your app's binary size. Apple will probably strip unneeded binaries from the runtime when individual users install, but if 32-bit ARM and 64-bit ARM both have the same-sized binary, the Swift runtime adds 9MB at install-time.

Monday, January 18, 2016

Mobile Testing Brain Dump

A friend asked me for advice on testing, and I thought my response might be helpful for the rest of the world. This is simply a brain dump, with very basic structure, and there might be some minor inaccuracies. I encourage corrections, and will update the post with them as needed. :)

Each platform has it's own testing hell. First unit testing:

For Android, unit testing is a real pain. By default, Google recommends that you run your unit tests on an emulator or real device, even if they don't require any UI components. This is because there are no Android libraries that run directly on the development box. Thus, you're best bet is to segment your Java code into as much pure Java as possible and write pure-Java unit tests for that. Then, write Android-specific unit tests for the code that uses Android libraries. You can write those with JUnit, and they'll run on an emulator or device. If this isn't an option, you can look into Robolectric, which is an attempt to create a fake Android environment that runs on the JVM. I recommend against Robolectric because it doesn't have the exact same behavior as a real device, and doesn't support the latest Android (or it didn't back in 2014 anyway).

For iOS unit testing, you basically must use XCTest. There are other frameworks like Kiwi or Specta, but they're just BDD frameworks that wrap XCTest by artificially creating test objects based on their own method and assertion specification patterns. I recommend using plain XCTest because Apple hasn't had great support in the past for running unit tests. Thus, if you stray far from Apple's recommended path, you're likely to find that stuff doesn't work between releases of Xcode. On my last project, we used ordinary XCTest and things worked fine. You can easily add OCHamcrest, which Jon Reid maintains (he's a great guy and very helpful if you have questions). OCHamcrest is just a better way of performing assertions, but it doesn't actually alter the testing structure, so I think it'll still remain pretty compatible with each new version of Xcode. If you need to mock, I recommend OCMock.

For UI interaction testing:

For Android UI interaction testing, you can use essentially 2 different options: Espresso and UIAutomator. You use espresso if you want to run tests exclusively within your own app. You use UIAutomator if you want to run tests that might call out to many different apps (for example, if you have a twitter client, you might write a UIAutomator app that opens the Photo Gallery, presses the Share button, and shares a photo to your twitter client). With espresso, you have access to objects within your application because the tests run within your application's process. With UIAutomator, the tests run outside your application's process, so you must use only the UI itself to perform actions and verify results. UIAutomator uses Android's accessibility framework to navigate the DOM and extract values from UI controls.

For iOS UI interaction testing, things are unfortunately a bit complicated. Up until iOS8, Apple recommended the UIAutomation tool in Instruments. UIAutomation seems like it works for simple tasks, but it starts breaking down by not recognizing views on the screen or not properly tapping the right things. Worse, you must write UIAutomation tests with javascript. Also, UIAutomation doesn't support interacting with other apps or with system alerts, so you can't grant permission for your app to use the GPS, Photos, etc, or interact with other apps. Finally, there is no simple support for running UIAutomation tests from the command line, and there is no actual unit testing framework for it, though primitives exist that you can use to build your own.

Because of these problems with UIAutomation, Square created KIF. It runs your tests within XCTest (which itself just runs within an instance of your app). It relies on some non-public API for interacting with the UI. It worked well up to iOS8, but I've never run it against iOS9, so I don't know if it works there. It doesn't support interacting with system alerts or with other apps because your tests are confined to your process.

Starting with iOS9, Apple created XCUIAutomation, which allows you to write UIAutomation-style tests within an XCTest-like context. I've never used it, so I don't know how well it works, but I've talked to people at Apple who said that despite XCUIAutomation's similar name and APIs, a new team worked on it and completely rewrote it, so maybe it works better. I don't know if it will support interacting with iOS system alerts or other apps, but I doubt it.

In short, if you want to run on iOS7, iOS8, and iOS9, KIF might be your best option. If you want the best official support, XCUIAutomation might be your best option.

Finally, if you're doing UI interaction testing, you should try to run all your tests on as many different OS/Hardware combinations as possible. Amazon and Google have options for this, but Google's is in private beta, and only supports Android. Thus, I recommend trying Amazon's. I haven't used it yet, but since I'm an Amazon employee (by way of Twitch), I plan to give it a try as soon as I get some time to work on UI Interaction testing.

However, even though all these tools for UI interaction testing exist, I recommend not relying too heavily on it. I've tried on many occasions with many different systems to do UI interaction testing (including on Swing, Eclipse, and Selenium), and I've found that UI interaction testing always tends to be brittle, regardless of the framework or GUI toolkit. Generally, front-ends change a lot, and when they change, they tend to break all the underlying tests. You can mitigate that a bit by writing an abstraction over your UI to perform basic tasks (sort of like Cucumber  encourages you to do with Rails apps), but also adds to the cost of testing. You can theoretically get a lot of benefit from UI interaction testing, but I've never seen it work well on a really fine-grained level. 

Friday, April 17, 2015

The 0.1x Programmer is not a Myth

TLDR

People that claim 10x programmers are a myth haven't spend enough time with terrible programmers.

Intro

Thanks to @kmoir, today I watched @jacobian's very interesting PyCon 2015 Keynote:
Jacob asserts that because most people fall onto a normal distribution that most programmers must as well. He makes a great analogy between marathon running ability and programming ability.

He illustrates his point with this chart:

Groundwork

I wholeheartedly agree that the mythical perception negatively affects a beginners confidence and that it is hurting our industry pipeline. I completely disagree that programmers lie on a normal distribution. Like Jacob, I have no data to support my claims, however, I'll be honest and say so, rather than presenting my claims as fact.

First, I want to introduce the three pillars I think someone needs to be a successful programmer (these probably make you successful at most things): hard work, a desire to improve, and intelligence. I posit that these qualities are mutable, but generally it is better to find someone that already has high values of at least two of these qualities. For example, if you're trying to hire a programmer for an esoteric language without much of a community, you might look for someone with intelligence and a desire to improve. If you need someone to pore over thousands of lines of COBOL looking for two-digit dates, you might optimize your hiring for hard-workers; they might not need to know the deep inner workings of the systems they're modifying. They might just need to expand all the records that contain a date.

There exist a lot of hard-working, intelligent programmers. There are unfortunately not as many with a desire to improve. A friend once told me you can have X years of experience, or 1 year of experience X times. (I'm sure that quote is attributable to someone famous.) I think this is extremely common in programming.

I believe that programmers have programming ability on a distribution more like this (enjoy my professional illustration):
However, I also believe that programmers have job-acquisition ability on a normal distribution:
I don't think there is any correlation between these graphs. I've seen plenty of programmers that have great job-acquisition ability, but poor programming ability. I've also seen plenty of the inverse. 

Burying the Lede

If you assume that my anecdotal graphs above are true, then there are probably a lot of bad programmers employed today. In Jacob's talk, he mentions that the US government projects a 1.5 million gap between available jobs and available programmers by 2020. Thus, companies have a high incentive to hope that a bad programmer will become better. In my experience, companies that employ most bad programmers don't know they have bad programmers, and thus are unlikely to fire them, so bad programmers have little incentive to improve. In my 15 years of professional experience, the only times I've ever seen a bad programmer be fired was for insubordination or other issues unrelated to ability.

TLDR Again

10x programmers are probably extremely rare. It is 0.1x programmers that are common. I've interviewed lots of them. I've worked directly with lots of them in my 6 years as a consultant and 4 years suffering in large midwestern corporations. I've also worked with some really good programmers as well. I've never worked with someone that I'd classify as a 10x programmer, but I've met a few 2x programmers.

Outsider

In Jacob's talk, he mentions that he didn't invent django, but rather just happened to be an early user. He further claims to be a mediocre programmer without presenting any evidence. I think Jacob is simply undervaluing himself.  I'm not part of the python community at all. I don't follow @jacobian on twitter. I could be completely wrong about the people that he's worked with, but based on a quick googling, he's only worked at The Lawrence Journal World (with the inventors of django) and Heroku. I assume that means he never spent 2008 hacking on a massive EJB-based single-use custom web framework from 2002 for an insurance company. I'm not holding up that as a badge of honor or martyrdom, but of a vastly different experience.

For the Beginners

I agree with Jacob that 10x expectations are unrealistic and are intimidating beginners away from the field. However, a 10x difference in ability is real. We need to communicate to beginners not that they must be 10x badasses, but that most programmers are 0.1x clock-punchers who don't have much desire to improve. Beginners have an amazing opportunity to quickly surpass people with lots more experience. This doesn't require working 60 hours/week every week, though one might be required occasionally. It requires a desire to investigate root causes of problems instead inserting Thread.sleep(1) in your code when you have a race condition. It requires a automating a database upgrade process instead of manually babysitting it every weekend so you can get easy overtime pay. It requires optimizing your build instead of accepting that it takes 2 minutes to redeploy your webapp after a simple JSP change. (These are all real-life 0.1x programmer examples.)

Epilogue

In case you think I spent past professional life in an awful gulag, I didn't at all. There were definitely some low points, but I had lots of opportunities to learn a vast array of different technologies, and I got the opportunity to work full time on iOS starting in 2009, which I'm still doing today.

Thursday, March 5, 2015

Mozilla Should Build an Android Permissions Delegator

Earlier today, I had a thought:
Of course, this wasn't novel:

However, this doesn't have to be an idea only used for evil. What if there existed a trusted app that exposed all permissions available to other apps at runtime? Mozilla seems like a good candidate to make this. They're a trusted organization dedicated to privacy and openness and they have the technical expertise to pull it off. Let's pretend one exists called Mozilla Services.

For end users, this could greatly improve the Android experience. Let's use Facebook (which I don't have installed because it requests WAY too many permissions) as an example:


Instead of this crazy long permissions list, Facebook could request only the ability to connect to the internet. (This permission is so common that Google hides it from the default list.) Then, when Facebook wants to do something evil like listen to my microphone when I post an update, it could do so at runtime by sending an Intent to the Mozilla Services App. Mozilla Services could keep an internal list of permissions granted to various apps (which users could revoke at any time). If Facebook hasn't been approved to use the microphone, Mozilla Services could pop up a dialog asking me to allow Facebook to use the microphone. Mozilla Services could then use the microphone and delegate access to the bitstream to Facebook. If I later decide I don't trust Facebook with microphone access, I could open Mozilla Services and remove Facebook's permission.

Of course, Facebook will never adopt this scenario because it already has over 1 billion installs even with all the permissions it requires. However, some smaller app like Wonder Workshop's Path for Dash, which doesn't yet have 500 installs, might want to reduce friction for users, so it could use Mozilla Services to set up its Bluetooth connection.

Maybe you're way more influential than me and can convince Mozilla to build this. They're hiring Android/iOS engineers...

Monday, February 2, 2015

Towards an Ideal Cocoapod Project Structure

The Situation

I recently returned to iOS from a year-old Android hiatus. On my last iOS project, I used cocoapods as my dependency manager, but this was bolted onto an existing codebase. I'm starting fresh now, and I want to find a canonical project structure for a cocoapod (think AFNetworking, CocoaLumberjack, etc).

 The Problem

I don't want to have duplicate configuration between an Xcode project and a podspec. Ideally, cocoapods would configure a project with which I can do daily development.

The Solution

Development Pods! Specifically, create a demo project for your cocoapod, and use the demo project's Podfile to import your cocoapod into Xcode:

Using the files from a local path.
If you wold like to use develop a Pod in tandem with its client project you can use the path option.
pod 'MyPod', :path => 'MyPod/MyPod'
Using this option CocoaPods will assume the given folder to be the root of the Pod and will link the files directly from there in the Pods project. This means that your edits will persist to CocoaPods installations.
http://guides.cocoapods.org/syntax/podfile.html#pod

I created a Single-View iOS App, which yielded a project structure like this:
MyPod.xcodeworkspace/
MyPod/MyPod/MyPod.h
MyPod/MyPod/MyPod.m
MyPod/MyPod/MyPod-Info.plist
MyPod/MyPod.xcodeproj/

The podspec is a supporting file, so it belongs in MyPod/MyPod with MyPod-Info.plist and friends. Now, if I want to modify my cocoapod's configuration by adding an AFNetworking dependency, I only need to change my podspec and run pod install.

Saturday, September 28, 2013

Cocoapods - ARGV - Returns Options as a Hash, OCMock only for Tests target.

Tonight I actually made my cocoapods objc port use cocoapods. Most of the point of this adventure is for me to deeply learn cocoapods. I initially wanted to replace some manually written stubs in my first ARGV test with OCMock, but it OCMock doesn't support stubbing -[NSObject description], so I didn't actually need it. I imagine I'll need OCMock at some point, and it was also a good exercise in learning how to setup cocoapods dependencies only for specific targets, so I left it in.
I also tried implementing the "returns options as a hash" spec, but ran out of time. Ben Chatelain suggested I use FSArgumentParser:
but I don't think that's going to work. However, FSArgumentParser depends upon CoreParse, and that looks like it'll work out great. I'll probably use FSArgumentParser as an example of how to use CoreParse for my purposes, so I think Ben's suggestion will pay many dividends. I love knowing smart people.

Friday, September 27, 2013

Cocoapods adventures

We've started using Cocoapods at work, and it seems like a really great system. However, it is written in ruby, and I hypothesize that it would get more participation from the community if it was written in Objective-C. Also, since Cocoapods is a specification as well as a system, it would benefit from a separate (though in this case not clean-room) implementation. Thus, I'm going to rewrite Cocoapods in Objective-C and blog about my journey along the way.
Thankfully, Cocoapods looks pretty modular, and it looks like it has good tests. I'm starting with CLAide, which is Cocoapods' command-line aide. This has RSpec tests, which I know nothing about, but I can generally figure out what they're doing just by reading the text.
Tonight, I just wanted to start something. I wasn't too concerned about crazy progress. I simply cloned CLAide and figured out how to run the existing RSpec tests. I found the .travis.yml file with a script command that looked promising, so I tried running that, and installed required gems (bundler, rspec) until it worked.
Next, I just converted the first two tests in the first spec (argv_spec.rb). I'm doing at least one commit for each test I convert. Also, I'm keeping all this work on a separate branch. Eventually, I'll just have a parallel Foundation command-line tool project that I'll keep up to date.
Tomorrow, I'll tackle the first real piece of parsing necessary. Hopefully, I'll have ARGV converted by Sunday night.