Thursday 1 October 2015

Wednesday 7 May 2014

TDD is not Dead. But, it shouldn't be evangelized either.

There’s been a lot of hoo-haa recently on the value and the future of TDD. DHH put out his controversial, blunt and somewhat rant-based blog post about a week ago.

This topic interests me a lot for a number of reasons.
  1. I come from a strong unit testing background. I’ve practiced it in several different flavors and discussed it philosophically with a lot of different types of programmers.
  2. I recently left a world where TDD was the defacto approach and have found myself in a team where it isn’t.
This blog post is going to review DHH's article, highlight weaknesses in it and provide counter arguments (with supportive evidence). Let's start with an overview of DHH’s post.

'TDD is Dead' Overview 

THESIS:
(shortened and paraphrased - please let me know if you think I got this wrong!): 

TDD is no longer needed as a practice in software development. Automated regression testing is all we need going forward.

ARGUMENT:
His argument is based on the following pros/cons of TDD.

Pros:
  • code coverage
  • confidence to make changes
  • driving code design
  • move towards automated/regression testing
  • training wheels for thinking about why/how to test code/software
Cons: 
  • guilt when not doing it
  • overly complex objects/indirection to avoid things that are ‘slow’

I really did scour this article for more cons… but I couldn’t find them. Let's go back to his thesis. Regression testing is enough to achieve all the pros you get from unit testing and should also alleviate the cons.


Weaknesses in DHH's Post


I think the primary issue with DHH's argument and evidence is that the claim that the pros you get by practicing TDD can be achieved with regression tests. Because, based on my experience... they can't. 
  1. Code coverage. 
  2. CAN - Arguably a different kind of code coverage however.. that being you don't break functionality.. designs can still be broken and not covered by unit tests
  3. Confidence to make changes
  4. CAN - Again, confidence your changes don't break functionality.. changes can still break design paradigms
  5. Driving well designed code
  6. CAN NOT - This is the kicker for me and where most of my argument lies. I'll elaborate on this next. Regression tests can't drive code design.
  7. Moving towards regression testing
  8. N/A - if you’re replacing your unit tests with regression tests, you’ve already moved towards and seen the value in regression testing
  9. Training wheels for thinking about why/how to test software
  10. SORT OF - This is really a deeper exercise than can be achieved with regression tests.. they go a short way in this but not the whole way


What's the Problem?


My experience in software development has allowed me to work on a vast array of different types of teams and software projects. Most of my work has been on teams and projects that were TDDed with a high level of code coverage and normally a suite of regression/smoke tests to back it up.

About 18 months ago, I moved out of this world and happened to find myself in the world of little to no unit tests.

I have found that this causes problems in your codebase that I did not see before on such scales: 
  • Little to no consistency in code design
  • Disregard (or lack of knowledge) for the single responsibility principle.
  • No shared understanding of how to approach common problems
  • A massive fear to change anything that has gotten into a particularly confusing state

I've identified a number of characteristics of code that has not been TDDed and are indicators of the above problems.
  1. Huge 1500+ line classes that do so much it’s hard to know what it is they ARE actually doing. (This is really the elephant in the room when it comes to crappy codebases.)
  2. An unreasonable large number of singletons (language permitting)
  3. No dependency injection
  4. Circular dependencies
  5. Huge, nested ifs, switches, whiles, fors.. etc...

These problems and the impact the leave on your code can't be addressed with regression tests. I do admit that TDD is not the only answer in addressing these. But, I will argue that in many cases it is a darn good one.. 


Why TDD is Not Dead


A well designed codebase respecting single responsibility and implemented in a consistent way is a pleasure to code in. You get things done quickly and efficiently. You're easily able to onboard new people and get them moving around it quickly. So, how can you avoid the above problems and achieve this?


Imagine a Project....


Lets assume that you are an excellent programmer who knows how to avoid the problems outlined above without TDD and unit tests. (Because you are, right?!!) Now, let's imagine the wide variety of different codebases and teams you might find yourself in throughout your awesome career.


One Person Team/Small-Medium Codebase

If you happen to be the only author of a small to medium-sized codebase.. you can probably write something beautiful and relatively stable without TDD. This is a strong candidate for skipping these techniques. Though I've found myself in the situation and I still practice TDD occasionally when I'm having trouble designing a particularly complex area of the codebase.


Small Team/Small-Medium Codebase

Maybe you're just a few people. You're all rock stars and you communicate effectively like bumble bees in hive serving your queen. (That queen representing an awesome codebase.) Maybe here you can get away without TDD as well. Perhaps.. but what happens when the team changes? New people are brought in? Communication drops? Guards are dropped, confusion sets in, there's no safety net.. Ahhhhh! 

I think these are the codebases that start out well, and have the best intentions... but if left running long enough, generally result in the negative codebase characteristics mentioned above.


Large Team/Large Codebase

The larger a team and codebase get, the harder it is to keep things clean. It's just the way of the programming world. Consistency is increasingly hard to maintain. You've got team members shifting on and off, writing code in areas they perhaps don’t understand. This is how you develop monster 1500+ line classes.


TDD is a tool to tackle these issues. How?:


Tests Drive Code Design

This is the single strongest reason outside of code coverage to practice TDD. This is also one of the pros that DHH mentioned about TDD.. and one that he didn't explain how it would be covered with a regression test suite alone. Write your tests before you code and you:

1. Expose your dependencies. 
2. Expose your expectations of what a class is responsible for.

What this results in that is like the whole point, is pain when writing your test if you have too many of either dependencies or responsibilities. This is an indicator that your class is getting too unwieldy and most often the easiest thing to do is to redesign to minimize dependencies or responsibilities. Wala! Single responsibility. Manageable, small classes. Boom. Bam.

Tests Document Your Code

Let’s face it. Documentation gets out of date. It’s also an excuse to leave behind confusing code. Tests act as a form of documentation for what your classes and their methods are responsible for. (Achievable by regression testing.. NO. Though regression tests do act as another form of documentation.) As with


Conclusion


As with everything, there's a balance to be had. And as with most contentious subjects there are those that operate on the extremes. I think the guilt DHH mentioned about how you feel when you don't TDD your code comes from those operating on the extreme side of 'TDD all your code, everytime'. It's impossible to do this and it's not expected. I think responding to this guilt with switching to the extreme other end of 'TDD is dead.. you don't need it ever' is not the appropriate response either. What is appropriate is thoughtful evaluation of each situation to decide if TDD's benefits would help bring about a better codebase and better software project.

Monday 7 October 2013

Xcode 5 support for iOS6 SDK

Issue:

1. You're device is running iOS 7 and so, any apps you deploy to it have to be deployed from Xcode 5.
2. You have a project that you'd like to run that has a target SDK of iOS 6. This project uses things like the camera or microphone that have to be tested on a real device?

Xcode 5 only ships with support for iOS 7 targets. Blah. But, you can add support for earlier targets if you can get your hands on those earlier SDKs.

I needed iOS 6 to run the PhotoPicker sample project on my device. I found that in my Xcode 4 installation at

/Applications/Xcode4.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS6.1.sdk

Copy that into the same directory for you Xcode 5 installation and you're off.

Thursday 19 September 2013

Presentation at Brooklyn iOS Meetup

I had the opportunity to put a presentation together for the Brooklyn iOS meetup recently and decided to do one based on 'Lessons Learned' from a self-taught stand point. I am an iOS developer largely self-taught and many things I learned along the way, I wish I had known earlier!

I think the meet-up has a range of people from lots of different experience levels and I wanted to engage all of them to also talk about what they've learned that they wish they knew earlier.

It went in a really cool direction with a lot of discussion around the tools people use. A lot of a developers power is in the tools they use and it seems like the iOS space is exploding with new and awesome tools to help you write apps more efficiently.

Here's the video of the presentation.



Also, a lot of tools were mentioned by audience members in the discussion following the presentation.. I'll mention here some of the tools I discussed and those mentioned by others:

Mentioned in presentation:

PaintCode, CocoaPods, AFNetworking, HockeyApp, TestFlight, RestKit(verdict: avoid), and MagicalRecord.

Mentioned in discussion:

iExplorer, Reveal, ImageOptim, Crashlytics, Pop, and Envision.

Saturday 14 September 2013

GoFest Released!

My first app released to the App Store, GoFest was released today.



You can find it there here.

It feels pretty good! GoFest is an app for beer festivals and will be hosting it's first festival in 2 weeks at New Orleans on Tap.



Thursday 5 September 2013

Metadata Rejected!

Rejection feels horrible doesn't it? It feels even worse when you could have avoided it so easily!

I submitted my first iOS app, GoFest, to iTunes Connect last week and just got notice back yesterday that it's been rejected because of it's metadata. What does this mean? Well, in my particular case, it's because I submitted my 4" retina screenshots were taken on a device running iOS 7. Doh! The app itself targets iOS 6, but because the phone I took the screenshots on was running iOS 7, they're not accepted.

I wish I had lots of devices around running all the various iOS versions available.. but alas, I do not. So, retake the screenshots. Resubmit. 

Tuesday 20 August 2013

Deploying an Echoprint server to EC2

For my current client work, I recently needed to deploy an instance of the open-sourced Echoprint server to the cloud. Echoprint is written in Python and uses Tokyo Cabinet and Tokyo Tyrant as it's data store. I found the only cloud hosting option available (given these technologies) was a custom Amazon EC2 environment. Now, I'm not an EC2 expert. And I must say their documentation and tutorials are both confusing.. and frequently out of date. It was a bit of a headache to get everything setup and configured.. so I thought I'd document it!

Here goes, if you'd like to setup the Echoprint open-sourced project on an Amazon EC2 instance... here's what you have to do. (As of Aug, 2013)


Setup an AWS account and launch an EC2 instance


Create (or login to) an Amazon AWS account and go to the management console. Navigate to your EC2 dashboard and click 'Launch Instance'. From here, choose the 'Quick Launch' wizard. This will be fine just to get us going. A few things to note:

1. Create a new public/private key pair for this server. We'll be using this later to setup ssh access. If you can download it now we'll get back to this later.

2. Server type. I chose the default Linux AMI, this is fine.

From here, finish up and click 'Continue' and then 'Launch'. This should launch an virtual server for you.. which is what we want!

From here, we need to be able to access this machine from our local computer. We'll need to setup SSH access for this. Download CLI tools. Confusingly these are called either the CLI tools or the API tools from different places in the AWS documentation. They are the same thing. Jeez!

I created a local directory where I would store all my EC2 stuff. I recommend doing the same. In a terminal execute:


> mkdir ~/.ec2

Unzip and move the bin and lib files from the CLI tools you just downloaded into this directory.
Move the key pair we created and downloaded during the wizard into here too.

You'll also need to update the permissions on that key-pair file.

> chmod 400 ~/.ec2/saskey-ec2.pem

Make sure the following environment variables are set. You'll have to get your AWS access and secret keys from the AWS console to set these variables. They are necessary. On a mac:

> vim ~/.bash_profile

Add:

export JAVA_HOME=$(/usr/libexec/java_home)
export EC2_HOME=~/.ec2
export PATH=$PATH:$EC2_HOME/bin
export AWS_ACCESS_KEY=AKIA***********FRS55A
export AWS_SECRET_KEY=u/ixxR*****************XsTfZl64I/H
export EC2_PRIVATE_KEY=$EC2_HOME/saskey-ec2.pem

Save and close (esc -> :wq!)

> source .bash_profile

Confirm everything is working by running the following command. Get the instance id from your ec2 instance console. It should look something like this: ec2-xx-xx-xx-xxx.compute-1.amazonaws.com.

Now we can ssh into our new linux server!

> ssh -i ~/.ec2/saskey-ec2.pem ec2-user@ec2-xx-xx-xx-xxx.compute-1.amazonaws.com

You should see something like this:

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

[ec2-user@ip-xx-xxx-xx-xx ~]$


Prepare This Server for Echoprint


We need to get the appropriate things onto the server:
Requirements for the server:
Java should be on there. run > java -version

Python should be on there. run > python --version

I'm running Python 2.6.8, so no need to worry about the simplejson

We need to install Tokyo Cabinet - run the following:

> wget http://fallabs.com/tokyocabinet/tokyocabinet-1.4.48.tar.gz
> tar -xvf tokyocabinet-1.4.48.tar.gz
> cd tokyocabinet-1.4.48
> sudo yum install gcc
sudo yum install zlib
> sudo yum install zlib-devel
> sudo yum install bzip2-devel.x86_64
> ./configure --enable-off64
> sudo make
> sudo make install

We need to install Tokyo Tyrant - run the following:

> wget http://fallabs.com/tokyotyrant/tokyotyrant-1.1.41.tar.gz
> tar -xvf tokyotyrant-1.1.41.tar.gz 
> cd tokyotyrant-1.1.41
> ./configure
> make
> sudo make install

Install the Echoprint project


Now we need the Echoprint project itself. I'm going to pull it from the Github project.

> sudo yum install git.x86_64

Go make a fork of the echoprint github project and git the github address. You'll need it below.


> cd /usr/local

> git clone https://github.com/your-git-id/echoprint-server

Now you've got the Echoprint project. Let's fire it up. Everything is installed and ready to startup.

Starting Solr (The Echoprint project uses Solr to index it's audiofingerprints)

> cd solr/solr
> java -Dsolr.solr.home=/usr/local/echoprint-server/solr/solr/solr/ -Djava.awt.headless=true -jar -DSTOP.KEY=YOURKEY -DSTOP.PORT=8079 start.jar

As a note, the logs will now output to: /usr/local/echoprint-server/solr/solr/logs


Stopping Solr (You may need this later)

> java -Dsolr.solr.home=/usr/local/echoprint-server/solr/solr/solr/ -Djava.awt.headless=true -jar -DSTOP.KEY=YOURKEY -DSTOP.PORT=8079 /usr/local/echoprint-server/solr/solr/start.jar --stop


Start Tokyo Tyrant

> sudo mkdir /var/ttserver

> sudo chown ec2-user /var/ttserver/
> cd /usr/local/sbin
> ls
nohup ttservctl start &
Starting the server of Tokyo Tyrant
Executing: ttserver -port 1978 -dmn -pid /var/ttserver/pid

Done

Start Echoprint API Server


sudo easy_install web.py
> cd /usr/local/echoprint-server/API
> nohup python api.py 8080 &

nohup because of ssh session




Make it Accessible to Outside world


If you'd like this accessible to the outside world.. to later do your importing/querying, you'll need to open up the port the Echoprint API server is running on. W
e have to tell our EC2 instance to do this. We do that by adding a rule to the security group's inbound rules, as shown below.



Now you're ready to import and query. Reference the Echoprint project documentation for instructions on how to do this!